Curso de creación de GUIs con Qt5 y Python. Capítulo 02: Hola, Mundo

En el anterior capítulo ya teníamos las maletas preparadas (instalación de nuestras herramientas) y empezamos a leer nuestra guía de viajes para nuestro viaje por el mundo de las aplicaciones de escritorio usando Python y Qt. En este capítulo voy a escribir el ‘Hola, Mundo’ de rigor para empezar a ver conceptos clave.

Índice:

[Los materiales para este capítulo los podéis descargar de aquí]

[INSTALACIÓN] Si todavía no has pasado por el inicio del curso, donde explico cómo poner a punto todo, ahora es un buen momento para hacerlo y después podrás seguir con esta nueva receta.

[AVISO] Cuando ejecute algo de código siempre voy a asumir que os encontráis en la carpeta correcta del repositorio y que tenéis activado el entorno virtual pyboqt. Todos los códigos estarán en la carpeta apps del repositorio. Por ejemplo, si voy a ejecutar un código del capítulo 02 (este capítulo) y os digo que lo ejecutéis desde una línea de comandos usando python programa_a_ejecutar.py siempre voy a asumir que la línea de comandos del terminal la tenéis localizada en la carpeta donde se encuentra el programa. Por ejemplo, si quiero ejecutar el programa main.py del capítulo 02 deberás estar en:

Deberás tener el entorno virtual instalado y activado. Si aun no lo tienes instalado mira en los párrafos anteriores:

Y una vez que el terminal esté ahí podréis hacer:

Para que se ejecute todo correcto.

Hola, Mundo

Vamos a escribir nuestro primer GUI con Python y Qt y vamos a aprender una serie de conceptos sobre los GUIs, Qt,… No os preocupéis si lo que vemos no tiene mucho sentido ahora mismo. Lo veremos más en detalle en próximos capítulos.

[Descargo de responsabilidad] Este GUI no va a hacer nada útil.

El Código

Ejecución del código

Guardad el anterior código en un fichero con extensión .py, por ejemplo, fichero.py. en la carpeta que queráis. Si habéis descargado el material del repositorio tendréis el anterior código en el fichero “/ruta/al/repo/pyboqt/apps/02-HolaMundo/main.py“. Abrid un terminal o Anaconda Prompt (Windows) y escribid:

y pulsad ‘Intro’ para que se active el entorno virtual donde tenemos todo instalado.

En este momento vamos a instalar qtpy. Igual ya está instalado pero por si acaso. Para ello hacemos:

Ahora, podéis hacer lo siguiente:

o

y pulsad ‘Intro’. Si todo funciona correctamente os debería de salir una ventanita como la siguiente:

Enhorabuena. Has creado tu primer GUI con Qt5.

El código explicado


Importamos la biblioteca sys (luego comento para qué la usamos) y establecemos la variable de entorno QT_API para usar PySide2 con ayuda de la biblioteca os (comenta las dos primeras líneas si prefieres usar PyQt5). De momento, esto no hace ninguna magia para mostrarnos GUIs en pantalla.


Importamos QApplication y QWidget del módulo QtWidgets de PyQt5 o de PySide2.

  • QApplication: Cualquier aplicación debe crear una instancia de esta clase. Más información abajo.
  • QWidget: Es la clase base de todos los widgets. Los widgets vienen a ser las interfaces que permiten interactuar al usuario con el GUI (botones, líneas de texto, deslizadores,…).

Lee aquí, DRY.


Aquí es donde creamos una instancia de QApplication. Todo GUI hecho con PyQt5/PySide2 debe tener una instancia de QApplication. Esta instancia nos proporciona acceso a información global como la carpeta de la aplicación, la hoja de estilos usada, el tamaño de la pantalla en la que corre el GUI,…

Si hiciéramos un print(dir(app)) obtendríamos lo siguiente:

Si os fijáis, al instanciar QApplication le pasamos sys.argv como argumento. Esto nos permite recoger los argumentos que pasamos a través de la línea de comandos. En el caso anterior lo único que le hemos pasado a Python es el nombre del programa (python fichero.py) y ese sería el único argumento de entrada y que podemos obtener mediante sys.argv[0] (sys.argv es una lista).

QApplication puede recibir una serie de argumentos desde la línea de comandos que le permiten definir su estado interno. Algunos ya vienen predefinidos desde C++ (más información aquí). De momento nos vale con saber esto y ya veremos más sobre esto (o no) en otras recetas.


En el ejemplo creamos una instancia de QWidget, que es la ventana que vemos. A QWidget le podríamos pasar un objeto padre (un objeto al que pertenecería nuestra instancia w, luego veremos más en detalle qué significa esto). Como no le pasamos ningún padre esta será la ventana padre, ventana principal de nuestra aplicación o una ventana top level window. La instancia w dispone de varios métodos. En este caso usamos:

  • resize para darle un tamaño a la ventana (500 x 300 píxeles),
  • move para colocar la ventana en la posición que queremos de nuestra pantalla (arriba a la izquierda en el ejemplo),
  • setWindowTitle nos permite poner un texto en la barra de arriba de la ventana y, por último, llamamos a
  • show para que se muestre en pantalla ya que hasta ahora solo estaba en memoria.

Aquí llamamos al método exec_ de la instancia de QApplication. Notad que el método lleva una barra baja al final para diferenciarlo del exec de Python y que no haya conflictos. Cuando llamamos al método exec_ lo que estamos haciendo es iniciar el bucle de eventos o event loop.

¿Qué es el bucle de eventos?

Digamos que, desde el momento en que llamamos a app.exec_(), la aplicación inicia el bucle de eventos y su estado pasa a ser el de esperar/escuchar/estar pendiente de eventos/señales que vengan de la propia aplicación. Estos eventos/señales pueden ser cosas como un click en un botón, un cambio de estado de una celda de una tabla (cuyo widget en Qt es un QTableWidget), un timeout de un temporizador,…, y la reacción del GUI a estos mismos eventos.

El bucle de eventos estará ‘escuchando’ hasta que llamemos al método exit de la instancia app.

¿Por qué llamamos a app.exec_() dentro de sys.exit?

De esta forma nos aseguramos que el proceso finaliza correctamente o, si ha surgido un error, podemos devolver un código de error al sistema.

El bucle de eventos en más detalle

Voy a dejar un esquema de cómo funciona, grosso modo, el bucle de eventos principal del GUI. Este esquema está adaptado (más bien copiado) del libro de Mark Summerfield [1]:

En la anterior imagen:

  • [1] indica que llamamos al método exec_ de la instancia de QApplication.
  • [2] Lo anterior provoca que se inicie el bucle de eventos y que nuestra aplicación se ponga a ‘escuchar’. Si no ocurre ningún evento sigue esperando. Pero si ocurre un evento reacciona.
  • [3] Si el evento NO es llamar al método exit de la instancia de QApplication lo procesará como corresponda y volverá a [2] a seguir esperando por nuevos eventos.
  • [4] Si el evento SÍ es llamar al método exit de la instancia de QApplication entonces cerrará la aplicación y devolverá el mensaje de estátus (normalmente 0 si ha ido todo correctamente).

Recapitulando un poco el ciclo de vida de un GUI

  • La aplicación será una instancia de QApplication.
  • En el momento que se ha instanciado la aplicación podemos llamar y mostrar nuestras ventanas.
  • Arrancamos el bucle de eventos (event loop) de la ventana principal y Qt empieza a gestionar los eventos relacionados con la aplicación.
  • Finalizamos la aplicación cuando llamamos al método exit de la aplicación o cuando la cerramos, como en el caso del ejemplo, pulsando la ‘X’ de arriba a la derecha de la ventana o pulsando ‘Alt+F4’. El hecho de pulsar es un evento que recibe el event loop y reacciona como le hayamos dicho. En este caso, la reacción es llamar al metodo exit de forma implícita vía el widget.

Terminando

Esto ya se ha puesto más interesante y hemos visto como crear nuestra primera ventana. Han surgido conceptos nuevos que se han explicado muy brevemente. Muchos de ellos los veremos en los próximos capítulos.

Estad atentos al siguiente capítulo.

Referencias

  • Mark Summerfield, 2007: “Rapid GUI programming with Python and Qt: the definitive guide to PyQt programming”. Prentice Hall.

P.D.: Si tienes alguna duda puedes usar los comentarios.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

− seven = 2