Lista (Listbox) en Tcl/Tk (tkinter)

Lista (Listbox) en Tcl/Tk (tkinter)

Actualizado el 25/10/2022.

La lista (Listbox) es un control de la interfaz de usuario de Tcl/Tk que permite presentar una secuencia de cadenas de caracteres.

Listbox en tkinter

Este widget no permite añadir íconos ni columnas. Para ello, utilícese la vista de árbol (Treeview).

Para añadir una lista a una ventana de Tk, creamos una instancia de la clase tk.Listbox.

import tkinter as tk

ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
listbox = tk.Listbox()
listbox.pack()
ventana.mainloop()

Manejando los elementos

Podemos añadir un ítem a la lista vía el método insert():

import tkinter as tk

ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
listbox = tk.Listbox()
listbox.insert(0, "Python")
listbox.pack()
ventana.mainloop()

El primer argumento indica la posición en la que queremos ubicar el elemento, que puede ser cualquier valor numérico o tk.END para indicar el final de la lista.

El método insert() puede tomar tantos valores como elementos quieran agregarse.

# Agregar cuatro elementos al final de la lista.
listbox.insert(tk.END, "Python", "C", "C++", "Java")

Si los elementos se encuentran en una lista o tupla, conviene hacer:

items = (
    "Python",
    "C",
    "C++",
    "Java"
)
# Agregar elementos almacenados en una lista o tupla.
listbox.insert(0, *items)

Para una explicación sobre la sintaxis *items, véase nuestro artículo Argumentos en funciones (*args y **kwargs).

Es posible conocer la cantidad de ítems en una lista vía la función size(), que devuelve un entero.

items = (
    "Python",
    "C",
    "C++",
    "Java"
)
listbox.insert(0, *items)
print(listbox.size())   # Imprime 4

Para obtener un ítem de la lista conociendo su posición, utilizamos get().

items = (
    "Python",
    "C",
    "C++",
    "Java"
)
listbox.insert(0, *items)
print(listbox.get(2))   # Imprime C++

Y para eliminarlo, delete().

# Remueve el elemento C++.
listbox.delete(2)

Las dos funciones anteriores también operan con rangos. Por ejemplo, el siguiente código retorna una tupla con todos los elementos de la lista.

# Obtener ítems desde la posición 0 hasta el final.
listbox_items = listbox.get(0, tk.END)
print(listbox_items)    # Imprime ('Python', 'C', 'C++', 'Java')

Y este otro borra todos los ítems:

listbox.delete(0, tk.END)

El parámetro listvariable

Otra forma de manejar los elementos en un Listbox es vía el sistema de variables de Tcl/Tk. Al inicializar la clase se asocia una variable, y luego todos los cambios realizados en ésta se reflejan automáticamente en la lista.

import tkinter as tk

ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
listbox = tk.Listbox()
items = tk.StringVar()
items.set(
    "Python "
    "C "
    "C++ "
    "Java "
)
listbox = tk.Listbox(listvariable=items)
listbox.pack()
ventana.mainloop()

Esto probablemente resulte cómodo cuando es utilizado en Tcl, pero no está pensado para las estructuras de datos de Python. Como se puede ver, todos los ítems se añaden como una única cadena separados por un espacio. Para obtener los elementos de la lista, la función get() también retorna una cadena.

listbox_items = items.get()
print(listbox_items, type(listbox_items))

Esto imprime:

('Python', 'C', 'C++', 'Java') <class 'str'>

Si quisiéramos acceder a un elemento en particular, deberíamos convertirlo a una tupla real vía la función incorporada eval():

# Convertir a una tupla.
t = eval(items.get())
# Acceder al primer elemento.
print(t[0])

Sin embargo, este es un método sumamente inseguro y debe usarse con mucho cuidado. La función eval() permite evaluar un código de Python a partir de una cadena cualquiera. Si la cadena proviene del usuario, un atacante podría inyectar código de Python malicioso en nuestra aplicación que resultara ejecutado por eval().

Obtener y establecer la selección

Por defecto una lista solo puede tener un ítem seleccionado (tk.BROWSE). Para cambiar este comportamiento y permitir múltiples selecciones, debe pasarse el valor tk.EXTENDED al parámetro selectmode al momento de crear el control.

import tkinter as tk

ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
# Permitir múltiples ítems seleccionados al mismo tiempo.
listbox = tk.Listbox(selectmode=tk.EXTENDED)
items = (
    "Python",
    "C",
    "C++",
    "Java"
)
listbox.insert(0, *items)
listbox.pack()
ventana.mainloop()

El método curselection() retorna una tupla con la posición (o índices) de los elementos seleccionados. Típicamente querremos usar este método en respuesta a algún evento, como la presión de un botón, por ejemplo:

import tkinter as tk
from tkinter import messagebox, ttk


def obtener_seleccion():
    # Esto es una tupla con los índices (= las posiciones)
    # de los ítems seleccionados por el usuario.
    indices = listbox.curselection()
    messagebox.showinfo(
        title="Ítems seleccionados",
        # Obtener el texto de cada ítem seleccionado
        # y mostrarlos separados por comas.
        message=", ".join(listbox.get(i) for i in indices)
    )


ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
listbox = tk.Listbox(selectmode=tk.EXTENDED)
items = (
    "Python",
    "C",
    "C++",
    "Java"
)
listbox.insert(0, *items)
listbox.pack()
boton_obtener_seleccion = ttk.Button(
    text="Obtener selección",
    command=obtener_seleccion
)
boton_obtener_seleccion.pack()
ventana.mainloop()

Listbox con ítems seleccionados

Nótese que curselection() retorna una tupla con los índices seleccionados. Para obtener el texto de cada ítem a partir del índice usamos el método get(), como se observa en la línea 13 del código anterior.

Para añadir uno o varios elementos a la selección utilizamos selection_set().

# Seleccionar el tercer elemento.
listbox.selection_set(2)
# Seleccionar todos los elementos.
listbox.selection_set(0, tk.END)

De forma similar opera selection_clear(), para remover la selección.

Por último, selection_includes() retorna True si el ítem con la posición indicada se encuentra seleccionado; False en caso contrario.

items = (
    "Python",
    "C",
    "C++",
    "Java"
)
listbox.insert(0, *items)
listbox.selection_set(2)
print(listbox.selection_includes(2))   # Imprime True.

Otras funciones

El método see() ajusta la visión de la lista para hacer visible un elemento determinado. Por ejemplo:

import tkinter as tk

ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
listbox = tk.Listbox()
for i in range(1, 101):
    listbox.insert(tk.END, f"Elemento {i}")
# Desplazar la vista del listbox al elemento
# en la posición 30.
listbox.see(30)
listbox.pack()
ventana.mainloop()

Barra de deslizamiento

El siguiente código añade una barra de deslizamiento vertical (ttk.Scrollbar) cuando la cantidad de elementos de la lista excede el límite visible.

import tkinter as tk
from tkinter import ttk


ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
# Marco para contener el listbox y la barra de desplazamiento.
frame = ttk.Frame()
listbox = tk.Listbox()
# Crear una barra de deslizamiento con orientación vertical.
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL)
# Vincularla con la lista.
listbox = tk.Listbox(frame, yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)
# Ubicarla a la derecha.
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
listbox.pack()
frame.pack()
# Insertar 100 ítems.
for i in range(1, 101):
    listbox.insert(tk.END, f"Elemento {i}")
ventana.mainloop()

Listbox con barra de desplazamiento

Si el texto de los elementos excede el ancho de la lista, también puede incluirse una barra de desplazamiento horizontal:

import tkinter as tk
from tkinter import ttk


ventana = tk.Tk()
ventana.geometry("400x300")
ventana.title("Lista en Tk")
frame = ttk.Frame()
listbox = tk.Listbox()
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL)
hscrollbar = ttk.Scrollbar(frame, orient=tk.HORIZONTAL)
listbox = tk.Listbox(
    frame, yscrollcommand=scrollbar.set, xscrollcommand=hscrollbar.set
)
scrollbar.config(command=listbox.yview)
hscrollbar.config(command=listbox.xview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
hscrollbar.pack(side=tk.BOTTOM, fill=tk.X)
listbox.pack()
frame.pack()
for i in range(1, 101):
    listbox.insert(tk.END, f"Este es el elemento número {i} de la lista")
ventana.mainloop()

Estilos

La clase Listbox acepta una serie de parámetros para modificar el estilo de la lista.

Notarás que por defecto el widget contiene un borde. La propiedad que controla este estilo es borderwidth, que indica su tamaño en píxeles. Para removerlo utilícese:

listbox = tk.Listbox(borderwidth=0)

Además, los elementos tienen un subrayado cuando están seleccionados. Para removerlo debe configurarse la propiedad activestyle a tk.NONE (otro valor puede ser tk.DOTBOX, para dibujar un recuadro de puntos).

listbox = tk.Listbox(activestyle=tk.NONE)

Tk permite configurar el color del borde que se muestra alrededor de la lista cuando tiene el foco. Por ejemplo, el siguiente código dibuja un recuadro de color rojo cuando el control no tiene el foco y uno de color azul cuando sí lo tiene.

listbox = tk.Listbox(highlightcolor="#0000ff", highlightbackground="#ff0000")

Los colores se indican con un numeral seguido de valores hexadecimales en el formato RGB. Puede utilizarse este selector de color para obtener código de color.

También es posible indicar el grosor, en píxeles, vía highlightthickness.

El fondo y el color del texto de la lista en general es controlado por las propiedades background o bg y foreground o fg, respectivamente.

# Fondo negro y texto blanco.
listbox = tk.Listbox(bg="#000", fg="#fff")

Listbox con fondo negro y letra blanca

Otras propiedades configurables incluyen selectbackground, selectforeground y selectborderwidth, que determinan el color de fondo, color de texto y tamaño del borde de un elemento seleccionado.

# Fondo verde con texto blanco para la selección.
listbox = tk.Listbox(selectforeground="#ffffff",
                     selectbackground="#00aa00",
                     selectborderwidth=5)

Listbox con estilos en la selección

Y para indicar la fuente:

listbox = tk.Listbox(font=Font(family="Sans Serif", size=10))

Debe importarse Font previamente vía from tkinter.font import Font.

Por último, también puede darse estilo a elementos individuales, indicando su posición vía la función itemconfigure.

listbox = tk.Listbox()
items = (
    "Python",
    "C",
    "C++",
    "Java"
)
listbox.insert(0, *items)
listbox.itemconfigure(0, bg="#00aa00", fg="#fff")
listbox.itemconfigure(3, bg="#ff0000", fg="#fff")

Listbox con estilos en los ítems

Las propiedades válidas para elementos individuales son background o bg, foreground o fg, selectbackground y selectforeground.

Curso online 👨‍💻

¡Ya lanzamos el curso oficial de Recursos Python en Udemy! Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter en 2024.

Consultoría 💡

Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.

12 comentarios.

    • Recursos Python says:

      Hola. Sí, fijate en el último apartado, está explicitado cómo cambiar la fuente (tipografía y tamaño):

      listbox = tk.Listbox(font=Font(family="Sans Serif", size=10))

      Saludos

  1. hola muy buenas como hago para establecer márgenes dentro de las listbox, y también poder expandir la listbox, ya que quiero hacer una consola que imprima texto necesito también una separación por cada elemento lo intente con ‘\n’ incluso insertando un espacio pero no me funciona.

    • Recursos Python says:

      Hola. Lamentablemente no hay ninguna opción para configurar los márgenes internos (es decir, entre elementos) de una lista.

      Saludos

  2. Hola y gracias, un apregunta, cuando quiero incluir una lista que introdusco en un campo Entry, tipo (1, 2, 3, 4), para incluirlo en una funcion que luego de obtener el resultado lo incluya en otro campo Entry, no lo reconoce como lista, y sale un error que dice que no se puede convertir str en int o no es iterable, quisiera saber cual es el problema por favor?

    • Recursos Python says:

      Hola. Todo lo que escribas en una caja de texto lo vas a obtener vía el método get() como una cadena, independientemente de si es un número, una lista, una coordenada o lo que fuera. Después tendrás que hacer las conversiones correspondientes. Si querés pasate por el foro y mostranos el código para verlo con mayor detalle.

      Saludos

  3. Hola como cambio la ubicación a la lista en la ventana,para agregarle más cosas a la ventana, ya que la necesito para visualizar una lista de claves de id de un sqlite, y ademas como al seleccionar un elemento de la lista crear con un evento para que me muestre los datos de la clave en un formulario para modificarlos

  4. hola, buenas tardes.

    he estado ojeando el artículo. Está bien, y solo una puntualización:

    en el apartado estilos relativo al ancho del borde

    self.listbox = tk.Listbox(self, borderwidth=0)

    yo le añadiría una configuración más:

    highlightthickness=0

    para completar el engaño visual. Aunque esto tambien puede depender del widget ‘padre’

    nota: lo he probado en windows 10. Lo comento, porque en linux algunos widgets se ven distinto

    • Recursos Python says:

      Hola, ¿cómo estás? Coincido. La propiedad highlightthickness controla el grosor del borde del control cuando tiene el foco. Creí haberlo pasado por alto pero está especificado unas líneas más abajo del código al que hacés mención.

      ¡Saludos!

Deja una respuesta