Autor: Luis Fernando Apáez Álvarez
-Curso PyM-
Proyecto 1: Conocimientos previos (parte 2)
Fecha: 27 de Agosto del 2022
Ahora bien, lo que haremos en esta clase será crear el "esqueleto" de una calculadora básica utilizando la librería Tkinter.
Decimos el "esqueleto" en el sentido de que sólo programaremos la estructura que se ve en la imagen anterior, pero no haremos que la calculadora sea funcional, pues de hacerlo, dicha calculadora fácilmente se podría convertir en un proyecto diferente al que estamos desarrollando y lo que queremos, por ahora, es sólo concentrarnos en el proyecto de crear una calculadora graficadora y que calcula límites y derivadas. El fin de esta clase es practicar los conceptos que ya trabajamos en la primera parte y ver algunos otros más adicionales. Para ello:
Como lo vimos en la parte anterior, podemos crear una ventana y configurarla escribiendo:
# Importaciones necesarias
import tkinter as tk
from tkinter import ttk
# Creamos un objeto ventana
ventana = tk.Tk()
# titulo de la ventana
ventana.title('Calculadora')
# tamanio de la ventana
ventana.geometry('500x300')
# mostramos la ventana
ventana.mainloop()
Lo que haremos a continuación será crear y agregar un menú desplegable a nuestra ventana principal
# Creamos un objeto ventana
ventana = tk.Tk()
# titulo de la ventana
ventana.title('Calculadora')
# tamanio de la ventana
ventana.geometry('500x300')
# --------------------------------------------------------
# --------------------------------------------------------
# Creamos un menu principal en la ventana antes creada
menu_principal = tk.Menu(ventana)
# agregamos el menu a la ventana
ventana.config(menu = menu_principal)
# Creamos un submenu de opciones para el menu principal
submenu = tk.Menu(menu_principal, tearoff = False)
# Agregamos opciones a dicho submenu:
# comando para| Texto a mostrar | acciones que se
# agregar | referente a esa | realizaran al
# opciones | opcion | presionar esa opc
submenu.add_command(label='Nueva ventana', command = _ _ _ )
submenu.add_command(label='Salir', command = ventana.destroy)
# Agregamos el submenu al menu_principal
# Nombre del | Submenu a
# menu | agregar
menu_principal.add_cascade(label='Menú', menu=submenu)
# mostramos la ventana
ventana.mainloop()
Notemos que en la opción 'Salir'
del submenu
le asociamos el comando (o la acción) ventana.destroy
el cual sirve para cerrar la ventana que le especifiquemos. De manera general
<nombre de la ventana a cerrar>.destroy
Así, cuando seleccionemos la opción 'Salir
del menú, entonces la ventana principal (ventana
) se cerrará. Ahora definiremos una función para asociarla a la opción 'Nueva ventana'
del submenu
; esta opción hará que, al ser seleccionada, nos abra una nueva ventana. Para ello:
def nueva_ventana():
# Creamos una nueva ventana y la denominamos new_ventana
# Nombre de la | Metodo para crear
# ventana nueva | una nueva ventana
new_ventana = tk.Toplevel()
# Configuramos el tamanio de esta nueva ventana en
# 500px por 200px
new_ventana.geometry('500x200')
# Titulo de esta nueva ventana
new_ventana.title('Ventana Nueva :D')
Ahora sí, podemos combinar todo el código que llevamos como sigue
# Creamos un objeto ventana
ventana = tk.Tk()
# titulo de la ventana
ventana.title('Calculadora')
# tamanio de la ventana
ventana.geometry('500x300')
# --------------------------------------------------------
# funcion asociada a la opcion Nueva ventana del submenu
def nueva_ventana():
# Creamos una nueva ventana y la denominamos new_ventana
new_ventana = tk.Toplevel()
# Configuramos el tamanio de esta nueva ventana en 500 por 200
new_ventana.geometry('500x200')
# Titulo de esta nueva ventana
new_ventana.title('Ventana Nueva :D')
# Creamos un menu principal en la ventana antes creada
menu_principal = tk.Menu(ventana)
# agregamos el menu a la ventana
ventana.config(menu = menu_principal)
# Creamos un submenu de opciones para el menu principal
submenu = tk.Menu(menu_principal, tearoff = False)
# Agregamos opciones a dicho submenu:
# le asociamos la funcion
# que definimos antes
submenu.add_command(label='Nueva ventana', command = nueva_ventana)
submenu.add_command(label='Salir', command = ventana.destroy)
# Agregamos el submenu al menu_principal
menu_principal.add_cascade(label='Menú', menu=submenu)
# mostramos la ventana
ventana.mainloop()
Si seleccionamos la opción de Nueva ventana
se nos debe mostrar:
la ventana nueva que creamos en al función nueva_ventana
.
Lo que haremos ahora será agregar todos los botones que vimos en la primer imagen, además agregaremos una caja de texto. Para el posicionamiento de todos los componentes anteriores utilizaremos el método grid()
como vimos en la primera parte.
# Caja de texto:
# Fila 1
caja_fun = ttk.Entry(ventana, width=30)
caja_fun.grid(row=0, column=0)
# Botones:
# Fila 2
boton_izq = ttk.Button(ventana, text='(')
boton_izq.grid(row=1, column=0)
boton_der = ttk.Button(ventana, text=')')
boton_der.grid(row=1, column=1)
boton_porc = ttk.Button(ventana, text='%')
boton_porc.grid(row=1, column=2)
boton_ac = ttk.Button(ventana, text='AC')
boton_ac.grid(row=1, column=3)
# Fila 3
boton_1 = ttk.Button(ventana, text='1')
boton_1.grid(row=2, column=0
)
boton_2 = ttk.Button(ventana, text='2')
boton_2.grid(row=2, column=1)
boton_3 = ttk.Button(ventana, text='3')
boton_3.grid(row=2, column=2)
boton_sum = ttk.Button(ventana, text='+')
boton_sum.grid(row=2, column=3)
# Fila 4
boton_4 = ttk.Button(ventana, text='4')
boton_4.grid(row=3, column=0)
boton_5 = ttk.Button(ventana, text='5')
boton_5.grid(row=3, column=1)
boton_6 = ttk.Button(ventana, text='6')
boton_6.grid(row=3, column=2)
boton_res = ttk.Button(ventana, text='-')
boton_res.grid(row=3, column=3)
# Fila 5
boton_7 = ttk.Button(ventana, text='7')
boton_7.grid(row=4, column=0)
boton_8 = ttk.Button(ventana, text='8')
boton_8.grid(row=4, column=1)
boton_9 = ttk.Button(ventana, text='9')
boton_9.grid(row=4, column=2)
boton_mult = ttk.Button(ventana, text='*')
boton_mult.grid(row=4, column=3)
# Fila 6
boton_0 = ttk.Button(ventana, text='0')
boton_0.grid(row=5, column=0)
boton_pt = ttk.Button(ventana, text='.')
boton_pt.grid(row=5, column=1)
boton_igual = ttk.Button(ventana, text='=')
boton_igual.grid(row=5, column=2)
boton_div = ttk.Button(ventana, text='÷')
boton_div.grid(row=5, column=3)
El código anterior lo agregaremos entre el final de la configuración de la ventana y el comienzo de la definición de la función nueva_ventana
:
ventana.geometry('500x300')
# --------------------------------------------------------
#. Botones
#. Botones
#. Botones
# funcion asociada a la opcion Nueva ventana del submenu
def nueva_ventana():
y se nos deberá mostrar, al ejecutar todo el código junto, algo como:
A pesar que los botones ya se ven y están bien colocados mediante las coordenadas, vemos que los espacios manejados, por defecto, en la cuadrícula no son los óptimos para la interfaz nuestra calculadora. De lo anterior podemos decir que:
columnspan=
al método grid()
, el cual especifica el número de columnas que ocupará, en este caso, nuestra caja de texto# cambiamos el numero total
# de caracteres a almacenar
# en esta caja de texto
caja_fun = ttk.Entry(ventana, width=40)
caja_fun.grid(row=0, column=0, columnspan=4)
Notamos que, con lo anterior, los botones se han juntado y adaptado al tamaño de la caja de texto, pero aún así no logramos ocupar todo el espacio de nuestra ventana.
grid()
en cada uno de los botones. Dicho parámetro es sticky=
. Antes de continuar, es preciso mencionar que dicho parámetro se complementa, primero, realizando configuraciones a las filas y columnas de nuestra cuadrícula. Dichas configuraciones definen una proporción entre el tamaño de las filas entre sí, o una proporción entre el tamaño de las columnas entre sí, respecto al tamaño total de la ventana. Por ejemplo# metodo para|numero| peso
# configurar |de la | referente a la
# la propor- |fila | proporcion
# de la fila | |
ventana.rowconfigure(0, weight=1)
ventana.rowconfigure(1, weight=2)
con lo anterior tenemos la proporción 1:2 de la fila 0 respecto a la fila 1, entorno al tamaño total de la ventana. De manera totalmente análoga podemos escribir
# metodo para|numero|peso
# configurar |de la |referente
# la propor- |colum-|a la
# de la colu-|na |proporcion
# na | |
ventana.columnconfigure(0, weight=1)
ventana.columnconfigure(1, weight=2)
con lo anterior tenemos la proporción 1:2 de la columna 0 respecto a la columna 1, entorno al tamaño total de la ventana. Así, dado que la interfaz de la calculadora implica 6 filas por 4 columnas, entonces realizamos la configuración:
# Creamos un objeto ventana
ventana = tk.Tk()
# titulo de la ventana
ventana.title('Calculadora')
# tamanio de la ventana
ventana.geometry('500x300')
# --------------------------------------------------------
# CONFIGURACION DE LAS FILAS Y COLUMNAS
# Filas:
ventana.rowconfigure(0, weight=1)
ventana.rowconfigure(1, weight=2)
ventana.rowconfigure(2, weight=2)
ventana.rowconfigure(3, weight=2)
ventana.rowconfigure(4, weight=2)
ventana.rowconfigure(5, weight=2)
# Columnas
ventana.columnconfigure(0, weight=2)
ventana.columnconfigure(1, weight=2)
ventana.columnconfigure(2, weight=2)
ventana.columnconfigure(3, weight=1)
# .
# .
# .
# .
donde:
Para las filas: se configuró una proporcición de 1:2 respecto a la fila 0 con el resto de las filas, entorno al tamaño total de la ventana. Lo anterior hará que la fila 0 sea más pequeña que el resto de las filas; además, con lo anterior, ocuparemos todo el tamaño de las filas de nuestra ventana.
Para las columnas: se configuró una proporcición de 1:2 respecto a la columna 3 con el resto de las columnas, entorno al tamaño total de la ventana. Lo anterior hará que la columna 3 sea más pequeña que el resto de las columnas; además, con lo anterior, ocuparemos todo el tamaño de las columnas de nuestra ventana.
Entonces conseguimos, con base en lo anterior, ocupar todo el espacio de nuestra ventana. Veamos cómo se ve ahora la interfaz de nuestra calculadora
A pesar de que ya ocupamos todo el espacio de la ventana, vemos que los botones no ocupan, propiamente, el espacio total de su coordenada. Esto es, si consideramos el botón en la coordenada (1,0)
, dicho botón solo ocupa una porción del tamaño total del espacio designado a dicha coordenada. Podemos decir lo mismo para el resto de los componentes. Es aquí donde ocuparemos el parámetro sticky
en el método grid()
.
Con este parámetro podemos podemos especificar la posición que ocuparán, en este caso, nuestros botones. Así:
sticky='E'
: Nuestro botón se posicionará en dirección al este, en otras palabras, nuestro botón se posicionará a la derecha del espacio total destinado a dicha coordenada.sticky='W'
: Nuestro botón se posicionará a la izquierda (oeste o west) del espacio total destinado a dicha coordenada.sticky='N'
: Nuestro botón se posicionará arriba (o hacia el norte) del espacio total destinado a dicha coordenada.sticky='S'
: Nuestro botón se posicionará abajo (o hacia el sur) del espacio total destinado a dicha coordenada.sticky='WE'
: Nuestro botón se posicionará en el espacio de izquierda a derecha, de modo que dicho botón ocupará todo el espacio fila disponible para la coordenada donde se ubica. Cabe resaltar que los valores 'WE'
y 'EW'
son equivalentes. sticky='NS'
: Nuestro botón se posicionará en el espacio de arriba a abajo, de modo que dicho botón ocupará todo el espacio columna disponible para la coordenada donde se ubica. Cabe resaltar que los valores 'NS'
y 'SN'
son equivalentes. sticky='NSWE'
: Nuestro botón se posicionará en el espacio de arriba a abajo, de modo que dicho botón ocupará todo el espacio columna disponible para la coordenada donde se ubica, además, se posicionará en el espacio de izquierda a derecha, de modo que dicho botón ocupará todo el espacio fila disponible para la coordenada donde se ubica. Entonces, con dicha especificación nuestro botón ocupará todo el espacio disponible para dicha coordenada.Lo anterior puede ilustrarse como:
que fue generado al escribir:
# Creamos un objeto ventana
ventana = tk.Tk()
# titulo de la ventana
ventana.title('Calculadora')
# tamanio de la ventana
ventana.geometry('500x300')
# --------------------------------------------------------
# Configuracion de las filas y columnas
ventana.rowconfigure(0, weight=1)
ventana.rowconfigure(1, weight=2)
ventana.rowconfigure(2, weight=2)
ventana.rowconfigure(3, weight=2)
ventana.rowconfigure(4, weight=2)
ventana.rowconfigure(5, weight=2)
ventana.columnconfigure(0, weight=2)
ventana.columnconfigure(1, weight=2)
ventana.columnconfigure(2, weight=2)
ventana.columnconfigure(3, weight=1)
# Caja de texto:
# Fila 1
caja_fun = ttk.Entry(ventana, width=40)
caja_fun.grid(row=0, column=0, columnspan=4)
# Botones:
# Fila 2
boton_izq = ttk.Button(ventana, text='(')
boton_izq.grid(row=1, column=0, sticky='E')
boton_der = ttk.Button(ventana, text=')')
boton_der.grid(row=1, column=1, sticky='E')
boton_porc = ttk.Button(ventana, text='%')
boton_porc.grid(row=1, column=2, sticky='N')
boton_ac = ttk.Button(ventana, text='AC')
boton_ac.grid(row=1, column=3, sticky='N')
# Fila 3
boton_1 = ttk.Button(ventana, text='1')
boton_1.grid(row=2, column=0, sticky='W'
)
boton_2 = ttk.Button(ventana, text='2')
boton_2.grid(row=2, column=1, sticky='W')
boton_3 = ttk.Button(ventana, text='3')
boton_3.grid(row=2, column=2, sticky='S')
boton_sum = ttk.Button(ventana, text='+')
boton_sum.grid(row=2, column=3, sticky='S')
# Fila 4
boton_4 = ttk.Button(ventana, text='4')
boton_4.grid(row=3, column=0)
boton_5 = ttk.Button(ventana, text='5')
boton_5.grid(row=3, column=1)
boton_6 = ttk.Button(ventana, text='6')
boton_6.grid(row=3, column=2)
boton_res = ttk.Button(ventana, text='-')
boton_res.grid(row=3, column=3)
# Fila 5
boton_7 = ttk.Button(ventana, text='7')
boton_7.grid(row=4, column=0, sticky='EW')
boton_8 = ttk.Button(ventana, text='8')
boton_8.grid(row=4, column=1, sticky='WE')
boton_9 = ttk.Button(ventana, text='9')
boton_9.grid(row=4, column=2, sticky='NS')
boton_mult = ttk.Button(ventana, text='*')
boton_mult.grid(row=4, column=3, sticky='SN')
# Fila 6
boton_0 = ttk.Button(ventana, text='0')
boton_0.grid(row=5, column=0, sticky='NSWE')
boton_pt = ttk.Button(ventana, text='.')
boton_pt.grid(row=5, column=1, sticky='NSWE')
boton_igual = ttk.Button(ventana, text='=')
boton_igual.grid(row=5, column=2, sticky='NSWE')
boton_div = ttk.Button(ventana, text='÷')
boton_div.grid(row=5, column=3, sticky='NSWE')
# .
# .
# .
# .
Concluimos entonces que, para obtener la interfaz de la calculadora que se mostró al inicio, debemos configurar en todos los componentes sticky='NSWE'
. De tal manera, el código final para obtener dicha interfaz de la calculadora es:
import tkinter as tk
from tkinter import ttk
ventana = tk.Tk()
ventana.title('Calculadora')
ventana.geometry('500x300')
ventana.rowconfigure(0, weight=1)
ventana.rowconfigure(1, weight=2)
ventana.rowconfigure(2, weight=2)
ventana.rowconfigure(3, weight=2)
ventana.rowconfigure(4, weight=2)
ventana.rowconfigure(5, weight=2)
ventana.columnconfigure(0, weight=2)
ventana.columnconfigure(1, weight=2)
ventana.columnconfigure(2, weight=2)
ventana.columnconfigure(3, weight=1)
caja_fun = ttk.Entry(ventana, width=30)
caja_fun.grid(row=0, column=0, sticky='NSWE', columnspan=4, padx=2, pady=2)
boton_izq = ttk.Button(ventana, text='(')
boton_izq.grid(row=1, column=0, sticky='NSWE')
boton_der = ttk.Button(ventana, text=')')
boton_der.grid(row=1, column=1, sticky='NSWE')
boton_porc = ttk.Button(ventana, text='%')
boton_porc.grid(row=1, column=2, sticky='NSWE')
boton_ac = ttk.Button(ventana, text='Ac')
boton_ac.grid(row=1, column=3, sticky='NSWE')
boton_1 = ttk.Button(ventana, text='1')
boton_1.grid(row=2, column=0, sticky='NSWE')
boton_2 = ttk.Button(ventana, text='2')
boton_2.grid(row=2, column=1, sticky='NSWE')
boton_3 = ttk.Button(ventana, text='3')
boton_3.grid(row=2, column=2, sticky='NSWE')
boton_sum = ttk.Button(ventana, text='+')
boton_sum.grid(row=2, column=3, sticky='NSWE')
boton_4 = ttk.Button(ventana, text='4')
boton_4.grid(row=3, column=0, sticky='NSWE')
boton_5 = ttk.Button(ventana, text='5')
boton_5.grid(row=3, column=1, sticky='NSWE')
boton_6 = ttk.Button(ventana, text='6')
boton_6.grid(row=3, column=2, sticky='NSWE')
boton_res = ttk.Button(ventana, text='-')
boton_res.grid(row=3, column=3, sticky='NSWE')
boton_7 = ttk.Button(ventana, text='7')
boton_7.grid(row=4, column=0, sticky='NSWE')
boton_8 = ttk.Button(ventana, text='8')
boton_8.grid(row=4, column=1, sticky='NSWE')
boton_9 = ttk.Button(ventana, text='9')
boton_9.grid(row=4, column=2, sticky='NSWE')
boton_mult = ttk.Button(ventana, text='*')
boton_mult.grid(row=4, column=3, sticky='NSWE')
boton_0 = ttk.Button(ventana, text='0')
boton_0.grid(row=5, column=0, sticky='NSWE')
boton_pt = ttk.Button(ventana, text='.')
boton_pt.grid(row=5, column=1, sticky='NSWE')
boton_igual = ttk.Button(ventana, text='=')
boton_igual.grid(row=5, column=2, sticky='NSWE')
boton_div = ttk.Button(ventana, text='÷')
boton_div.grid(row=5, column=3, sticky='NSWE')
def nueva_ventana():
new_ventana = tk.Toplevel()
new_ventana.geometry('500x200')
new_ventana.title('Ventana Nueva :D')
menu_principal = tk.Menu(ventana)
ventana.config(menu = menu_principal)
submenu = tk.Menu(menu_principal, tearoff = False)
submenu.add_command(label='Nueva ventana', command = nueva_ventana)
submenu.add_command(label='Salir', command = ventana.destroy)
menu_principal.add_cascade(label='Menú', menu=submenu)
ventana.mainloop()