Si eres programador o has programado en Python, te habrás dado cuenta lo simple y sencillo que es crear programas, pero también te has dado cuenta que uno de los inconvenientes que existen en Python es la creación de interfaces gráficas.
Tenemos tkinter el modulo nativo para esta tarea, pero el desarrollarlas siempre es complicado y acarrea el escribir muchas lineas de código.
Pero que pensarías si te dijera que existe otra solución mas fácil para crear interfaces gráficas y que incluso puedes hacerlo con una linea de código?
PySimpleGUI es un modulo para el diseño y creacion de interfaces graficas en Python.
Es simple, bello y elegante. Optimiza tus lineas de codigo y velocidad en la creacion las interfaces graficas para tus proyectos en Python.
¿Como se instala PySimpleGUI? Puedes instalarlo desde comandos usando pip.
pip install pysimplegui
pip3 install pysimplegui
Puedes visitar el repositorio en Github para clonar el proyecto directamente:
Primero debemos entender como funciona el diseñador de PySimpleGUI, en PySimpleGUI la colocacion de los objetos gráficos se realiza a modo de filas y columnas, donde introducimos los objetos de acuerdo al orden y posición que queremos que se visualicen.
Sigamos el ejemplo de las siguientes imágenes:
En este ejemplo vemos que el primer elemento es una etiqueta de texto, le sigue un cuadro de texto y finalmente un boton. Sencillo, no?
Ahora veremos como es su programación en PySimpleGUI:
import PySimpleGUI as sg
layout = [[sg.Text('Enter a Number')],
[sg.Input()],
[sg.OK()] ]
event, values = sg.Window('Enter a number example', layout).Read()
sg.Popup(event, values[0])
¿Cómo debe verse la programación GUI en Python? ¿Al menos para principiantes?
Mientras que un objetivo era simplificar la creación de una GUI, otro objetivo tan importante era hacerlo de manera pitónica. Es discutible si logró estos objetivos, pero fue un intento igual.
La clave para ventanas personalizadas en PySimpleGUI es ver ventanas como FILAS de elementos GUI. Cada fila se especifica como una lista de estos elementos. Pon las filas juntas y tendrás una ventana. Esto significa que la GUI se define como una serie de Listas, una forma pitónica de ver las cosas.
Diseccionemos este pequeño programa
import PySimpleGUI as sg
layout = [[sg.Text('Rename files or folders')],
[sg.Text('Source for Folders', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Submit(), sg.Cancel()]]
window = sg.Window('Rename Files or Folders', layout)
event, values = window.read()
window.close()
folder_path, file_path = values[0], values[1] # get the data from the values dictionary
print(folder_path, file_path)
Acordemos que la ventana tiene 4 filas.
La
primera fila solo tiene texto
‘Rename
files or folders
’
La
segunda fila tiene 3 elementos. Primero el texto
‘Source
for Folders
’
,
luego un campo de ‘submit’,
luego un botón de ‘browse’.
Ahora veamos cómo esas 2 filas y las otras dos filas del código Python:
layout = [[sg.Text('Rename files or folders')],
[sg.Text('Source for Folders', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Submit(), sg.Cancel()]]
¿Ves cómo el código fuente refleja el diseño? Simplemente haga listas para cada fila, luego envíe esa tabla a PySimpleGUI para mostrar y obtener valores.
¿Y qué hay de esos valores de retorno? La mayoría de las personas simplemente quieren mostrar una ventana, obtener los valores de entrada y hacer algo con ellos. Entonces, ¿por qué dividir el código en devoluciones de llamada de botones, etc., cuando simplemente quiero que me den los valores de entrada de mi ventana?
Para valores de retorno, la ventana se escanea de arriba a abajo, de izquierda a derecha. Cada campo que es un campo de entrada ocupará un lugar en los valores de retorno.
En nuestra ventana de ejemplo, hay 2 campos, por lo que los valores de retorno de esta ventana serán un diccionario con 2 valores. Recuerde, si no especifica un key
al crear un elemento, se creará uno para usted. Son ints que comienzan con 0. En este ejemplo, tenemos 2 elementos de entrada. Serán direccionables como valores [0] y valores [1]
event, values = window.read ()
ruta_carpeta, ruta_archivo = valores [ 0 ], valores [ 1 ]
En
una declaración, ambos mostramos la ventana y leemos las entradas
del usuario. En la siguiente línea de código, el diccionario
de
valores de retorno se divide en variables individuales ‘folder_path
’
y
‘file_path’
.
¿No es esto lo que quiere un programador de Python que busca una GUI? Algo fácil de trabajar para obtener los valores y pasar al resto del programa, donde se está llevando a cabo la acción real. ¿Por qué escribir páginas de código GUI cuando se puede lograr el mismo diseño con PySimpleGUI en 3 o 4 líneas de código? 4 líneas o 40? La mayoría elegiría 4.
Valores de retorno
Hay
2 valores de retorno de una llamada a Window.read()
,
una event
que
provocó el Read
y values
una
lista o diccionario de valores. Si no hay elementos con claves en el
diseño, será una lista. Sin embargo, algunos elementos, como
algunos botones, tienen una clave añadida automáticamente. Es
mejor usar claves
en todos sus elementos de tipo de entrada.
Dos valores de retorno
Todas las llamadas de lectura de ventana devuelven 2 valores. Por convención se escribe una declaración de lectura:
evento, valores = window.read ()
No tiene que escribir sus lecturas de esta manera. Puedes nombrar tus variables como quieras. Pero si desea codificarlos de una manera que otros programadores que usan PySimpleGUI están acostumbrados, entonces use esta declaración.
Eventos
El
primer parámetro event
describe
por
qué se completó
la lectura. Los eventos son uno de estos:
Para todas las ventanas:
- Clic de botón
- Ventana cerrada con X
Para Windows que los ha habilitado específicamente:
- Pulsación de tecla del teclado
- Rueda del mouse arriba / abajo
- Elemento de menú seleccionado
- Un elemento cambiado (control deslizante, ruleta, etc.)
- Se hizo clic en un elemento de la lista
- Se presionó la tecla de retorno en el elemento de entrada
- Tiempo de espera esperando evento
- Se hizo clic en el texto.
- Elemento de cuadro combinado elegido
- Fila de tabla seleccionada
- etc.
La mayoría de las veces el evento será un clic de botón o la ventana se cerró.Los otros tipos de eventos específicos del Elemento suceden cuando configura enable_events=True
cuando crea el Elemento.
Evento de ventana cerrada
Otra convención a seguir es la comprobación de ventanas cerradas con una X. Este es un evento de importancia crítica para atrapar . Si no verifica esto e intenta usar la ventana, su programa se bloqueará. Verifique si hay una ventana cerrada y salga de su programa con gracia. Tus usuarios te agradarán por ello.
Cierra las ventanas cuando hayas terminado con ellas, aunque salir del programa también las cerrará. tkinter puede generar un error / advertencia a veces si no cierra la ventana. Para otros puertos, como PySimpleGUIWeb, no cerrar la ventana potencialmente hará que su programa continúe ejecutándose en segundo plano.
Para verificar si hay una ventana cerrada, use esta línea de código:
if event is None:
Poniendo todo junto terminamos con un «bucle de eventos» que se parece a esto:
while True:
event, values = window.read()
if event is None:
break
window.Close()
Esta declaración if es igual a:
if event is None or event == 'Exit':
break
En lugar de 'Exit
'
usa el nombre / clave del botón que desea salir de la ventana (Cancelar, Salir, etc.)
Eventos de clic de botón
Por defecto, los botones siempre devolverán un evento de clic, o en el caso de botones en tiempo real, un evento de botón pulsado. No tiene que hacer nada para habilitar los clics en los botones. Para deshabilitar los eventos, deshabilite el botón utilizando su método de Actualización.
Puede
habilitar un evento adicional «Botón modificado»
configurando enable_events=True
en
la llamada del Botón. Estos eventos se activan cuando algo «escribe»
en un botón, generalmente
porque
el botón aparece como «objetivo» en otro botón.
El valor del botón de una llamada de lectura será uno de los 2 valores:
- El texto del botón: predeterminado
- La tecla del botón: si se especifica una tecla
Si un botón tiene una clave establecida cuando se creó, se devolverá esa clave, independientemente del texto que se muestre en el botón. Si no se establece ninguna clave, se devuelve el texto del botón.
Si no se hizo clic en ningún botón, pero la ventana volvió de todos modos, el valor del evento es la clave que causó la generación del evento. Por ejemplo, enable_events
se establece en un Input
Element y alguien escribe un personaje en eseInput
, entonces el evento será la clave del cuadro de entrada.
None se devuelve cuando el usuario hace clic en la X para cerrar una ventana.
Si su ventana tiene un bucle de eventos donde se lee una y otra vez, recuerde darle a su usuario un «out». Siempre debe verificar el valor Ninguno y es una buena práctica proporcionar un botón Salir de algún tipo. Así, los patrones de diseño a menudo se parecen a este bucle de eventos:
while True:
event, values = window.read()
if event is None or event == 'Quit':
break
En realidad, la «versión Pythonic» más se usa en la mayoría de los programas de demostración y ejemplos.Hacen exactamente lo mismo.
while True:
event, values = window.read()
if event in (None, 'Quit'):
break
Eventos de elementos
Algunos
elementos son capaces de generar eventos cuando algo les sucede. Por
ejemplo, cuando se mueve un control deslizante, o se hace clic en el
elemento de la lista o se hace clic en la fila de la tabla. Estos
eventos no están habilitados por defecto. Para habilitar eventos
para un Elemento, configure el parámetro enable_events=True
.
Esto es lo mismo que el click_submits
parámetro anterior.
Encontrará el click_submits
parámetro
todavía en la definición de la función. Puedes seguir usándolo.
Son el mismo escenario. Se utiliza un ‘o’ de los dos valores. En el
futuro, se eliminarán click_submits, así que migre su código para
usar enable_events
.
Name | events |
---|---|
InputText | any change |
Combo | item chosen |
Listbox | selection changed |
Radio | selection changed |
Checkbox | selection changed |
Spinner | new item selected |
Multiline | any change |
Text | clicked |
Status Bar | clicked |
Graph | clicked |
Graph | dragged |
Graph | drag ended (mouse up) |
TabGroup | tab clicked |
Slider | slider moved |
Table | row selected |
Tree | node selected |
ButtonMenu | menu item chosen |
Right click menu | menu item chosen |
Elemento del menú de la barra de menú elegido para los menús de la barra de menú y los menús de menú de botones
Recibirá la clave para MenuBar y ButtonMenu. Use esa tecla para leer el valor en el diccionario de valores de retorno. El valor mostrado será el texto completo más la tecla para el elemento de menú elegido. Recuerde que puede poner claves en los elementos del menú. Obtendrá el texto y la clave juntos como lo definió en la definición del menú.
Elemento de menú de clic derecho elegido
A diferencia de los menús de barra de menú y botón, recibirá directamente el texto del elemento del menú y su valor clave. No hará una búsqueda en el diccionario para obtener el valor. Es el código de evento devuelto por WindowRead ().
Windows: teclado, rueda de desplazamiento del mouse
Windows es capaz de devolver eventos de teclado. Estos se devuelven como un solo carácter o una cadena si es una clave especial. Experimento es todo lo que puedo decir. Los eventos de la rueda de desplazamiento del mouse también son cadenas. Ponga una impresión en su código para ver qué devuelve.
Tiempos de espera
Si establece un parámetro de tiempo de espera en su lectura, se devolverá el sistema TIMEOUT_KEY. Si especificó su propia clave de tiempo de espera en la llamada de Lectura, ese valor será el que se devuelva.
La variable values: valores de retorno como una lista
El segundo parámetro de una llamada de lectura es una lista o un diccionario de los campos de entrada en la ventana.
Por defecto, los valores de retorno son una lista de valores, una entrada para cada campo de entrada, pero para todas las ventanas, excepto la más simple, los valores de retorno serán un diccionario. Esto se debe a que es probable que use una ‘clave’ en su diseño. Cuando lo hace, obliga a los valores de retorno a ser un diccionario.
Cada uno de los elementos que son elementos de entrada tendrá un valor en la lista de valores de retorno. Si sabe con certeza que los valores se devolverán como una lista, entonces podría ser inteligente y descomprimir directamente en variables.
event, (filename, folder1, folder2, should_overwrite) = sg.Window('My title', window_rows).Read()
O, más comúnmente, puede desempaquetar los resultados de devolución por separado. Este es el método preferido porque funciona tanto para valores de retorno de lista como de diccionario.
event, values = sg.Window('My title', window_rows).Read()
event, value_list = window.read()
value1 = value_list[0]
value2 = value_list[1]
Sin
embargo, este método no es bueno cuando tiene muchos campos de
entrada. Si inserta un nuevo elemento en su ventana, entonces tendrá
que barajar sus desempaques, modificando cada una de las
declaraciones para hacer referencia value_list[x]
.
El método más común es solicitar que sus valores se devuelvan como un diccionario colocando claves en los elementos «importantes» (aquellos de los que desea obtener valores y con los que desea interactuar)
El bucle de eventos / funciones de devolución de llamada
Todas las GUI tienen una cosa en común, un «bucle de eventos». Por lo general, el marco de la GUI ejecuta el bucle de eventos por usted, pero a veces desea un mayor control y ejecutará su propio bucle de eventos. A menudo escucha el término bucle de eventos cuando habla de sistemas embebidos o en una Raspberry Pi.
Con PySimpleGUI, si su ventana permanecerá abierta después de hacer clic en los botones, su código tendrá un bucle de eventos. Si su programa muestra una sola ventana «de una sola vez», recopila los datos y luego no tiene otra interacción GUI, entonces no necesita un ciclo de eventos.
Los bucles de eventos no tienen nada de misterioso … son bucles en los que usted se encarga … espere … eventos . Los eventos son cosas como clics de botones, pulsaciones de teclas, rueda de desplazamiento del mouse hacia arriba / abajo.
Este pequeño programa tiene un típico bucle de eventos PySimpleGUI.
La anatomía de un bucle de evento PySimpleGUI es la siguiente, en general .
-
La
parte real del «bucle» es un
while True
- «read» cualquier valor y evento de entrada que tenga la ventana
- Verifique si la ventana estaba cerrada o si el usuario desea salir
- Una serie de condiciones y declaraciones
Es sencillo crear una interfaz gráfica en Python.
Una interfaz grafica en una linea de codigo?
import PySimpleGUI as sg
event, values = sg.Window('Get filename example', [[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]).Read()
Veamos una interfaz con mas elementos y un poco mas de codigo:
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
class interfaz:
def __init__(self):
sg.ChangeLookAndFeel('LightGreen')
layout = [[sg.Text('Inicio de Sesion', size=(40, 1), justification='center')],
[sg.Text(text='Inicio de Sesion', justification='center')],
[sg.Text(text='Usuario')],
[sg.InputText()],
[sg.Text('Contraseña')],
[sg.InputText()],
[sg.Button('Iniciar Sesion', key='validar'), sg.Button('Cancelar', key = 'cancelar')]
]
self.window = sg.Window('Inicio de Sesion', location=(800, 400))
self.window.Layout(layout).Finalize()
while True:
event, values = self.window.Read()
if event == 'Exit' or event is None:
sys.exit()
break
if event == 'validar':
self.validar(values[0], values[1])
if event == 'cancelar':
sys.exit()
def validar(self, usuario, contraseña):
if usuario == 'usuario' and contraseña == 'contraseña':
sg.Popup('Usuario Validado')
else:
sg.Popup('Usuario Incorrecto')
inter = interfaz()
En esta interfaz gráfica colocamos varios elementos como lo son, cuadros de texto, etiquetas y botones.
Importamos las librerias
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
Creamos una clase llamada interfaz, y en su init definimos los elementos de la interfaz grafica:
class interfaz:
def __init__(self):
sg.ChangeLookAndFeel('LightGreen')
layout = [[sg.Text('Inicio de Sesion', size=(40, 1), justification='center')],
[sg.Text(text='Inicio de Sesion', justification='center')],
[sg.Text(text='Usuario')],
[sg.InputText()],
[sg.Text('Contraseña')],
[sg.InputText()],
[sg.Button('Iniciar Sesion', key='validar'), sg.Button('Cancelar', key = 'cancelar')]
]
self.window = sg.Window('Inicio de Sesion', location=(800, 400))
self.window.Layout(layout).Finalize()
y añadimos un ciclo que hara que constantemente estemos leyendo y actuualizando los valores en la interfaz grafica
while True:
event, values = self.window.Read()
if event == 'Exit' or event is None:
sys.exit()
break
if event == 'validar':
self.validar(values[0], values[1])
if event == 'cancelar':
sys.exit()
Ademas de definir un metodo para la validacion del usuario
def validar(self, usuario, contraseña):
if usuario == 'usuario' and contraseña == 'contraseña':
sg.Popup('Usuario Validado')
else:
sg.Popup('Usuario Incorrecto')
De modo que la ejecucion queda asi:
En los ejemplos he usado popup’s, de modo quue listare los diferentes popup’s que estan disponibles
- Popup
- PopupOk
- PopupYesNo
- PopupCancel
- PopupOkCancel
- PopupError
- PopupTimed, PopupAutoClose
- PopupNoWait, PopupNonBlocking
y se ejecutan:
sg.Popup('Popup') # Shows OK button
sg.PopupOk('PopupOk') # Shows OK button
sg.PopupYesNo('PopupYesNo') # Shows Yes and No buttons
sg.PopupCancel('PopupCancel') # Shows Cancelled button
sg.PopupOKCancel('PopupOKCancel') # Shows OK and Cancel buttons
sg.PopupError('PopupError') #
sg.PopupTimed('PopupTimed') # Automatically closes
sg.PopupAutoClose('PopupAutoClose') # Same as PopupTimed
Finalmente mostramos una interfaz gráfica que contiene todos los objetos gráficos para que realices tus pruebas:
import PySimpleGUI as sg
sg.ChangeLookAndFeel('GreenTan')
# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']],
['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
['&Help', '&About...'], ]
# ------ Column Definition ------ #
column1 = [[sg.Text('Column 1', background_color='lightblue', justification='center', size=(10, 1))],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]]
layout = [
[sg.Menu(menu_def, tearoff=True)],
[sg.Text('(Almost) All widgets in one Window!', size=(30, 1), justification='center', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)],
[sg.Text('Here is some text.... and a place to enter text')],
[sg.InputText('This is my text')],
[sg.Frame(layout=[
[sg.Checkbox('Checkbox', size=(10,1)), sg.Checkbox('My second checkbox!', default=True)],
[sg.Radio('My first Radio! ', "RADIO1", default=True, size=(10,1)), sg.Radio('My second Radio!', "RADIO1")]], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags')],
[sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)),
sg.Multiline(default_text='A second multi-line', size=(35, 3))],
[sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)),
sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)],
[sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))],
[sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)),
sg.Frame('Labelled Group',[[
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, tick_interval=25),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10),
sg.Column(column1, background_color='lightblue')]])],
[sg.Text('_' * 80)],
[sg.Text('Choose A Folder', size=(35, 1))],
[sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'),
sg.InputText('Default Folder'), sg.FolderBrowse()],
[sg.Submit(tooltip='Click to submit this form'), sg.Cancel()]]
window = sg.Window('Everything bagel', layout, default_element_size=(40, 1), grab_anywhere=False)
event, values = window.read()
sg.Popup('Title',
'The results of the window.',
'The button clicked was "{}"'.format(event),
'The values are', values)
Si deseas que hagamos un tutorial en especifico para un ejemplo, dejalo en los comentarios.
excelente!…cómo sería la conexión de la información que recoge ese formulario con, no sé, mysql server por ejemplo…
Muy bueno, en la lía del comentario anterior, podrías hacer un tutorial-ejemplo de un crud contra sqlserver ..??
necesito ayuda para hacer una interface que pida dos datos nombre yedad luego que la edad 18 entre 70 puedan votar si es mor de 18 no puede votar ni tampoco si es mayor 70 y luego preguntar si reguistro cedula si no no puede votar
Hola buenas tardes, podrias entre los objetos graficos hacer un ejemplo de un pageframe y un grid? desde ya muchas gracias!!! Esto lo usé en visual fox!!!
Traceback (most recent call last):
File «interfaz1.py», line 8, in
class interfaz:
File «interfaz1.py», line 29, in interfaz
event, values = self.window.Read()
NameError: name ‘self’ is not defined
tengo este error
Excelente, muchas gracias!