Vamos a seguir evolucionando hacia hacer GUIs cada vez más sofisticados. En este capítulo describo como crear una pantalla inicial que sale previamente a la carga de la aplicación principal y que permite ir informando sobre como va el proceso de arranque de la misma.
Índice:
- Instalación de lo que vamos a necesitar.
- Qt, versiones y diferencias.
- Hola, Mundo.
- Módulos en Qt.
- Añadimos icono a la ventana principal.
- Tipos de ventana en un GUI.
- Ventana de inicio – SplashScreen (este capítulo).
- Menú principal. Introducción.
- Mejorando algunas cosas vistas.
- Gestión de eventos o Acción y reacción.
- Introducción a Designer.
- Los Widgets vistos a través de Designer: Primera parte.
- Los Widgets vistos a través de Designer: Segunda parte.
- Los widgets vistos a través de Designer: Tercera Parte.
- Los widgets vistos a través de Designer: Cuarta Parte.
- Los widgets vistos a través de Designer: Quinta Parte.
- Los widgets vistos a través de Designer: Sexta parte.
- TBD… (lo actualizaré cuando tenga más claro los siguientes pasos).
[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.
Habéis visto muchas veces el tipo de ventanitas que vamos a crear hoy. Su nombre en inglés es Splash Screen y en castellano no tengo ni idea de cómo se puede llamar. A continuación os dejo unos cuantos enlaces con ejemplos para que quede claro a lo que nos estamos refiriendo:
Esta ventana no es más que una ventana que sale al principio de ejecutar la aplicación y que permite que la aplicación realice una serie de cosas antes de estar funcional. Por ejemplo, imagínate que cuando haces doble click sobre la aplicación X esta tiene que conectarse a una base de datos y cargar una serie de campos antes de que podamos empezar a usarla. Esto puede llevar algún tiempo. Si no avisamos al usuario de que se está cargando después de que haya pulsado el doble click puede acabar pensando que algo va mal y que la aplicación no responde. Para evitar esto se suele mostrar el Splash Screen que puede dar cierta información al usuario de como va el arranque. Este Splash Screen es lo primero que se carga antes de que la aplicación principal esté totalmente funcional.
Vamos a partir del último código que usamos en el anterior capítulo:
''' Curso de creación de GUIs con Qt5 y Python Author: Kiko Correoso Website: pybonacci.org Licencia: MIT ''' import os os.environ['QT_API'] = 'pyside2' import sys from pathlib import Path from qtpy.QtWidgets import QApplication, QMainWindow from qtpy.QtGui import QIcon class MiVentana(QMainWindow): def __init__(self): super().__init__() self._create_ui() def _create_ui(self): self.resize(500, 300) self.move(0, 0) self.setWindowTitle('Hola, QMainWindow') ruta_icono = Path('.', 'imgs', 'pybofractal.png') self.setWindowIcon(QIcon(str(ruta_icono))) self.statusBar().showMessage('Ready') self.show() if __name__ == '__main__': app = QApplication(sys.argv) w = MiVentana() sys.exit(app.exec_())
Imagen
Podemos usar una imagen para el fondo del Splash Screen. Vamos a usar una con licencia permisiva. En este caso voy a usar la que se encuentra en este enlace.
La podéis descargar y guardar en una carpeta que se llame imgs/ con el nombre splashscreen_background.jpg. La carpeta debe estar en el mismo sitio donde esté nuestro fichero .py.
Si habéis descargado el repositorio, simplemente deberéis ir a la carpeta apps/06-SplashScreen para ejecutar el programa desde allí.
Al anterior código le vamos a añadir una serie de cosas que destacaremos, como siempre, usando el comentario ## NUEVA LÍNEA
en cada línea nueva de código y comentaremos más tarde:
''' Curso de creación de GUIs con Qt5 y Python Author: Kiko Correoso Website: pybonacci.org Licencia: MIT ''' import os os.environ['QT_API'] = 'pyside2' import sys from pathlib import Path import time ## NUEVA LÍNEA from qtpy.QtCore import Qt ## NUEVA LÍNEA from qtpy.QtWidgets import QApplication, QMainWindow, QSplashScreen ## NUEVA LÍNEA from qtpy.QtGui import QIcon, QPixmap ## NUEVA LÍNEA class MiVentana(QMainWindow): def __init__(self): super().__init__() self._create_ui() def _create_ui(self): self.resize(500, 300) self.move(0, 0) self.setWindowTitle('Hola, QMainWindow') ruta_icono = Path('.', 'imgs', 'pybofractal.png') self.setWindowIcon(QIcon(str(ruta_icono))) self.statusBar().showMessage('Ready') self.show() if __name__ == '__main__': app = QApplication(sys.argv) # Crea y muestra el splash screen path = Path('imgs', 'splashscreen_background.jpg') ## NUEVA LÍNEA splash_pix = QPixmap(str(path)) ## NUEVA LÍNEA splash = QSplashScreen( ## NUEVA LÍNEA splash_pix, ## NUEVA LÍNEA Qt.WindowStaysOnTopHint ## NUEVA LÍNEA ) ## NUEVA LÍNEA splash.setEnabled(False) ## NUEVA LÍNEA splash.show() ## NUEVA LÍNEA # Esto es un simple contador/temporizador para mostrar en pantalla # el splash screen. En el futuro haremos que esto sea más útil. for i in range(0, 5): ## NUEVA LÍNEA msg = ( ## NUEVA LÍNEA '<h1><font color="yellow">' ## NUEVA LÍNEA f'Listo en {5-i}s' ## NUEVA LÍNEA '</font></h1>' ## NUEVA LÍNEA ) ## NUEVA LÍNEA splash.showMessage( ## NUEVA LÍNEA msg, ## NUEVA LÍNEA int(Qt.AlignBottom) | int(Qt.AlignHCenter), ## NUEVA LÍNEA Qt.black ## NUEVA LÍNEA ) ## NUEVA LÍNEA time.sleep(1) ## NUEVA LÍNEA app.processEvents() ## NUEVA LÍNEA w = MiVentana() splash.finish(w) ## NUEVA LÍNEA sys.exit(app.exec_())
Muchos ## NUEVA LÍNEA
. Explico un poco el código nuevo y luego vemos cómo hacerlo funcionar.
from qtpy.QtCore import Qt
Qt
es un espacio de nombres con información útil que Qt (el framework) usa internamente en todo su código. Luego vemos lo que usamos de este espacio de nombres.
from qtpy.QtWidgets import QApplication, QMainWindow, QSplashScreen
Importamos la clase QSplashScreen
. Esta clase no es más que una nueva clase que, nuevamente, hereda de QWidget
, como QMainWindow
, y que tiene alguna nueva funcionalidad para que creemos la Splash Screen para nuestra aplicación.
from qtpy.QtGui import QIcon, QPixmap
Importamos la clase QPixmap
. La usamos únicamente para añadir un mapa de píxeles (nuestra imagen) a la ventana del Splash Screen. Luego vemos cómo la estamos usando.
# Crea y muestra el splash screen path = Path('imgs', 'splashscreen_background.jpg') splash_pix = QPixmap(str(path)) splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) splash.setEnabled(False) splash.show()
En esta parte ya hacemos uso de algunas de las nuevas cosas que hemos importado:
path
simplemente guarda la ruta a la imagen que vamos a usar de fondo. Esto no es código relacionado con Qt en sí.splash_pix
es una instancia deQPixmap
. Nos guarda la imagen en una forma que entiende Qt.splash
es la instancia deQSplashScreen
. Recibe la imagen en la forma en que la entiende Qt,splash_pix
, y, además, le pasamos una marca o flag,Qt.WindowStaysOnTopHint
, que le indica que debe permanecer delante del resto de ventanas en el escritorio.splash.setEnabled(False)
nos permite decirle al programa que no esté activo a eventos que le podamos pasar, como un click, ya que podemos tener algún efecto indeseado.splash.show()
, mediante este método mostramos el Splash Screen en pantalla.
Nuestro Splash Screen estará en pantalla hasta que le digamos. Para que se vea un rato voy a usar un temporizador y vamos a actualizar los mensajes que muestra la pantalla.
# Esto es un simple contador/temporizador para mostrar en pantalla # el splash screen. En el futuro haremos que esto sea más útil. for i in range(0, 5): msg = ( '<h1><font color="yellow">' f'Listo en {5-i}s' '</font></h1>' ) splash.showMessage( msg, int(Qt.AlignBottom) | int(Qt.AlignHCenter), Qt.black ) time.sleep(1) app.processEvents()
Aquí vamos a actualizar el mensaje que muestra el Splash Screen en pantalla. La clase QSplashScreen
dispone de varios métodos
para mostrar información en la pantalla. Podríamos añadir una barra de
progreso, leer del programa a medida que va cargando cosas,…, pero lo
vamos a mantener simple ahora usando un temporizador
Hacemos un bucle que vaya de 0 a 4 porque contaremos una serie de segundos. Dentro de este bucle:
- Creamos el mensaje que queremos monstrar guardado en
msg
. Como veis, uso HTML ya que está permitido. - Usamos el método
showMessage
de la claseQSplashScreen
para escribir el mensaje en la ventanita de bienvenida/Carga inicial. Le pasamos el mensaje, la posición del mensaje ((*)int(Qt.AlignBottom) | int(Qt.AlignHCenter)
) y el color (Qt.black
). En este caso, el color no tendrá efecto porque estamos pasando un código HTML y se hace caso al código HTML. - Dejamos que la aplicación duerma 1 segundo usando
time.sleep
. - Llamamos al método
processEvents
de la claseQApplication
que nos permite ir procesando cosas que puedan estar pendientes antes de poder continuar. En este caso no estamos haciendo nada especial pero no hace daño meterlo.
Del bloque que acabamos de comentar vamos a hacer varias variaciones para que podáis ver en vivo como afectan estas variaciones. Por ejemplo, con el tema del color en el mensaje, podéis eliminar el código HTML y pasarle el color blanco, por ejemplo, para que véais cómo se verá ahora el mensaje:
# ... msg = f'Listo en {5 - i} s' splash.showMessage( msg, int(Qt.AlignBottom) | int(Qt.AlignHCenter), Qt.white ) # ...
Por otra parte, había dejado un asterisco (*) al lado de int(Qt.AlignBottom) | int(Qt.AlignHCenter)
para ver si puedo explicar un poco mejor lo que hace eso. Ahí estamos
definiendo la alineación del mensaje en la pantalla. Estamos usando el
operador bitwise or, |
, y el espacio de nombres de Qt
. En ese espacio de nombres se definen varias posiciones como AlignLeft
, AlignTop
,…
Mediante combinaciones de ellas podemos posicionar el mensaje donde nos
interese. Qt recibirá un entero y sabrá que ese entero significará, por
ejemplo, arriba a la derecha, centrado en medio,…
Por ejemplo, según la documentación:
AlignLeft
tiene el valor0x0001
que en binario será 00000001.AlignVCenter
tiene el valor0x0080
que en binario será 10000000.
Si queremos que salga alineado a la izquierda y centrado en altura podemos combinar ambas con el operador |
y nos devolverá, en binario, 10000001, que en decimal será 129.
Usando los valores del espacio de nombres será más sencillo que tener que aprender todos estos números y sabiendo que los podemos combinar como he indicado podemos llegar a tener un gran control sobre lo que queremos mostrar.
Después de este breve inciso podéis cambiar el valor int(Qt.AlignBottom) | int(Qt.AlignHCenter)
por los siguientes valores para ver el efecto de todo esto:
int(Qt.AlignCenter)
int(Qt.AlignRight) | int(Qt.AlignTop)
int(Qt.Alignleft) | int(Qt.AlignBottom)
- …
Sigo comentando el código que nos queda una última línea nueva:
splash.finish(w)
En esta línea usamos el método finish
de QSplashScreen
para que se cierre la ventana de inicio en el momento que la ventana principal, w
, esté lista.
Si guardáis el código que hemos comentado en un programa que se llame main00.py y en la misma carpeta donde está este programa tenéis una carpeta imgs/ con la imagen llamada splashscreen_background.jpg lo podréis ejecutar usando la línea de comandos (o Anaconda Prompt en Windows) de la siguiente forma:
python main00.py
Si todo funciona correctamente, deberéis de ver una ventanita parecida a la siguiente:
Si cambiáis el mensaje para que no use HTML y use el color blanco, como hemos comentado más arriba, se debería ver así (se ve muy pequeñito abajo):
Si cambiáis la alineación podéis hacer que el mensaje aparezca en otros lados. Un ejemplo con el mensaje abajo a la izquierda se debería ver así:
Y aquí lo dejamos hoy. En el próximo capítulo seguiremos añadiendo cosas a nuestra GUI.
Con tu imagen me sale pero con otras no, a que puede ser devido?
Gracias
Sin ver el código que estás usando no tengo ni idea de qué puede estar fallando pero supongo que será que la ruta que estás usando es incorrecta.
Sería interesante ver como añadir la barra de progreso. Mil gracias por los tutoriales.