Texto (index, tag y mark)

El widget tkinter.Text permite mostrar textos de diferentes modos. Su constructor es

Text(master=None, cnf={}, **kw)

Las opciones que tiene son:

 |      STANDARD OPTIONS
 |      
 |          background, borderwidth, cursor,
 |          exportselection, font, foreground,
 |          highlightbackground, highlightcolor,
 |          highlightthickness, insertbackground,
 |          insertborderwidth, insertofftime,
 |          insertontime, insertwidth, padx, pady,
 |          relief, selectbackground,
 |          selectborderwidth, selectforeground,
 |          setgrid, takefocus,
 |          xscrollcommand, yscrollcommand,
 |      
 |      WIDGET-SPECIFIC OPTIONS
 |      
 |          autoseparators, height, maxundo,
 |          spacing1, spacing2, spacing3,
 |          state, tabs, undo, width, wrap,

Es uno de los widgets más complejos por la gran cantidad de métodos propios que define. Si escribes import tkinter as tk; for n, i in enumerate(dir(tk.Text)): print(n, i) en la consola iterativa de Python descubrirás que contiene 265 atributos y métodos. Tómate unos minutos para leerlos con el único propósito de familiarizarte con ellos e imaginar para qué se podrían utilizar en el desarrollo de una aplicación.

Tiene métodos pre-definidos, implementados de forma interna, como copiar, cortar o pegar. Si creamos una aplicación con este widget no tenemos que hacer nada más para conseguir usar estas acciones a través de los atajos de teclado o del menú Edit que muestran por defecto las aplicaciones generadas con tkinter. Prueba las tres acciones en la siguiente aplicación:

import tkinter as tk

v_principal = tk.Tk()

tk.Text(v_principal).pack()

v_principal.mainloop()

Adelantando algo de contenido, comentaré que la instrucción pack admite dos parámetros muy útiles cuando se usan widgets con gran contenido: fill y expand.

Si añadimos fill=tk.X a la orden de empaquetado y redimensionamos la ventana en tiempo de ejecución comprobaremos que el widget se adapta horizontalmente a las dimensiones de la ventana. Con fill=tk.Y no conseguimos el efecto esperado verticalmente al hacer más alta la ventana. Añadiendo expand=True sí que lo conseguimos, ya que estamos indicando que cuando se redimensione la ventana se ha de expandir el widget con ella.

Métodos

tkinter.Text define un buen número de métodos:

bbox(index)
compare(index1, op, index2)
count(index1, index2, *args)
debug(boolean=None)
delete(index1, index2=None)
dlineinfo(index)
dump(index1, index2=None, command=None, **kw)
edit(*args)
edit_modified(arg=None)
edit_redo()
edit_reset()
edit_separator()
edit_undo()
get(index1, index2=None)
image_cget(index, option)
image_configure(index, cnf=None, **kw)
image_create(index, cnf={}, **kw)
image_names()
index(index)
insert(index, chars, *args)
mark_gravity(markName, direction=None)
mark_names()
mark_next(index)
mark_previous(index)
mark_set(markName, index)
mark_unset(*markNames)
peer_create(newPathName, cnf={}, **kw)
peer_names()
replace(index1, index2, chars, *args)
scan_dragto(x, y)
scan_mark(x, y)
search(pattern, index, stopindex=None, forwards=None, backwards=None, exact=None, regexp=None, nocase=None, count=None, elide=None)
see(index)
tag_add(tagName, index1, *args)
tag_bind(tagName, sequence, func, add=None)
tag_cget(tagName, option)
tag_configure(tagName, cnf=None, **kw)
tag_delete(*tagNames)
tag_lower(tagName, belowThis=None)
tag_names(index=None)
tag_nextrange(tagName, index1, index2=None)
tag_prevrange(tagName, index1, index2=None)
tag_raise(tagName, aboveThis=None)
tag_ranges(tagName)
tag_remove(tagName, index1, index2=None)
tag_unbind(tagName, sequence, funcid=None)
window_cget(index, option)
window_configure(index, cnf=None, **kw)
window_create(index, cnf={}, **kw)
window_names()
yview_pickplace(*what)

Y hereda otros tantos de las clases BaseWidget, Misc, Pack, Place, Grid, XView e YView. No las necesitamos todas pero es bueno saber que existen cuando desarrollemos aplicaciones que usen este widget.

Indexado, etiquetado y marcado

Uno de los aspectos interesantes de este widget es la posibilidad de etiquetar y marcar su contenido, con lo que es capaz de mostrar texto enriquecido. Para crear etiquetas o marcas hay que conocer antes cómo se indexa su contenido.

Indexado

El contenido de este widget está guardado en una matriz, cuyas filas y columnas son el índice de cada uno de sus elementos. Tiene la particularidad de que sus filas están enumeradas comenzando con el 1, mientras que sus columnas lo están partiendo del 0, por lo que el primer elemento está en la posición 1.0 si nos referimos a ella con la notación fila.columna.

Podemos averiguar la posición actual del cursor con el método index(tk.INSERT) del widget Text. O la más cercana al puntero del ratón usando el método index(tk.CURRENT) del widget. tk.END indica la posición posterior al último elemento del widget. Con la expresión index('end') obtendremos el mismo resultado que con index(tk.END), igual que si sustituimos 'insert' o 'current' por sus correspondientes atributos de tkinter.

Comprueba el resultado de esta función en tiempo de ejecución con la siguiente aplicación:

import tkinter as tk
import tkinter.ttk as ttk

def actualizar_posicion():
    cursor.configure(text='cursor: {}'.format(texto.index("insert")))
    puntero.configure(text='puntero: {}'.format(texto.index('current')))
    final.configure(text='final: {}'.format(texto.index("end")))

v_principal = tk.Tk()

texto = tk.Text(v_principal)
texto.pack(fill=tk.Y)

cursor = tk.Label(v_principal, text='cursor: {}'.format(texto.index("insert")))
cursor.pack()
puntero = tk.Label(v_principal, text='puntero: {}'.format(texto.index("current")))
puntero.pack()
final = tk.Label(v_principal, text='final: {}'.format(texto.index("end")))
final.pack()

ttk.Button(v_principal, text='Actualizar posición', command=actualizar_posicion).pack()

v_principal.mainloop()

Si añades texto.insert('end', '\nOtra línea') a la función definida comprobarás cómo funciona este método del widget Text. Piensa qué ocurrirá si cambiaa 'end' por 'insert' o 'cursor' y pruébalo una vez que creas que ya lo sabes.

Receta

Para obtener por separado la fila y columna de la posición se puede usar la función split, en el ejemplo anterior:

fila, columna = texto.index("insert").split('.')

Los índices se pueden manipular como se indica en la guía de referencia de Tkinter 8.5.

Etiquetado

Si seleccionamos contenido de este widget observaremos visualmente la selección hecha mediante un cambio de color frontal y de fondo. tkinter añade de forma automática la etiqueta 'sel' (o SEL) al contenido seleccionado, con lo que se le aplica el cambio de formato que tiene establecido el sistema en su configuración.

Se puede añadir una etiqueta usando la función tag_add(tagName, index1, *args), en la que proporcionamos un identificador para la etiqueta (una cadena de caracteres sin espacios) y el rango al que afecta. Un mismo rango puede tener más de una etiqueta, y una etiqueta puede usarse en múltiples rangos del contenido del widget.

En este widget, las etiquetas permiten controlar el formato de su contenido usando opciones como

background(color)
bgstipple(bitmap)
borderwidth(distance)
fgstipple(bitmap)
font(font)
foreground(color)
justify(constant)
lmargin1(distance)
lmargin2(distance)
offset(distance)
overstrike(flag)
relief(constant)
rmargin(distance)
spacing1(distance)
spacing2(distance)
spacing3(distance)
tabs(string)
underline(flag)
wrap(constant)

Estas opciones se pueden indicar en la declaración de la etiqueta o modificar con el método tag_configure(tagName, cnf=None, **kw).

Por defecto, si el texto introducido en el widget es más ancho que el widget, se muestra en la siguiente línea sin crear internamente una nueva fila. Este detalle lo controla la opción wrap que tiene por defecto el valor 'char' y admite los valores 'word' y 'none' (que evita que se muestre toda la línea cuando no cabe dentro del widget).

Receta

def ajuste_de_linea():
    if texto['wrap'] == 'none':
        texto.configure(wrap='word')
    else:
        texto.configure(wrap='none')

...

ttk.Button(v_principal, text='Ajuste de línea', command=ajuste_de_linea).pack()

También se puede enlazar un evento a ciertas etiquetas, como el cambio de cursor cuando pasamos el puntero del ratón sobre un texto marcado como hiperenlace.

Receta

texto.tag_config("hiperenlace", foreground="blue", underline=1)
texto.tag_bind("<Enter>", show_hand_cursor)
texto.tag_bind("<Leave>", show_arrow_cursor)
texto.tag_bind("<Button-1>", click)
texto.config(cursor="arrow")

Marcas

Una marca en el contenido de este widget indica una posición concreta. Ya conocemos las marcas INSERT y CURRENT, que gestiona directamente tkinter, y podemos definir nuestras propias marcas con el método mark_set(markName, index).

Las marcas no se borran aunque eliminemos el contenido en el que se han creado, para borrarlas debemos usar el método mark_unset(*markNames).

Referencias

results matching ""

    No results matching ""