<<

U5 Interfaz gráfica

Unidad 5 Interfaz Gráfica de Usuarios (GUI)

Informática III – ISM – UNL 2018

Motivación

 Durante todo el cursado los programas que hemos realizado se utilizan mediante ingreso de texto por teclado en una consola (es decir, un interprete de comandos).  Esto hace poco “amigable” e interactivo a nuestros desarrollos, más aún que actualmente tenemos tantos dispositivos con posibilidades gráficas como laptops, celulares, tablets, etc.  Por lo tanto daremos una introducción a la programación de interfaces gráficas, usando en Python el módulo wxPython.

GUI para Python

 GUI proviene de Graphical User Interface, que podríamos traducir a “Interfaz Gráfica de Usuario” en español, es decir, una forma de programar que nos permita desarrollar usando elementos gráficos estándar como ventanas, menúes, botones, grillas, etc.

 Cada lenguaje tiene sus propios GUIs, en el caso de Python podemos nombrar a Tkinter (nativo en Python) wxPython, , GTK, entre los más conocidos.  En este cursado aprenderemos wxPython.

wxPython

 wxPython es un módulo para Python de la librería wxWidgets (escrita para C++) creado en 1996.  El principal desarrollador de wxPython es Robin Dunn, quien en 1995 necesitaba mostrar un desarrollo gráfico en una máquina Unix (HP-UX) , pero su jefe también quería mostrarlo en Windows 3.1.  wxPython es cross platform, es decir, puede usarse el mismo código para desarrollar en Windows, Mac y Unix.  Nota: una guía de instalación de wxPython en

Thonny puede encontrarse en este link. Ejemplo de wxPython

 Veamos el siguiente ejemplo:

Ejemplo de wxPython

 Veamos el siguiente ejemplo:

Importar el módulo wxPython

 Esto importa el módulo que tiene todas las funciones de wxPython.  Todas las funciones de wxPython que usemos de ahora en adelante deberán comenzar con wx.

Ejemplo de wxPython

 Veamos el siguiente ejemplo:

Se crea la aplicación gráfica

 Si o si, todas las aplicaciones que desarrollemos de ahora en adelante con wxPython deben tener creado un objeto wx.App() que representa la aplicación gráfica.

Ejemplo de wxPython

 Veamos el siguiente ejemplo:

Se crea la ventana gráfica (wx.Frame) y se le da un título

 wx.Frame es la ventana gráfica, un tipo de “contenedor” (container) ya que es el objeto base que contendrá a los demás objetos (botones, menués, grillas, etc.).  En otras palabras un objeto Frame es el “” (parent) de los otros objetos que crearemos.  En este caso, el parent de frame no existe (es None) ya que es la

ventana principal (o sea, no depende de nadie para inicializarse). Ejemplo de wxPython

 Veamos el siguiente ejemplo:

Se muestra la ventana

 Al objeto frame debemos mostrarlo en pantalla usando el método Show()  Si no lo hacemos, al ejecutarlo no veremos la ventana, aunque esté creada correctamente.

Ejemplo de wxPython

 Veamos el siguiente ejemplo:

Inicia la ejecución de la aplicación gráfica

 Finalmente ejecutamos un bucle infinito (si! y antes se enseñó que no debieran existir…).  MainLoop (o “bucle principal”) nos permite ejecutar la aplicación hasta que la misma se cierre  Mientras tanto, permite capturar todos los eventos de la ventana, como por ejemplo cuando se hace clic en el botón cerrar.

El objeto wx.Frame

 Como vimos en el ejemplo, Frame es el objeto que representa una ventana y forma parte de los contenedores.  wx.Frame posee una barra de título, bordes y un área central contenedora. Barra de titulo

Borde Área contenedora

El objeto wx.Frame

 Tamaño de la ventana: Se puede aplicar un tamaño específico de ventana mediante el método SetSize(ancho,alto), donde alto y ancho son los valores en píxeles. Por defecto el tamaño es 400x250  Posición de la ventana: También se puede configurar en qué posición de nuestra pantalla posicionar inicialmente la ventana con SetPosition((x,y)). Donde x e y son las posiciones de la esquina superior izquierda de la ventana. Por defecto (0,0).

El objeto wx.Frame

 Posición de la ventana: frame.SetPosition((0,0))

frame.SetPosition((500,0))

- Centrado en pantalla: frame.Centre()

Crear objetos de wx.Frame

 Usar una variable de tipo wx.Frame nos permite hacer ventanas y aplicaciones rápidamente, pero necesitamos cosas más complejas, como eventos, por ejemplo.  Para eso debemos crear nuestra propia clase ventana, extendiendo de la clase wx.Frame: import wx class MiVentana(wx.Frame): def __init__(self,parent,id,name): wx.Frame.__init__(self,parent,id,name)

Crear objetos de wx.Frame

 class es una palabra reservada que nos permite crear una ventana customizada, pero usando las propiedades genéricas de wx.Frame.  Cuando se crea una nueva ventana se ejecuta automáticamente el método __init__() por eso lo escribimos ejecutando el método __init__() de la clase (wx.Frame).  Una vez que definimos MiVentana, decimos que esta clase es “hija” de la clase wx.Frame.  Análogamente decimos que wx.Frame es “padre” de MiVentana.

Crear objetos de wx.Frame

 Dentro de la clase MiVentana el objeto self sirve para referenciar las propiedades de la ventana misma (self).  Por ejemplo, si queremos cambiar el tamaño, hacemos: self.SetSize(300,500)  Además, cada función creada dentro de la clases MiVentana, deberá llevar como primer parámetro la palabra self: class MiVentana(wx.Frame): … def CargarTitulo(self,titulo): self.SetTitle(titulo)

Crear objetos de wx.Frame

 Este es un ejemplo de una clase derivada de wx.Frame

Menú principal

 El menú principal de una ventana es el que vemos en la barra de título.  wxPython nos permite crear esta barra de menú mediante el componente wx.MenuBar.  Cada menú dentro de la barra puede crearse mediante wx.Menu.  Y los items de cada menú mediante wx.MenuItem.  Luego de creados cada item debe ser agregado a su correspondiente menú, y cada menú a la barra.  Veamos un ejemplo:

Menú principal

Menú principal

wxFormBuilder

 Programar ventanas desde el código si bien permite mayor control, hace más difícil de diseñar un programa con ventanas.  Para diseñar visualmente las ventanas usaremos un software que nos permitirá crear las ventanas y luego exportar el código generado a nuestra IDE (Thonny).  Este tipo de software se denominan RAD por Rapid Application Development (Desarrollo Rápido de Aplicaciones).  Este software es wxFormBuilder (link) cuyo tutorial para instalar en su máquina pueden encontrarlo en el PDF de instalación de wxPython y Pyo.  En este cursado cubriremos el uso de wxFormBuilder, pero también se pueden usar otras alternativas como wxGlade o

DialogBlocks (free trial). wxFormBuilder

Selección de componentes

Componentes del proyecto

Visualización gráfica/ Visualización código

Propiedades y eventos de los componentes Ejemplo de uso de wxFormBuilder

 A continuación se mostrarán los pasos necesarios para hacer una aplicación con wxFormBuilder y wxPython.  Vamos a generar una ventana con un botón y al hacer clic en el mismo, se mostrará un mensaje  La salida debería ser la siguiente:

Ejemplo de uso de wxFormBuilder

El primer paso es crear el Proyecto

Se ingresa un nombre en “name” y en “path” se hace clic en […] para elegir donde se guarda en la PC

Ejemplo de uso de wxFormBuilder

(1) Ahora agregaremos wx.Frame a nuestro (2) En Component Palette proyecto Elegimos la pestaña “Forms” y luego “Frame”

Le damos el nombre (“name”), que será el nombre de variable y el titulo (“title”)

Ejemplo de uso de wxFormBuilder  La Paleta de Componentes (“Component Palette”) permite seleccionar los componentes visuales para hacer una aplicación con wxPython

 Cada pestaña es una categoría:  Common (Comunes) tiene los componentes más usados en general, botones, menú de selección, texto estático, checks, imágenes, etc.  Additional (adicionales) tiene componentes menos usados, pero útiles como buscador de archivos, colores, hiperlinks.  Data (datos) posee los componentes para mostrar información, como grillas. Ejemplo de uso de wxFormBuilder

 Cada pestaña es una categoría:  Containers (contenedores) son los componentes que permiten tener otros componentes, como paneles y separadores.  Menu/Toolbar (menú y barra de herramientas) como los que vimos, pero permite otras configuraciones de menú con botones e imágenes.  Layout: permiten ordenar automáticamente los componentes mediante los “sizers”.

 Forms: frames y ventanas de diálogos. Ejemplo de uso de wxFormBuilder  Una vez creada la Ventana tenemos que posicionar un botón para hacer clic.  Para ese paso vamos a la pestaña “Common” y seleccionamos “wxButton”.  Probablemente veamos un mensaje de error como este:

 El mensaje nos indica que tenemos que agregar un “sizer” para que el objeto creado se posicione correctamente en la ventana.

Ejemplo de uso de wxFormBuilder  Podemos encontrar los sizers en la pestaña “Layout”, donde veremos las siguientes opciones:

 wxBoxSizer es el que más se usa y nos permite elegir ordenar los componentes dentro de la ventana en sentido horizontal o vertical.  wxGridSizer permite ordenar los componentes en una grilla de filas y columnas de igual tamaño.  wxFlexGridSizer es igual al wxGridSizer, pero las columnas o filas pueden variar de tamaño.

 wxGridBagSizer similar a wxGridSizer , pero permite cambiar el tamaño por celdas. Ejemplo de uso de wxFormBuilder

(1) Agregamos el sizer (2) En Component Palette A la ventana Elegimos la pestaña “Layout” y luego “wxBoxSizer”

(3) Le damos el nombre (“name”) y la orientación “orient” wxVERTICAL, de manera que los items ubiquen uno debajo del otro.

Ejemplo de uso de wxFormBuilder

(1) dentro del sizer (2) En Component Palette ubicamos el botón elegimos la pestaña “Common” y luego “wxButton”

(3) Le damos el nombre (“name”) y un mensaje para mostrar en la opción “label”. Por ejemplo, “Click me!”

Ejemplo de uso de wxFormBuilder

 El siguiente paso es situar un componente de texto estático (wxStaticText) dentro de la ventana.  El mensaje estará vacío cuando la ventana se abra, y mostrará un mensaje cuando hagamos click en el botón anteriormente creado.

 Para eso elegiremos en la pestaña Common el componenete wxStaticText y lo colocaremos en el wxBoxSizer antes creado, así aparecerá debajo del botón.

Ejemplo de uso de wxFormBuilder

(1) dentro del sizer (2) En Component Palette ubicamos el texto elegimos la pestaña “Common” y luego “wxStaticText”

(3) Le damos el nombre texto e iniciamos el “label” con una cadena vacía

Ejemplo de uso de wxFormBuilder

 Una vez creada la ventana, podemos “exportar” el código en Python y usarlo en Thonny para ejecutar nuestro código.  Para ello debemos ir a la pestaña “Python” de el menú “Editor”:

 En esa pestaña veremos la ventana creada pero en formato de líneas de código.  Copiaremos ese código y lo pasaremos a una aplicación de Thonny.  Cada vez que modifiquemos algo en wxFormBuilder deberemos copiarlo a Thonny para evitar que no se muestren los últimos cambios. Ejemplo de uso de wxFormBuilder

Ejemplo de uso de wxFormBuilder

Fuera de MiFrame creamos la aplicación Ejemplo de uso de wxFormBuilder  Ahora se puede ejecutar el código, y deberíamos tener una ventana como la siguiente.

 Pero al hacer clic en el botón no pasa nada. Debemos setear los “eventos” del botón.

 Los eventos son parte muy importante en una aplicación visual, ya que permite interactuar al usuario con sus componentes (ya veremos esto en detenimiento).

 Para generar un evento en un componente debemos ir a la pestaña “Events” en “Object Properties” (Propiedades de Objetos) Ejemplo de uso de wxFormBuilder  El evento que queremos que se ejecute debe suceder cuando el botón sea clickeado, que es “OnButtonClick”

 Allí escribimos el nombre de la función que va a ejecutarse cuando se haga click. En este caso la llamaremos “escribirMensaje”.

 Esa función automáticamente se escribirá en la pestaña de Python junto con el código de la clase MiFrame: def escribirMensaje( self, event ): event.Skip()

 Y también, dentro de la clase MiFrame se “conectará” el componente del botón al evento con el método Bind que recibe como parámetros el tipo de evento (wx.EVT_BUTTON) y el nombre de la función que ejecutará el evento: self.boton.Bind( wx.EVT_BUTTON, self.escribirMensaje ) Ejemplo de uso de wxFormBuilder

 Dentro de la función escribirMensaje pondremos lo que deseamos que se ejecute, en nuestro caso que la propiedad “label” del componente texto muestre un mensaje.

 Para ello usamos el método SetLabel() de wx.StaticText def escribirMensaje( self, event ): self.texto.SetLabel("Hola, wxMundo!")

 Notemos que self representa al objeto padre (MiFrame), texto es el componente wx.StaticText y el mensaje es “Hola, wxMundo”.

 También notemos que como la propiedad se llama “label” el método para cambiar la propiedad siempre tiene el prefijo “Set”. Lo mismo sucede con otras propiedades (SetStyle, SetName, etc.)

 Para leer el contenido de una propiedad se usa el prefijo “Get”. Ejemplo de uso de wxFormBuilder

Ejemplo de uso de wxFormBuilder

Conexión del botón al evento (wx.EVT_BUTTON) y a la función que se ejecutará al dar click

Ejemplo de uso de wxFormBuilder

Ejecución del evento, cambiando el valor de la propiedad label de texto.

Ejemplo de uso de wxFormBuilder Antes y después de hacer click:

wx.BoxSizer

 ¿Qué pasaría si a la propiedad orient del sizer del ejemplo anterior la cambiamos a wx.HORIZONTAL?  Veríamos algo como esto:

 El container wx.BoxSizer permite manejar dos tipos de alineaciones, verticales u horizontales. wx.BoxSizer

 En el código veremos que un wx.BoxSizer se define de la siguiente manera: box = wx.BoxSizer(wx.HORIZONTAL)

 Si cambiamos a orientación vertical: box = wx.BoxSizer(wx.VERTICAL)

wx.BoxSizer  Para agregar elementos a un wx.BoxSizer se utiliza el método Add, por ejemplo: box.Add(componente,proportion,flag,border)

 Los parámetros proportion, flag y border de cada componente se puede ver seleccionando el componente en wxFormBuilder y buscando en “Properties” los ítems “sizeritem” y “sizeritembase”:

Componente seleccionado

Componente seleccionado

wx.BoxSizer  Para agregar elementos a un wx.BoxSizer se utiliza el método Add, por ejemplo: box.Add(componente,proportion,flag,border)

 proportion es la opción para cambiar la proporción de tamaño a los distintos componentes que vamos agregando. Por defecto la proporción inicial es 0.

 En el siguiente ejemplo tenemos un wx.BoxSizer horizontal y agregamos 3 componentes con proporciones distintas. A mayor proporción más tamaño. box.Add(c1,0) box.Add(c2,1) box.Add(c3,2)

 En este caso el componente de proporción 2 tiene el doble que el de proporción 1. wx.BoxSizer  Para agregar elementos a un wx.BoxSizer se utiliza el método Add, por ejemplo: box.Add(componente,proportion,flag,border)

 flag nos permite habilitar los bordes (con tamaño border=5) en las distintas partes del componente, por ejemplo si a c1 le habilitamos el borde derecho: box.Add(c1,0,wx.RIGHT, 5)  Se puede habilitar el izquierdo junto con el derecho usando | : box.Add(c1,0,wx.RIGHT | wx.LEFT, 5)

 Si queremos borde en todas las direcciones: wx.ALL

wx.BoxSizer

 Muchas veces queremos tener configuraciones en las cuales los componentes en la misma ventana tengan distintas orientaciones, por ejemplo:

wx.BoxSizer

 Podemos combinar varios wx.BoxSizer’s dentro de otros

 En este ejemplo tenemos un sizerBase (en rojo) con orientación wx.HORIZONTAL que tiene 2 componentes que a su vez también son wx.BoxSizer (sizer1 y sizer2)

sizer1 sizer2 sizerBase

wx.BoxSizer

 sizer1 tiene orientación wx.VERTICAL, mientras que sizer2 tiene orientación wx.HORIZONTAL. Ambos están dentro de sizerBase.

 sizer1 tiene proporción=0 y sizer2 tiene proporción=1.

 A su vez V1, V2 y V3 son componentes de sizer1, y H1, H2 y H3 de sizer2

sizer1 sizer2

wx.BoxSizer

 El código y el proyecto wxFormBuilder de esa parte sería:

sizerBase = wx.BoxSizer(wx.HORIZONTAL)

sizer1 = wx.BoxSizer(wx.VERTICAL)

sizer2 = wx.BoxSizer(wx.HORIZONTAL)

# agregar componentes a sizer1

sizer1.Add(V1,2,wx.ALL)

sizer1.Add(V2,1,wx.ALL) sizer1.Add(V3,0,wx.ALL) # agregar componentes a sizer2

sizer2.Add(H1,0,wx.ALL)

sizer2.Add(H2,0,wx.ALL)

sizer2.Add(H3,2,wx.ALL)

# agregar sizer1 y sizer2 al sizerBase:

sizerBase.Add(sizer1, 0)

sizerBase.Add(sizer2, 1) Otros Sizers

 wx.GridSizer – Permite ordenar los componentes en una tabla bidimensional. Cada celda dentro de la tabla tiene el mismo tamaño: grilla = wx.GridSizer(filas, columnas, espacio_v, espacio_h) – En este caso filas y columnas es la cantidad de filas y columnas que usaremos, mientras que espacio_v y espacio_h son el espacio vertical y horizontal entre componentes, respectivamente. grilla = wx.GridSizer(4 , 3 , 5, 10) nos genera algo así:

Otros Sizers

 wx.GridSizer – El método Add también se usa para wx.GridSizer – En este caso se empieza agregando componentes por la fila y luego por columna. – Otro flag que podemos usar con Add es wx.EXPAND, que hace que el componente agregado tome toda la celda: grilla.Add(b00,0,wx.EXPAND) espacio_h=10 grilla.Add(b01,0,wx.EXPAND) grilla.Add(b10,0,wx.EXPAND) grilla.Add(b11,0,wx.EXPAND)

espacio_v=5 Otros Sizers

 wx.FlexGridSizer – Es similar a wx.GridSizer pero permite tener celdas con el mismo alto en una fila y con el mismo ancho en una columna, pero no necesariamente todas las celdas tienen el mismo alto y ancho. flex = wx.FlexGridSizer(filas,columnas,espacio_v,espacio_h) – Los métodos AddGrowableRows y AddGrowableCols permiten seleccionar qué filas y columnas podrán “agrandarse” si se aplica wx.EXPAND. flex.AddGrowableRow(0) flex.AddGrowableCol(2)

Otros Sizers

 wx.GridBagSizer – Es el más complejo de los sizers, permite posicionar un componente directamente en una posición pos=(x,y) de la grilla. – Además puede hacer que un componente ocupe varias filas y/o columnas (span). bag = wx.GridBagSizer(espacio_v,espacio_h) bag.Add(b00, pos=(0,0) , span=(1,2),wx.EXPAND) bag.Add(b11, pos=(1,1) )

Eventos

 La parte más importante de las apps GUI (además de la visualización) son los eventos. De hecho, decimos que una aplicación gráfica esta orientada a los eventos.

 Sin eventos, solo tenemos una “maqueta” de la aplicación.

 Los eventos nos permite interactuar mediante teclado, mouse, y otros tipos de periféricos con el entorno gráfico.  Existen muchos tipos de evento: – De teclado (al presionar tecla, al dejar de presionar) – De mouse (mover, hacer click, mantener presionado) – De wx.Frame (al cerrar una ventana, al abrir) – Aquellos que dependen del componente.

Eventos

 Evento de mouse: – wx.EVT_MOTION

Eventos

 Evento de mouse: – wx.EVT_MOTION – En la función MoverMouse es donde se definirá qué hacer cuando el mouse se mueve por encima del wx.Frame que hemos creado. – Notemos que la función recibe como parámetro a event. Esta variable es de tipo wx.MoveEvent y posee información del evento que la creo, por ejemplo para obtener la posición actual del mouse usamos el método GetPosition() que retorna la lista [x,y]. def MoverMouse(self,event): [x,y] = event.GetPosition() print(“(x,y)=(%d,%d)”%(x,y))

Eventos

 Evento de mouse: – Si ejecutamos ese código en la IDE Thonny tendremos que cada que vez que pasemos el mouse sobre el wx.Frame veremos en la salida de las coordenadas en píxeles.

– Con un wx.StaticText que modifique su label en el evento podríamos mejorar esto:

Eventos

 Conexión de eventos (Binding) – Detengamos un poco en la definición del wx.Frame que creamos, y en especial en el método Bind() – Bind nos permite conectar un componente a un evento y designar la función que se ejecutará cuando el evento se “dispare”: self.Bind(wx.EVT_MOTION,self.MoverMouse)

Eventos

 Conexión de eventos (Binding) – Detengamos un poco en la definición del wx.Frame que creamos, y en especial en el método Bind(). – Bind nos permite conectar un componente a un evento y designar la función que se ejecutará cuando el evento se “dispare”: self.Bind(wx.EVT_MOTION,self.MoverMouse)

Componente que dispara el evento, en este caso el mismo wx.Frame

Eventos

 Conexión de eventos (Binding) – Detengamos un poco en la definición del wx.Frame que creamos, y en especial en el método Bind(). – Bind nos permite conectar un componente a un evento y designar la función que se ejecutará cuando el evento se “dispare”: self.Bind(wx.EVT_MOTION,self.MoverMouse)

Tipo de evento que la función debe esperar que se ejecute

Eventos

 Conexión de eventos (Binding) – Detengamos un poco en la definición del wx.Frame que creamos, y en especial en el método Bind() – Bind nos permite conectar un componente a un evento y designar la función que se ejecutará cuando el evento se “dispare”: self.Bind(wx.EVT_MOTION,self.MoverMouse)

Función que se ejecutará cuando el evento se genere

Eventos

 Otros eventos de Mouse: – OnLeftDown (wx.EVT_LEFT_DOWN): al presionar con el botón izquierdo – OnLeftUp (wx.EVT_LEFT_UP): al dejar de presionar con el botón izquierdo – OnLeftDClick (wx.EVT_LEFT_DCLICK): al hacer doble click con el botón izquierdo. – OnRightDown (wx.EVT_RIGHT_DOWN): al presionar con el botón derecho – OnRightUp (wx.EVT_RIGHT_UP): al dejar de presionar con el botón derecho – OnMiddleDown (wx.EVT_MIDDLE_DOWN): al presionar con el botón del medio (rueda) – OnMiddleUp (wx.EVT_MIDDLE_UP): al dejar presionar con el botón del medio (rueda) – OnMouseWheel (wx.EVT_MOUSEWHEEL): al mover la rueda del mouse arriba/abajo. Eventos

 Eventos de menú: – Ya vimos como crear un menú, pero no hemos generado ningún evento cuando algún item es clickeado. – El evento que se dispara cuando se hace click en el menú es wx.EVT_MENU. – Cada ítem creado tiene asociado un ID único para identificarlo cuando un EVT_MENU se dispara, supongamos los siguientes items. – Lo primero que haremos es crear el menú a través de wxFormBuilder y asignarle un evento a cada ítem:

Eventos

 Eventos de menú: – Seleccionamos un ítem y vamos a Events, donde crearemos la función que maneje el evento de ese ítem:

Item elegido Función asociada al evento – Esto genera las funciones en el código que podemos ver haciendo click en la pestaña de Python:

Eventos

 Eventos de menú: – Dentro de cada función manejadora del evento (Event Handler) podemos programar qué queremos que suceda. – Un ejemplo sería probar de seleccionar “Cerrar” y que la ventana se cierre, para esto usamos el método Close() de wx.Frame: def OnCerrarItem( self, event ): self.Close()

– Pero muchas veces un usuario hace click equivocadamente y no quiere cerrar la ventana, por lo tanto es mejor mostrar una ventana que pregunte si se desea cerrar o no la ventana: una confirmación. – Para ello usaremos el componente wx.MessageDialog wx.MessageDialog(parent,mensaje,titulo,estilo) – Donde mensaje es lo que se desea “preguntar” al usuario, titulo de la ventana de diálogo y estilo es un grupo de flags que le dará formato a la ventana. Eventos

 wx.MessageDialog: – Veamos como quedaría la función que se ejecuta al clickear en Cerrar:

– Generamos el wx.MessageDialog con el identificador diag, la ventana mostrará la pregunta “¿Desea salir?” y utilizará la constante de estilo wx.YES_NO, es decir mostrará un botón para SI y otro para NO. – Luego se muestra el diálogo con el método ShowModal() que permite esperar la elección del usuario (SI o NO). – Luego se compara la respuesta ret con la constante wx.ID_YES que identifica la respuesta cuando el botón seleccionado fue SI. En ese caso se cierra la ventana.

Eventos

 wx.MessageDialog:

Mensaje

wx.ID_NO wx.ID_YES

Eventos

 Eventos de teclado: – Además del mouse, el otro gran dispositivo de entrada es el teclado y muchas veces queremos capturar los eventos de teclado para brindar alguna funcionalidad a nuestros programas. – Existen 3 eventos de teclado: • wx.EVT_KEY_DOWN (OnKeyDown) : se ejecuta cuando alguna tecla ha sido presionada • wx.EVT_KEY_UP (OnKeyUp): se ejecuta cuando alguna tecla ha sido levantada. • wx.EVT_CHAR (OnChar): se ejecuta al escribir algún carácter con teclado.

Eventos

 Eventos de teclado: – En este ejemplo, la presión de la tecla ESC hace que la ventana se cierre. – Lo primero es generar un wx.Panel porque los wx.Frame no pueden capturar eventos de teclado, solo sus componentes. – Luego en el evento OnKeyDown del panel asignamos la función CerrarVentana:

Eventos

 Eventos de teclado: – wx.EVT_KEY_DOWN se ejecuta cuando cualquier tecla esta presionada, pero ¿cómo sabemos qué tecla se presionó? ¿Cómo identificar ESC? – Esto lo hacemos con el método GetKeyCode() de event que retorna un número entero identificando el código ASCII de la tecla presionada (¿se acuerdan de la función ord()?) – Para identificar la tecla ESC podemos preguntar por el código 27 o, mejor aún, por la constante de wxPython que identifica la tecla ESC: wx.WXK_ESCAPE:

Eventos

 Códigos de teclas: – wx.WXK_RIGHT, wx.WXK_LEFT, wx.WXK_UP, wx.WXK_DOWN: teclas de cursor. – wx.WXK_F1 hasta wx.WXK_F12: teclas de funciones. – wx.WXK_NUMPAD0 hasta wx.WXK_NUMPAD9: teclas del teclado numérico – wx.WXK_BACK: tecla ← (o backspace) – wx.WXK_TAB: tabulación – wx.WXK_RETURN: tecla Enter – wx.WXK_SPACE: barra espaciadora – wx.WXK_DELETE: tecla suprimir (o delete) – wx.WXK_SHIFT, wx.WXK_ALT, wx.WXK_CONTROL (para Mac representa la tecla Command) – wx.WXK_RAW_CONTROL: en Mac, si la tecla Ctrl está mapeada con la tecla Command. – wx.WXK_NONE: ninguna tecla válida se ha presionado. Widgets

 Un “widget” puede ser traducido como un componente visual o herramienta para el uso en aplicaciones.

 En wxPython un widget es una herramienta sobre la cual se puede interactuar y generar eventos.

 Comencemos con wx.Button que representa un botón: boton = wx.Button(parent,id,mensaje)  parent es el “padre” que contiene al componente botón.

 mensaje es el valor que mostrará el botón.

 id, entero que sirve para identificar unívocamente el botón entre todos los componentes. Si se ingresa -1, wxPython se encargará de asignarle un id. boton = wx.Button(self,-1,”Cerrar Ventana”)

Widgets

 Evento de botón: – El evento que se dispara cuando se hace click en un componente wx.Button es wx.EVT_BUTTON, que se puede programar eligiendo el componente y en Events seleccionando OnButtonClick

– El valor del mensaje del botón puede ser cambiado con el método SetLabel boton.SetLabel(“Otro mensaje”)

Widgets

 wx.TextCtrl – Este control equivale a un componente de edición de texto básico. textCtrl = wx.TextCtrl(parent,id,valor) – valor es la cadena inicial que muestra el componente. Por defecto es una cadena vacía. – Los métodos más usados de TextCtrl son: • textCtrl.GetValue(): retorna una cadena con el valor actual. • textCtrl.SetValue(nuevo_texto): ingresa un nuevo valor al componente.

Widgets

 wx.TextCtrl – Veamos el siguiente ejemplo: un botón que, al presionarlo, muestra la suma del texto en 2 componentes TextCtrl:

Ingreso de la función que manejará el evento Selección del Componente que dispara el evento Widgets

 wx.TextCtrl – En la función CalcularSuma tenemos:

Widgets

 wx.TextCtrl – En la función CalcularSuma tenemos:

GetValue() retorna un str, por lo tanto el operador + no suma, concatena

Widgets

 wx.TextCtrl – En la función CalcularSuma tenemos:

Notar que SetLabel recibe Como parámetro un str.

Widgets

 wx.TextCtrl – Qué pasa si no ingresamos alguno de los valores en el TextCtrl?

– Tenemos error ya que int() espera algún valor. – Para solucionarlo podemos inicializar con 0 el TextCtrl o modificar el valor en la función CalcularSuma. Widgets

 wx.ComboBox – Un “combo box” es una lista desplegable de opciones de las cuales solo se puede seleccionar una. combo = wx.ComboBox(parent,id,choices=[ ]) – choices es una lista de cadenas con los ítems a mostrar. combo = wx.ComboBox(self,-1,choices=[“rock”,”jazz”,”R&B”])

Widgets

 wx.ComboBox – El evento principal sobre un ComboBox es al seleccionar una opción. Esto puede verse con el evento wx.EVT_COMBOBOX, seleccionando OnCombobox en wxFormBuilder:

– El método event.GetString() retorna la cadena seleccionada cuando se cambia de valor el wx.ComboBox: def CambiarTexto(self,event): tex = event.GetString() self.texto.SetLabel("Opción elegida: %s"%tex) Widgets

 wx.ComboBox – A veces es necesario conocer cuál es la posición del item elegido dentro de la lista, esto lo podemos observar con event.GetSelection() que retorna un entero donde 0 es el primer elemento de la lista. def CambiarTexto(self,event): tex = event.GetString() ind = event.GetSelection() self.texto.SetLabel("Opción elegida: %s en la posición %d"%(tex,ind))

Widgets

 Otros métodos de wx.ComboBox – combo.GetCount() retorna la cantidad de elementos del ComboBox. – combo.GetString(indice): retorna la cadena correspondiente a la opción en la posición indice. – combo.SetString(indice,texto): modifica el texto de la opción en la posición indice. Si indice no es válido, será un error.  Estilo de wx.ComboBox: – Se puede darle un estilo al ComboBox mediante el parámetro style. Los estilos pueden combinarse usando el carácter | – wx.CB_READONLY: configura al ComboBox en “solo lectura” es decir no puede ser editable. – wx.CB_SORT: ordena las opciones dentro del ComboBox alfabéticamente. combo = wx.ComboBox(self,-1,choices=[“Uno”,”Dos,”Tres”], style=wx.CB_SORT | wx.CB_READONLY)

Widgets

 Volvamos al ejemplo de la suma, pero ahora elegiremos qué tipo de operación se realizará entres 2 números, eligiendo la misma de un wx.ComboBox

Agregamos las opciones haciendo click en “choices”

Widgets

 Calcular ahora necesita determinar qué operación se eligió:

Convertimos a float para poder tomar mejores cálculos

Usamos GetSelection() Para obtener el indice de la opción elegida

Recordemos que en caso de resto y división el divisor debe ser distinto de 0.

Widgets

 Calcular ahora necesita determinar qué operación se eligió:

Widgets

 Notemos que puede haber algunos errores cuando ejecutamos el programa: – Si el divisor es cero, cuando hacemos Cociente o Resto – Si no se selecciona una operación (GetSelection() es -1) – Si los números ingresados son texto vacío.

 Para mitigar esto, en el código se agregaron sentencias return, pero deberíamos mostrar al usuario que el ingreso fue equivocado.  Antes se usaba print() pero ahora necesitamos algún mensaje visual. wx.MessageBox(mensaje,titulo,estilo)

 Al ejecutar MessageBox, se abre una ventana con un botón y un mensaje que nosotros podemos codificar, por ejemplo: wx.MessageBox(“Debe seleccionar operación”,”Error!”,wx.ICON_ERROR)

Widgets

 Uso de wx.MessageBox para mostrar errores:

Widgets

 Uso de wx.MessageBox para mostrar errores:

Widgets

 wx.CheckBox – Los CheckBox son componentes que nos permiten activar o desactivar opciones con un “check”. – Consiste de una caja para chequear y una etiqueta para determinar qué opción se activa o desactiva. – El evento que se dispara cuando se hace click en la caja es OnCheckBox, que genera un wx.EVT_CHECKBOX – El ejemplo que veremos permite habilitar la edición de un campo de texto o deshabilitarla mediante un CheckBox:

Widgets

 wx.CheckBox – El método que nos deja ver cuál es el estado del CheckBox es GetValue(), que retornará True si está “tildado” o False si no lo está. – Otra alternativa es usar el método IsChecked(), que es análogo al anterior. – El método que usaremos en el TextCtrl es Enable(habilitar), donde habilitar es un bool: si es True habilita la edición. – Notemos que al iniciar la app ponemos CheckBox está “tildado”, por lo tanto TextCtrl tendrá la edición habilitada inicialmente.

Widgets

 wx.RadioButton – Tiene un comportamiento similar a wx.CheckBox, pero la diferencia es que permite elegir una de varias opciones. – Si una opción es elegida, las otras automáticamente dejan de estar seleccionadas, así que también se puede decir que es parecido a wx.ComboBox. – En realidad para usar el RadioButton, los mismos deben ser agrupados en otro componente llamado wx.RadioBox, que contendrá todas las opciones mutuamente excluyentes entre si. – Luego yendo a choices del RadioBox podemos ingresar las opciones:

Widgets

 wx.RadioButton – Volvamos atrás al ejemplo de la calculadora, pero en lugar de opciones en un CheckBox, usaremos las mismas opciones en un RadioBox – Al igual que con CheckBox, el índice de la opción en la lista se obtiene con GetSelection()

Widgets

 wx.Slider – Es un control que permite mover un selector entre un valor mínimo y otro máximo mediante el mouse.

– El valor mínimo se configura en la opción minValue, el máximo en maxValue y el valor inicial en value. – Con “style” se puede elegir el estilo del Slider: wx.SL_LABELS muestra los valores máximo, mínimo y actual. wx.SL_VERTICAL cambia la dirección a vertical. wx.SL_INVERSE invierte el orden de los valores. Widgets

 wx.Slider – En este ejemplo se mueve el Slider y mediante el evento OnScroll se muestra en un wx.StaticText el valor actual del Slider.

Widgets

 wx.SpinCtrl – Un SpinCtrl es similar al Slider pero los valores se muestran en un TextCtrl y se incrementan y/o decrementan con un botones que ya vienen en el componente. – Como en Slider también se deben configurar los valores máximo, mínimo e inicial. – El evento que se dispara cuando se oprimen los botones es OnSpinCtrl

wx.Timer

 Este componente permite tener un contador de tiempo en nuestro código.

 wx.Timer ejecuta eventos en un tiempo determinado para que otros componentes de la aplicación puedan realizar tareas con una frecuencia o a un tiempo necesarios. timer = wx.Timer(parent,id)

 Un Timer se inicia (y reinicia) con el método Start(ms), donde ms es la cantidad de milisegundos (1 s = 1000 ms) y se detiene con Stop().

 En wxFormBuilder podemos encontrar el wx.Timer en la pestaña “Additional” con la forma de un reloj:

wx.Timer  Timer posee un solo evento, wx.EVT_TIMER cuyo manejador es OnTimer en wxFormBuilder.

 La función (en este caso ActualizarReloj) se ejecutará automáticamente cada ms milisegundos.

 El siguiente código lanza un Timer cuando se presiona ToggleButton y actualiza los segundos en el StaticText (period = 1000).

 Un ToggleButton es un botón que tiene dos estados, presionado (True) y no presionado (False), y GetValue() retorna esos valores cuando se activa el evento OnToggleButton.

wx.Timer  Timer posee un solo evento, wx.EVT_TIMER cuyo manejador es OnTimer en wxFormBuilder.

 La función (en este caso ActualizarReloj) se ejecutará automáticamente cada ms milisegundos.

 El siguiente código lanza un Timer cuando se presiona ToggleButton y actualiza los segundos en el StaticText (period = 1000).

 Un ToggleButton es un botón que tiene dos estados, presionado (True) y no presionado (False), y GetValue() retorna esos valores cuando se activa el evento OnToggleButton.

El módulo wx.grid

 Un Grid es una tabla similar a una matriz gráfica, como las usadas en planillas de cálculo.

 En wxFormBuilder puede encontrarse en la pestaña “Data”

 Veamos las partes de un wxGrid:

col_label grid_lines Columnas (cols)

Filas (rows)

row_label El módulo wx.grid  Realizaremos un ejemplo en el cual podremos mostrar en una grilla los valores de un archivo del mismo formato que la base de datos de materiales para reverberación

 Es decir, las columnas serán el nombre del material y 6 frecuencias. En total 7 columnas.

 Para abrir el archivo usaremos otra herramienta visual, el wx.FilePickerCtrl, del que ya hablaremos Como primer paso, pondremos cols=7 y rows=0 ya que aún no cargamos ningún valor.

 Luego pondremos los nombres de cada columna, iremos a col_label_values y haremos clic en […]

 En column_sizes podemos poner una lista de números separados por coma con el tamaño de las columnas en orden. Para este caso usaremos 120,60,60,60,60,60,60

 Como no queremos tener ninguna etiqueta de fila, usaremos row_label_size = 0 El módulo wx.grid  Esta es la maqueta inicial

 Ahora veremos qué sucede al elegir el archivo mediante el wx.FilePickerCtrl.

 Elegimos en wxFormBuilder ese componente y vamos a evento, donde el evento cuando se elige un archivo es OnFileChanged.

 Llamaremos al manejador de ese evento AbrirArchivo

El módulo wx.grid

 GetPath() del wx.FilePickerCtrl retorna el archivo elegido.

 Abrimos el archivo como siempre y recorremos por renglón.

 Se divide (split) por “;” lo cual nos da las columnas de cada fila.

 AppendRows() de la grilla agrega una fila (row) vacía a la misma

 GetNumberRows() retorna cuantas filas tiene la grilla en ese momento.

 SetCellValue(fila,columna,valor) coloca un valor en la fila y columna dada. El módulo wx.grid

 Notemos qué pasa si queremos abrir otro archivo:

 Se agrega al final del archivo anterior. Para solucionar, antes de abrir el nuevo archivo debemos borrar de la grilla los datos del anterior.

 Usaremos el método DeleteRows(desde,filas), donde desde es la primera fila desde la que se borrará y filas es la cantidad a borrar desde esa fila. El módulo wx.grid limpiarGrilla() elimina los valores de la grilla desde 0 a filas

Antes de cargar nada se debe “limpiar” la grilla

El módulo wx.grid  Agregaremos botones para eliminar y agregar coeficientes.  Cuando hagamos click en el botón “Eliminar Material” debemos borrar la fila seleccionada.  Antes debemos saber cuál fila es, debemos capturar el momento en el que se cambia de fila con el evento OnGridSelectCell, al cual le asignaremos la función CambiarFila  El evento ejecutado es un wx.grid.GridEvent que posee el método GetRow(), del cual se obtiene el índice de la fila seleccionada cada vez que cambiamos de celda.  Usaremos la variable filaSeleccionada para mantener el último valor de fila elegida.

El módulo wx.grid  Cuando eliminamos algo en alguna aplicación siempre es bueno preguntar antes.  Usemos el wx.MessageDialog para tener una confirmación de la eliminación del material

El módulo wx.grid  Habremos notado a esta altura que borrar o agregar un nuevo material no se ve reflejado en el archivo de texto.  Esto se debe a que aún no guardamos la información modificada.  Pongamos un nuevo botón, llamado guardar para guardar los cambios de la grilla en el archivo con la función GuardarDatos.

El módulo wx.grid  El último paso podría ser hacer más “amigable” nuestra app, usemos botones gráficos: wx.BitmapButton.  Estos botones nos permiten usar una imagen en cualquier formato para identificar la función del botón en lugar de una etiqueta

Integración wx y pyo  Con los componentes y eventos de wxPython podemos realizar cualquier operación sobre los módulos de Python.  pyo no es la excepción, y además cuenta con componentes visuales propios que iremos nombrando.  Veamos un ejemplo, un selector de señales y un visualizador de espectro y osciloscopio.

Integración wx y pyo  El osciloscopio es el componente pyo.PyoGuiScope.  El espectrómetro es pyo.PyoGuiSpectrum  Ninguno de esos controles están en wxFormBuilder, así que deberemos ingresarlos como CustomControl en la pestaña Additional:

 Y luego completar los campos de las propiedades del CustomControl de la siguiente manera (en este ejemplo para el PyoGuiSpectrum): – name: es el nombre que tendrá el control en nuestro programa. – class: es el nombre de la clase. – construction: es como se define cuando se inicializa el objeto.

Integración wx y pyo  Una vez creada la maqueta es hora de editar el archivo de python en Thonny.  Lo primero es crear el servidor de Pyo y los objetos Scope() y Spectrum(), le pasamos como input una señal senoidal, para inicializarlos correctamente. import pyo pyoServer = pyo.Server().boot() pyoSc = pyo.Scope(pyo.Sine()) pyoSp = pyo.Spectrum(pyo.Sine())

 Ahora agregaremos un evento llamado OnPlay al botón botonPlay que inicie la reproducción de la señal seleccionada en el wx.ChoiceBox que hemos llamado tipoSenyal.

 Notemos que para que el PyoGuiScope() funcione necesita que se lo asocie con un objeto tipo Scope(). Para eso usaremos el método setAnalyzer() de PyoGuiScope()

Integración wx y pyo

Obtenemos la señal elegida y asignamos a senyal

Si la señal no es de las elegidas, mostramos error

Si Server() no fue iniciado, lo hacemos

Cambiamos la señal de entrada de los analizadores a la señal elegida

El objeto visual ahora está relacionado con su correspondiente objeto de análisis Integración wx y pyo  Finalmente, programamos otros 2 eventos.  En el botón Stop (botonStop) agregamos el evento OnStop(), que detendrá la reproducción de la señal.  Y en el wx.Frame usamos el evento EVT_CLOSE para programar la función OnClose(). Notemos que en esa función detenemos el servidor de pyo y luego destruimos la ventana con el método Destroy() que es similar a Close() pero limpia mejor la memoria.

Integración wx y pyo

 Además de PyoGuiSpectrum y PyoGuiScope, pyo también tiene otros componentes gráficos:  PyoGuiSndView que permite conectar con un componente SndTable para visualizar contenido de un archivo de audio y seleccionar por partes

Integración wx y pyo

 Los componentes PyoGuiGrapher y PyoGuiMultiSliders pueden ser vistos en el siguiente ejemplo: https://github.com/belangeo/pyo/blob/master/examples/wxgui/ 01_gui_widgets_example.py

Bibliografía

 Bodnar, J (2018, Mayo) wxPython Tutorial. ZetCode. Consultado Octubre 2018 desde http://zetcode.com/wxpython/  Dunn, R (2017, Agosto) wxPythonHistory. WxPython. Consultado Octubre 2018 desde https://wiki.wxpython.org/WxPythonHistory

 The wxPython Team (2018, Junio) wxPython Docs. WxPython API Documentation. Consultado Octubre 2018 desde https://docs.wxpython.org/index.html  Belanger, O. (2018, Agosto) Additional wxPython Widgets. Consultado Octubre 2018 desde http://ajaxsoundstudio.com/pyodoc/api/classes/wxgui.h tml