PyQt 5 + Designer – Lo instalamos y examinamos código – tutorial

Interfaz gráfica Python - Pyqt Primera ventana

Heey! Ahora si se acabo la consolita cutre, vamos a ver algo un poco más bonito!! Aprenderemos a crear aplicaciones gráficas usando PyQt y vamos a aplicar lo que aprendimos en el Modulo 1 y Modulo 2. Trataremos de que sea fácil y preciso explicando cada parte de código en “criollo” como siempre para que aprendas de verdad y no te estanques!. Vamos a conocer un poco PyQt para luego instalar y sacar a volar una ventana sencilla, con un label y un botón bastante chulito!

 

¿Qué es PyQt?

Pyqt como dijimos es un binding de la biblioteca gráfica de QT para el lenguaje de programación Python. Nos permitirá desarrollar aplicaciones con un entorno gráfico agradable.Pyqt - 5

Este entorno de desarrollo tiene algunas ventajas y desventajas que debemos destacar:

Ventajas de utilizar Pyqt:

  • Completo conjunto de elementos (Listados, árboles, grillas, etc.))
  • Flexible y potente control del comportamiento de la interface.
  • Rápido y de apariencia nativa (las últimas versiones utilizan apariencia nativa en windows))
  • Se puede separar el diseño de la interface, pero utiliza un compilador Pyuic para crear las clases.
  • Arquitectura opcional para Modelo/Vista de las tablas, listas y árboles.

Desventajas de utilizar Pyqt:

  • No viene pre-instalado con Python.
  • Algo más complejo de aprender que otras GUI’s.
  • No es del todo “Pythonico”. En ocasiones emerge la implementación en C++ subyacente, teniendo que hacer cast entre tipos de datos (conversión de tipos de datos.), etc.
  • El prefijo Qt/Q (QtGUI, QWidget, QAplicattion) hace que nuestro código sea menos “Pythonico”.)

Pero todas estas desventajas no nos importan, lo vamos a instalar igual!. Venga a ello!

 

 

Tutorial: Instalar PyQt 5 y QtDesigner

 

Use PIP!Primero lo primero dijo mi primo!. Para instalar PyQt de una manera más sencilla necesitamos PIP (una utilidad de línea de comandos que permite instalar, reinstalar o desinstalar paquetes PyPI con un comando simple y directo: “pip”)

 

 

Instalando PIP + PyQt5 en Linux (terminal):

Por supuesto primero debemos saber que versión de Python tenemos instalada, en caso de tener ambas como en el mío instala

Aquí te dejo los comandos:

#Detectar version de Python (utiliza uno u otro):
python --version

python3 --version

            ################ Instalar PIP ################
#Python 2.x
sudo apt-get install python-pip

#Python 3.x
sudo apt-get install python3-pip

#Actualizar PIP una vez instalado

pip install -U pip

            ################ Instalar PyQt5.- ################

pip install pyqt5

#Y eso es todo!

 

 

Instalando PIP + PyQt5 en Windows (cmd):

Para instalar PIP en Windows primero debemos saber que versión de Python tenemos instalada, luego procederemos a descargar de las URL que te dejo debajo el script de PIP llamado “get-pip.py” (es un script de Python) y a ejecutarlo.

 

#Detectar version de Python (utiliza uno u otro):
python --version

python3 --version

            ################ Instalar PIP ################
#Python 2.x
#Descarga el siguiente archivo: https://bootstrap.pypa.io/get-pip.py

#Python 3.x
#Descarga el siguiente archivo: https://bootstrap.pypa.io/3.2/get-pip.py

#Posicionate en la carpeta donde lo guardaste. Puedes utilizar el comando "cd" para
#moverte por los directorios en Windows, una vez estés allí ejecuta, según tu versión:

#Python 2.x:
python get-pip.py

#Python 3.x:
python3 get-pip.py


            ################ Instalar PyQt5.- ################

pip install pyqt5

#Y eso es todo!

Nos preguntara si deseamos proceder y escribimos “Y” e instalamos. Una vez finalizada la instalación podemos abrir QtDesigner:

En linux desde el menú o en la consola:

designer

QtDesigner

Bonito! Sublime!

-Ahora, que coño es esto?

Pues QtDesigner nos va a permitir diseñar el entorno gráfico de nuestro programa a través de Widgets (botones, cuadros de texto, checkbox, etc.) que puedes arrastrar y soltar, redimensionar, en fin, pura comodidad. Luego podrás exportarlo a un archivo .ui (User interface). El cual mas tarde vamos a pasar a .py (Archivo de Python)

Venga a ello, empecemos de una vez!

 

 

Nuestra primera ventana en Pyqt 5

Primeramente debemos crear una ventana (Main Window), con un tamaño por defecto, ya luego la dimensionaremos a nuestro gusto!

Primera ventana en Qtdesign

Veremos algo como esto, así que vamos a dimencionarla a nuestro gusto y agregaremos dos Widget’s. Un Label (Texto) y un botón (Push Button), ambos los vamos a dejar por defecto!:

 

Label Pyqt5

Si deseas puedes ver una vista previa de tu ventana, tanto en tu SO actual como por ejemplo en Windows o Linux. Así:

 

Preview pyqt

 

 

Exportando nuestra ventana .ui a .py

Bien ahora vamos a guardar nuestro diseño en un archivo .UI para luego pasarlo a uno de Python que nos permita trabajar el código. Pues esto solo es la gráfica, si presionas ese botón, no pasara ni papa. Recuerda guardar el archivo donde te quede mas fácil para acceder luego desde la consola. En mi caso simplemente lo guarde en el escritorio. Solo vas a Archivo > Guardar Como.. Y le das un nombre ademas de especificar la ubicación.

Guardar pyqt 5

Una vez que tienes tu archivo.UI vamos a convertirlo a .py. Para eso vamos a la consola y mediante el siguiente comando lo convertimos. (Recuerda que en la consola debes posicionarte donde se encuentra el archivo primero.)

pyuic5 -x miventana.ui -o miventana_ui.py

Tu archivo debe tener el nombre con el que lo has guardado, así que cambia en el comando por tu nombre y ejecutas. Deberías ver inmediatamente que aparece un archivo .py

Si ejecutas este archivo .py como normalmente lo hacemos vas a ver que aparece tu ventana, pero obviamente como dijimos, no hace nada.

 

El código Python de nuestra ventana Pyqt 5

Ahora con nuestro IDE vamos a abrir ese archivo haber que contiene, en mi caso utilizo Geany. Así que al abrirlo vemos el siguiente código:

Abrir Spoiler
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'miventana.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(293, 189)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(100, 100, 80, 23))
        self.pushButton.setObjectName("pushButton")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(110, 20, 59, 15))
        self.label.setObjectName("label")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 293, 20))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))
        self.label.setText(_translate("MainWindow", "TextLabel"))
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Vamos a detenernos a leerlo, aunque suene extraño. Es muy importante que te detengas a mirarlo, observar con paciencia el código.

 

En la primera linea vemos lo siguiente:

from PyQt5 import QtCore, QtGui, QtWidgets

Esta linea nos esta diciendo que se importa desde PyQt5 el QtCore que vendría a ser el “núcleo” de Pyqt. Siguiendo por QtGUI (Interfaz de usuario) y QtWidgets (Los widgets). Estas son las librerías que necesita Python para mostrar el entorno gráfico, sin ellas no seria posible. Si no entiendes esta parte del codigo pegale un vistazo a esta entrada sobre módulos y librerías en python.

Sigamos! Estoy seguro que reconoces que lo que viene luego es una clase que hereda de object llamada UI_MainWindow. Ahora ves la importancia de aprender las clases en python y programacion orientada a objetos. Si logras comprender el código, al menos un poco es que estas bien parado y puedes continuar 😛

Lo que sigue son métodos y atributos que definen la estética de la ventana y sus Widget’s como por ejemplo el tamaño del botón y dimensiones de la ventana, su label, etc. Vamos a estudiar linea por linea, comentándolas. Venga que tengo unas ganas!

Abrir Spoiler

Nota: Utiliza la función expand code (opción superior derecha del código)para verlo completo sin scroll horizontal. (<–|–>)

# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'miventana.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
    def setupUi(self, MainWindow): #Define el método "Configuración de Interfaz de usuario 
									#y da el argumento MainWindow que si revisas abajo es un objeto instanciado
									#Que hereda la clase de la librería QtWidgets.QMainWindow()
        MainWindow.setObjectName("MainWindow") #Define el nombre de la ventana (objeto) mediante método (H)
        MainWindow.resize(293, 189) #Define las dimensiones de la ventana (objeto) mediante método (H)
        self.centralwidget = QtWidgets.QWidget(MainWindow) #Aquí esta creando centralwidget (A) para MainWindow mediante (M)
        self.centralwidget.setObjectName("centralwidget") #Le asigna el nombre a la parte central widgets mediante (M)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)#Aqui esta creando (A) pushButton en centralwidget mediante (M)
        self.pushButton.setGeometry(QtCore.QRect(100, 100, 80, 23))#ESTABLECE la GEOMETRIA del boton (M)
        self.pushButton.setObjectName("pushButton")#ESTABLECE que el BOTON tiene el label "pushButton" (M)
        self.label = QtWidgets.QLabel(self.centralwidget)#Aqui esta creando el (A)label para centralwidget(M)  
        self.label.setGeometry(QtCore.QRect(110, 20, 59, 15))#Establece la GEOMETRIA del LABEL (M)
        self.label.setObjectName("label")#Establece el Nombre del LABEL (M)
        MainWindow.setCentralWidget(self.centralwidget) #Aqui  mediante método establece centralwidget (A) como una capa
        self.menubar = QtWidgets.QMenuBar(MainWindow) #Crea atributo menubar (Menu de la ventana) para MainWindow(Obj)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 293, 20))#Define medidas (M)
        self.menubar.setObjectName("menubar")#Define nombre del menu (M)
        MainWindow.setMenuBar(self.menubar)#Establece la capa del menu(menubar(A)) al objeto MainWindow mediante (M)
        self.statusbar = QtWidgets.QStatusBar(MainWindow) #Crea atributo statusbar para MainWindow mediante (M)
        self.statusbar.setObjectName("statusbar") #Define nombre de statusbar (A) mediante (M)
        MainWindow.setStatusBar(self.statusbar) #Establece la capa statusbar(A)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow): #Método para traducir la ventana.
        _translate = QtCore.QCoreApplication.translate #Establece valores a variable privada translate (M)
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))
        self.label.setText(_translate("MainWindow", "TextLabel"))
if __name__ == "__main__": #Condicional que comprueba si ha sido ejecutado
    import sys #Importa el módulo Sys
    app = QtWidgets.QApplication(sys.argv) #crea un objeto de aplicación listado de Argumentos
    MainWindow = QtWidgets.QMainWindow() #Instancia
    ui = Ui_MainWindow() #Instancia
    ui.setupUi(MainWindow) #Llama al Método setupUI con MainWindow como argumento
    MainWindow.show() #Método que muestra la ventana
    sys.exit(app.exec_()) #Inicia el ciclo de eventos y bloquea hasta que se cierre la aplicación

 

Examinando y comprendiendo el código

Bien para comenzar a entender un poco el código vamos a comenzar por el condicional puesto que en las lineas anteriores esta importando módulos, librerías y declarando una clase que hereda de object con dos métodos (setupUi, retranslateUi).

Condicional __name__ == __main__

if __name__ == "__main__": #Condicional que comprueba si ha sido ejecutado
    import sys #Importa el módulo Sys
    app = QtWidgets.QApplication(sys.argv) #crea un objeto de aplicación (Argumentos de sys)
    MainWindow = QtWidgets.QMainWindow() #Instancia
    ui = Ui_MainWindow() #instancia
    ui.setupUi(MainWindow) #Llama al Metodo setupUI con MainWindow como argumento
    MainWindow.show() #Método que muestra la ventana
    sys.exit(app.exec_()) #Inicia el ciclo de eventos y bloquea hasta que se cierre la aplicación

Como vemos en la primera linea existe un condicional que esta verificando si el atributo __name__ es igual a “__main__”. Esto sirve para evitar que nuestra ventana aparezca también si se importase como un módulo:

Todo modulo o código de python tiene estos atributos donde __name__ varia depende de como se lo este utilizando. Si fue ejecutado por ejemplo desde el terminal, o si fue importado como un módulo. En este caso el condicional comprueba mediante el operador de comparación (==) si se paso el parámetro “__main__” para __name__. Lo que significaría que fue ejecutado. Y entonces al cumplirse este condicional se ejecuta el código debajo.

Por el contrario si nuestro archivo “.py” fuese importado como un módulo esta condición no se cumpliría y no se ejecutaría el código debajo del condicional. Porque el atributo __name__ en ese caso recibiría el nombre del archivo “.py” Me explico?

Cuando llamamos un archivo como módulo en otro: El atributo __name__ recibe como parámetro su mismo nombre.

Cuando ejecutamos un archivo como programa principal: El atributo __name__ recibe como parámetro “__main__”

Import sys

Ahora cumpliéndose este condicional sigue con “import sys”.

Sys es un módulo de sistema que nos permite trabajar con el interprete. Es decir, que este módulo carga las variables y funcionalidades relacionadas con el interprete. Y es utilizado en nuestro código en la ultima linea “sys.exit(app.exec_())” el cual fuerza la salida del interprete. Pero le esta brindando como argumento “app.exec()” el cual ejecuta un bucle principal que funciona hasta que el programa es cerrado, en lo cual sys.exit() devolverá un código de estado. Este “estado” indica si se han recibido códigos de error. Si este código de estado no es cero (0) es porque hubo errores. Entonces básicamente esa ultima linea es la que ejecuta un bucle que mantiene el programa en ejecución controlada (por decir así) hasta que se envía la orden de cerrarlo (donde recopila información de errores). Si borraras esa ultima linea tu programa se ejecutaría y se cerraría inmediatamente!

app = QtWidgets.QApplication(sys.argv)

Aquí esta instanciando app pasando como argumento de QtWidgets.QApplication a sys.argv. Si nos fijamos en la documentación de Python “sys.argv” retorna una lista de todos los argumentos que se han pasado por lineas de comando. Es decir que, sys.argv retornara una matriz de todos los argumentos que han sido pasados en nuestro código partiendo de cero [0].

MainWindow = QtWidgets.QMainWindow()

Esta instanciando, indica que el objeto MainWindow (nuestra ventana) pertenece a la clase QMainWindow() de la librería QtWidgets. (Aquí en la documentación oficial de PyQt puedes ver mas sobre ella). Entonces al instanciarla esta definiendo todas las propiedades de nuestra ventana. Pero aquellas por defecto, es decir aun no estaríamos aplicando los cambios que realizamos!

ui = Ui_MainWindow()

Aquí indica que ui (objeto) pertenece a la clase UI_MainWindow() que es la que se crea al comienzo de nuestro código luego de la importación de las librerías (Linea 11). Y finalmente llama el método SetupUi:

ui.setupUi(MainWindow)

Entonces aquí es donde se aplica la configuración a nuestra ventana (MainWindow) que tenia por defecto asignado los “estilos por defecto” de QMainWindow()

De esta forma instancia MainWindow con la clase por defecto primeramente, luego crea “ui” instanciadoló a la clase UI_MainWindow que es aquella que tiene nuestras modificaciones y finalmente llama el método setupUI que las aplica a MainWindow. Obtenemos una ventana “por defecto” con nuestras modificaciones (las que aplicamos en Designer) aplicadas a ella. Mas abajo dejo un diagrama.

MainWindow.show()

Este método es el encargado de mostrar la ventana.

Pyqt Código

Espero haberlo explicado lo mejor posible y si por ahí encuentras algún error déjame un comentario!. En realidad no es muy difícil de entender lo que se hace en este código. Pero tampoco vamos a editar este código para crear nuestra aplicación, en realidad deberemos separar la programacion gráfica de la funcional. Eso lo veremos en la siguiente entrada, pero es importante ir comprendiendo como funciona pyqt.

Compartir es agradecer! :)