Índice de contenidos
1. Introducción
2. Entorno
3. Widgets
4. Configuración
5. Gestión de la composición
6. Ejecución
7. Ejemplo práctico
1. Introducción
Tkinter es el paquete más utilizado para crear interfaces gráficas en Python. Es una capa orientada a objetos basada en Tcl (sencillo y versátil lenguaje de programación open-source) y Tk (la herramienta GUI estándar para Tcl).
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2.2 Ghz Intel Core i7, 16GB DDR3).
- Sistema Operativo: Mac OS Mojave 10.14
- Aplicación de desarrollo: Sublime Text 3.2.2
3. Widgets
A la hora de montar una vista con Tkinter, nos basaremos en widgets jerarquizados, que irán componiendo poco a poco nuestra interfaz. Algunos de los más comunes son:
- Tk: es la raíz de la interfaz, donde vamos a colocar el resto de widgets.
- Frame: marco que permite agrupar diferentes widgets.
- Label: etiqueta estática que permite mostrar texto o imagen.
- Entry: etiqueta que permite introducir texto corto (típico de formularios).
- Text: campo que permite introducir texto largo (típico para añadir comentarios).
- Button: ejecuta una función al ser pulsado.
- Radiobutton: permite elegir una opción entre varias.
- Checkbutton: permite elegir varias de las opciones propuestas.
- Menu: clásico menú superior con opciones (Archivo, Editar…).
- Dialogs: ventana emergente (o pop-up).
Cuando vayamos a inicializar el componente, debemos pasar por constructor el elemento que quede “por encima” en la jerarquía de la vista (si queremos colocar una label dentro de un frame, al construir la etiqueta le pasaremos el marco como argumento del constructor).
4. Configuración
Para configurar un widget, simplemente llamamos a .config() y pasamos los argumentos que queramos modificar. Algunas opciones son:
- bg: modifica el color de fondo. Se puede indicar con el color en inglés (incluyendo modificadores, como “darkgreen”) o su código RGB en hexadecimal (“#aaaaaa” para blanco). Ojo: en MacOS no se puede modificar el color de fondo de los botones; aunque indiquemos un nuevo color, se mostrará en blanco. Lo más parecido que podemos hacer es configurar el highlightbackground, que pintará el fondo alrededor del botón del color que indiquemos.
- fg: cambia el color del texto.
- cursor: modifica la forma del cursor. Algunos de los más utilizados son “gumby”, “pencil”, “watch” o “cross”.
- height: altura en líneas del componente.
- width: anchura en caracteres del componente.
- font: nos permite especificar, en una tupla con nombre de la fuente, tamaño y estilo, la fuente a utilizar en el texto del componente. Por ejemplo, Font(“Times New Roman”, 24, “bold underline”).
- bd: modificamos la anchura del borde del widget.
- relief: cambiamos el estilo del borde del componente. Su valor puede ser “flat”, “sunken”, “raised”, “groove”, “solid” o “ridge”.
- state: permite deshabilitar el componente (state=DISABLED); por ejemplo, una Label en la que no se puede escribir o un Button que no se puede clickar.
- padding: espacio en blanco alrededor del widget en cuestión.
- command: de cara a que los botones hagan cosas, podemos indicar qué función ejecutar cuando se haga click en el mismo.
5. Gestión de la composición
Es MUY IMPORTANTE que, cuando tengamos configurado el componente, utilicemos un gestor de geometría de componentes. Si no, el widget quedará creado pero no se mostrará.
Los tres más conocidos son:
- Pack: cuando añadimos un nuevo componente, se “hace hueco” a continuación de los que ya están incluidos (podemos indicar que se inserte en cualquiera de las 4 direcciones), para finalmente calcular el tamaño que necesita el widget padre para contenerlos a todos.
- Place: este es el más sencillo de entender, pero puede que no el más sencillo de utilizar para todo el mundo. Al insertar un componente, podemos indicar explícitamente la posición (coordenadas X e Y) dentro del widget padre, ya sea en términos absolutos o relativos.
- Grid: la disposición de los elementos es una matriz, de manera que para cada uno debemos indicar la celda (fila y columna) que queremos que ocupe. Podemos además especificar que ocupe más de una fila y/o columna (rowspan/columnspan=3), “pegarlo” a cualquiera de los 4 bordes de la celda en vez de centrarlo (sticky=W para el borde izquierdo, por ejemplo)…
Personalmente, me siento mucho más cómodo utilizando un Grid: de esta manera te aseguras la distribución de los componentes, y la hora de añadir nuevos es muy visual poder pensar en ello como matriz, en vez de coordenadas o posiciones relativas a otros widgets.
6. Ejecución
Una vez tenemos todos los componentes creados, configurados y añadidos en la estructura, debemos terminar el script con la instrucción tk.mainloop() (“tk” = variable Tk). Así, cuando lo ejecutemos, se abrirá la ventana principal de nuestra GUI.
Bricotruco: si guardamos nuestro script con formato .pyw en vez de .py, al ejecutarlo se abrirá nuestra interfaz, sin tener que pasar por terminal o abrir algún IDE para ello.
7. Ejemplo práctico
Finalmente, os dejo por aquí el código de una sencilla calculadora, que hace uso de varios componentes y configuraciones. Espero que os haya gustado Tkinter y que lo explotéis al máximo en vuestras aplicaciones pythoneras!
from tkinter import * from tkinter import messagebox class Pycalc(Frame): def __init__(self, master, *args, **kwargs): Frame.__init__(self, master, *args, **kwargs) self.parent = master self.grid() self.createWidgets() def deleteLastCharacter(self): textLength = len(self.display.get()) if textLength >= 1: self.display.delete(textLength - 1, END) if textLength == 1: self.replaceText("0") def replaceText(self, text): self.display.delete(0, END) self.display.insert(0, text) def append(self, text): actualText = self.display.get() textLength = len(actualText) if actualText == "0": self.replaceText(text) else: self.display.insert(textLength, text) def evaluate(self): try: self.replaceText(eval(self.display.get())) except (SyntaxError, AttributeError): messagebox.showerror("Error", "Syntax Error") self.replaceText("0") except ZeroDivisionError: messagebox.showerror("Error", "Cannot Divide by 0") self.replaceText("0") def containsSigns(self): operatorList = ["*", "/", "+", "-"] display = self.display.get() for c in display: if c in operatorList: return True return False def changeSign(self): if self.containsSigns(): self.evaluate() firstChar = self.display.get()[0] if firstChar == "0": pass elif firstChar == "-": self.display.delete(0) else: self.display.insert(0, "-") def inverse(self): self.display.insert(0, "1/(") self.append(")") self.evaluate() def createWidgets(self): self.display = Entry(self, font=("Arial", 24), relief=RAISED, justify=RIGHT, bg='darkblue', fg='red', borderwidth=0) self.display.insert(0, "0") self.display.grid(row=0, column=0, columnspan=4, sticky="nsew") self.ceButton = Button(self, font=("Arial", 12), fg='red', text="CE", highlightbackground='red', command=lambda: self.replaceText("0")) self.ceButton.grid(row=1, column=0, sticky="nsew") self.inverseButton = Button(self, font=("Arial", 12), fg='red', text="1/x", highlightbackground='lightgrey', command=lambda: self.inverse()) self.inverseButton.grid(row=1, column=2, sticky="nsew") self.delButton = Button(self, font=("Arial", 12), fg='#e8e8e8', text="Del", highlightbackground='red', command=lambda: self.deleteLastCharacter()) self.delButton.grid(row=1, column=1, sticky="nsew") self.divButton = Button(self, font=("Arial", 12), fg='red', text="/", highlightbackground='lightgrey', command=lambda: self.append("/")) self.divButton.grid(row=1, column=3, sticky="nsew") self.sevenButton = Button(self, font=("Arial", 12), fg='white', text="7", highlightbackground='black', command=lambda: self.append("7")) self.sevenButton.grid(row=2, column=0, sticky="nsew") self.eightButton = Button(self, font=("Arial", 12), fg='white', text="8", highlightbackground='black', command=lambda: self.append("8")) self.eightButton.grid(row=2, column=1, sticky="nsew") self.nineButton = Button(self, font=("Arial", 12), fg='white', text="9", highlightbackground='black', command=lambda: self.append("9")) self.nineButton.grid(row=2, column=2, sticky="nsew") self.multButton = Button(self, font=("Arial", 12), fg='red', text="*", highlightbackground='lightgrey', command=lambda: self.append("*")) self.multButton.grid(row=2, column=3, sticky="nsew") self.fourButton = Button(self, font=("Arial", 12), fg='white', text="4", highlightbackground='black', command=lambda: self.append("4")) self.fourButton.grid(row=3, column=0, sticky="nsew") self.fiveButton = Button(self, font=("Arial", 12), fg='white', text="5", highlightbackground='black', command=lambda: self.append("5")) self.fiveButton.grid(row=3, column=1, sticky="nsew") self.sixButton = Button(self, font=("Arial", 12), fg='white', text="6", highlightbackground='black', command=lambda: self.append("6")) self.sixButton.grid(row=3, column=2, sticky="nsew") self.minusButton = Button(self, font=("Arial", 12), fg='red', text="-", highlightbackground='lightgrey', command=lambda: self.append("-")) self.minusButton.grid(row=3, column=3, sticky="nsew") self.oneButton = Button(self, font=("Arial", 12), fg='white', text="1", highlightbackground='black', command=lambda: self.append("1")) self.oneButton.grid(row=4, column=0, sticky="nsew") self.twoButton = Button(self, font=("Arial", 12), fg='white', text="2", highlightbackground='black', command=lambda: self.append("2")) self.twoButton.grid(row=4, column=1, sticky="nsew") self.threeButton = Button(self, font=("Arial", 12), fg='white', text="3", highlightbackground='black', command=lambda: self.append("3")) self.threeButton.grid(row=4, column=2, sticky="nsew") self.plusButton = Button(self, font=("Arial", 12), fg='red', text="+", highlightbackground='lightgrey', command=lambda: self.append("+")) self.plusButton.grid(row=4, column=3, sticky="nsew") self.negToggleButton = Button(self, font=("Arial", 12), fg='red', text="+/-", highlightbackground='lightgrey', command=lambda: self.changeSign()) self.negToggleButton.grid(row=5, column=0, sticky="nsew") self.zeroButton = Button(self, font=("Arial", 12), fg='white', text="0", highlightbackground='black', command=lambda: self.append("0")) self.zeroButton.grid(row=5, column=1, sticky="nsew") self.decimalButton = Button(self, font=("Arial", 12), fg='white', text=".", highlightbackground='lightgrey', command=lambda: self.append(".")) self.decimalButton.grid(row=5, column=2, sticky="nsew") self.equalsButton = Button(self, font=("Arial", 12), fg='red', text="=", highlightbackground='lightgrey', command=lambda: self.evaluate()) self.equalsButton.grid(row=5, column=3, sticky="nsew") Calculator = Tk() Calculator.title("AdictoCalculator") Calculator.resizable(False, False) Calculator.config(cursor="pencil") root = Pycalc(Calculator).grid() Calculator.mainloop()
Hola Buenas tardes, estoy comenzando a desarrollar aplicaciones en Python
Me gustaría si puedes darme ejemplo de programas de manejo de interfaz grafica
excelente
[…] es plan de escribir lo que ya está escrito, pero sí de acomodarlo a nuestros esquemas. En http://adictosaltrabajo.com/2020/06/30/interfaces-graficas-en-python-con-tkinter/ puedes encontrar muy bien explicados los diferentes widgets (se llaman así) que podemos poner […]
Hola!! Muy buenos días…
Todo va super bien, muchas gracias, eres resultado de una búsqueda de una persona que explicara mas a meno, no a todos se les entiende, jajaja y creo que le descubrí, la piedra en el zapato a tu calculadora, jajaja la cuota del alumno jajajaj, white x black. voy a seguir tu curso.