«Checkbox» (Checkbutton) en Tcl/Tk (tkinter)



Nota: este artículo se aplica a la clase ttk.Checkbutton, introducida en Tk 8.5.

El control ttk.Checkbutton, también conocido en la jerga como «Checkbox», es un tipo de botón que permite representar dos estados opuestos (activado/desactivado, encendido/apagado, sí/no, etc.) o bien un estado indeterminado.

El siguiente código crea un checkbox con el texto «Opción» como se muestra en la imagen anterior.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
from tkinter import ttk

class Application(ttk.Frame):
    
    def __init__(self, main_window):
        super().__init__(main_window)
        main_window.title("Checkbutton en tkinter")
        
        self.checkbox = ttk.Checkbutton(self, text="Opción")
        self.checkbox.place(x=40, y=70)
        
        self.place(width=300, height=200)

main_window = tk.Tk()
app = Application(main_window)
app.mainloop()

Nótese, sin embargo, que el control se inicializa con un estado indeterminado. Para poder obtener acceso al estado de un checkbox primero es necesario crear una variable que pueda almacenar su valor.

Por conveniencia utilizamos tk.BooleanVar, que representará el estado «activado» como True y «desactivado» como False.

        self.checkbox_value = tk.BooleanVar(self)
        self.checkbox = ttk.Checkbutton(self,
            text="Opción", variable=self.checkbox_value)

Por defecto, tk.BooleanVar se inicializa con el valor False, por lo que inicialmente el control no está chequeado.

Ahora podemos establcer u obtener el estado leyendo el valor de la variable con las funciones set() y get(), respectivamente. Por ejemplo, el siguiente código utiliza el parámetro command para imprimir el valor del control cada vez que éste es presionado.

        self.checkbox_value = tk.BooleanVar(self)
        self.checkbox = ttk.Checkbutton(self,
            text="Opción",
            variable=self.checkbox_value,
            command=self.checkbox_clicked)
        self.checkbox.place(x=40, y=70)
        
        self.place(width=300, height=200)
    
    def checkbox_clicked(self):
        print(self.checkbox_value.get())

De la misma forma, si quisiéramos inicializar el control con un valor positivo, podemos utilizar:

        self.checkbox_value = tk.BooleanVar(self)
        self.checkbox_value.set(True)

Podemos simular un cambio de estado en el control empleando la función invoke. Una llamada a invoke() es similar a que el usuario haya cliqueado sobre el checkbox. Por ejemplo, podríamos añadir un botón que sea capaz de cambiar el estado.

        self.checkbox_value = tk.BooleanVar(self)
        self.checkbox = ttk.Checkbutton(self,
            text="Opción",
            variable=self.checkbox_value,
            command=self.checkbox_clicked)
        self.checkbox.place(x=40, y=70)

        self.button = ttk.Button(self,
            text="Cambiar", command=self.button_clicked)
        self.button.place(x=40, y=130)
        self.place(width=300, height=200)
    
    def checkbox_clicked(self):
        print(self.checkbox_value.get())
    
    def button_clicked(self):
        self.checkbox.invoke()

La diferencia entre llamar a invoke() y cambiar el valor manualmente utilizando self.checkbox_value.set() es que el primer método simula por completo un cambio de estado, eso incluye la llamada a la función checkbox_clicked o cualquier otra registrada vía el parámetro command, e incluso el control recibe el foco como se esperaría ante un clic del usuario.

Una alternativa más agradable

Conociendo ya los detalles de la clase Checkbutton, podemos obtener un nuevo control que se base en ésta pero que otorgue una implementación más agradable.

En nuestro código anterior, debíamos crear dos objetos: el control en sí (self.checkbox) y una variable que almacene su estado (self.checkbox_value). Para simplificar esta operación, nuestra clase introduce tres funciones:

  • checked() para determinar el estado del control (True o False).
  • check() y uncheck() para establecer los estados.

Solo es necesario crear un objeto. Veamos el ejemplo:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
from tkinter import ttk


class Checkbox(ttk.Checkbutton):
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.variable = tk.BooleanVar(self)
        self.configure(variable=self.variable)
    
    def checked(self):
        return self.variable.get()
    
    def check(self):
        self.variable.set(True)
    
    def uncheck(self):
        self.variable.set(False)


class Application(ttk.Frame):
    
    def __init__(self, main_window):
        super().__init__(main_window)
        main_window.title("Checkbutton en tkinter")
        
        # No es necesario crear una variable.
        self.checkbox = Checkbox(self,
            text="Opción", command=self.check_clicked)
        self.checkbox.place(x=40, y=70)
        
        self.place(width=300, height=200)
    
    def check_clicked(self):
        print(self.checkbox.checked())


main_window = tk.Tk()
app = Application(main_window)
app.mainloop()

Como podrás observar, nuestro código se torna más compacto y legible. Además, incluye los métodos explicados anteriormente, como invoke().

Otras consideraciones

Por conveniencia se suele utilizar una variable booleana para representar los estados de un checkbox. Sin embargo, en Tcl/Tk, puede usarse cualquier otro tipo, como IntVar o StringVar.

Por ejemplo, un control podría almacenar su estado como cadenas de caracteres, a saber, "activado" y "desactivado".

        self.checkbutton_value = tk.StringVar(self)
        self.checkbutton = ttk.Checkbutton(
            self,
            text="Opción",
            variable=self.checkbutton_value,
            onvalue="activado",
            offvalue="desactivado")

De esta forma, self.checkbutton_value.get() retornará alguno de los dos valores especificados en los parámetros onvalue y offvalue.

No es posible determinar qué valor retornará get() si el usuario aún no ha presionado sobre el checkbox, pero se tiene la certeza de que será un valor distinto a onvalue y offvalue. En nuestro ejemplo, podemos considerar:

chosen = self.checkbutton_value.get() in ("activado", "desactivado")

chosen será igual a False cuando aun no se ha hecho una elección y True en caso contrario.



Deja una respuesta