Trabajar con el marco PyQT de Python

En esta guía, veremos cómo usar el marco PyQT de Python para desarrollar una interfaz gráfica de usuario (GUI) para una aplicación de escritorio en Python. Población...

Introducción

En esta guía, veremos cómo usar el marco PyQT de Python para desarrollar una Interfaz gráfica de usuario (GUI) para una aplicación de escritorio en Python.

Las alternativas populares de Python para desarrollar una GUI incluyen Tkinter, Kivy, PySimpleGUI , y wxPython.

Nota: Al momento de escribir este tutorial, PyQt6 es la última y más avanzada versión del marco PyQT de Python, y también la versión que usaremos.

Pasaremos por el proceso de instalación y nos familiarizaremos con los elementos clave de PyQT, antes de pasar a Administradores de diseño, Widgets, Señales y ranuras, así como también cómo Estilo Widgets, como así como echar un vistazo a los archivos de interfaz de usuario y cómo crear interfaces de usuario a través de una interfaz intuitiva de arrastrar y soltar, que luego podemos exportar a scripts de Python ejecutables:

  • Instalación

  • Introducción a PyQt

  • Gerentes de diseño

  • Widgets

    • Labels
    • Buttons
    • Line Edits
    • Combo Boxes
    • Radio Buttons
    • Displaying Data Using a Table Widget
    • Displaying Data using a Tree Widget
  • Señales y Slots

  • Diseñar las aplicaciones de widgets

  • Archivos de interfaz de usuario

    • qtDesigner
    • Converting UI Files to Python
  • Conclusión

Instalación

Para utilizar el marco PyQt, primero debemos instalarlo con el administrador de paquetes pip.

Si tiene pip instalado en su sistema, ejecutemos el siguiente comando para instalar la versión más reciente de PyQt:

1
$ pip install pyqt6

Si pip install pyqt6 falla, puede comprobar los cambios de instalación aquí.

Introducción a PyQt

PyQt es un conjunto de herramientas que es el producto de la biblioteca Qt y el lenguaje de programación Python. Como PyQt es uno de los marcos GUI más utilizados para Python, hay toneladas de documentación bien escrita y una gran comunidad.

Una de las clases principales de PyQt es la clase QWidget, la implementación de un Widget. Los widgets son componentes GUI y los componentes básicos de las interfaces de usuario. Un widget puede ser una etiqueta, un botón, un menú, un cuadro combinado, una barra de desplazamiento, una barra de herramientas, un cuadro de diálogo de archivo, etc.

Hay muchos Widgets, y acostumbrarse a ellos lleva tiempo. Repasaremos los widgets más utilizados que, en su mayor parte, estarán presentes en casi todas las aplicaciones PyQt.

El orden relativo de estos Widgets en el marco de una aplicación es dictado y administrado por un Administrador de diseño. También veremos los administradores de diseño disponibles y cómo influyen en el posicionamiento de los componentes de la GUI.

El punto de entrada de cada aplicación PyQt es la clase QApplication, que representa la aplicación en sí. Maneja toda la inicialización y el "lienzo" en el que dibujamos.

Nota: Siempre hay una sola instancia de QApplication, sin importar la cantidad de ventanas o cuadros modales en su aplicación.

Demos un salto e inicialicemos una aplicación PyQt, e inicialicemos una ventana con un lienzo vacío:

1
2
3
4
5
6
7
8
import sys
from PyQt6.QtWidgets import QApplication, QWidget
app = QApplication(sys.argv)
root = QWidget()
root.setWindowTitle('A Simple PyQt6 App')
root.setGeometry(100, 100, 280, 80)
root.show()
sys.exit(app.exec())

Ejecutar este código inicializa una aplicación simple:

Repasemos esta inicialización línea por línea.

En primer lugar, importamos el módulo sys integrado de Python que nos proporciona funciones para manipular el Python Runtime Environment. En nuestro caso, usaremos este módulo para manejar el estado de salida de la aplicación, cuando un usuario presiona el botón "X":

1
import sys

Luego, podemos importar QApplication (la base) y QWidget (los componentes GUI) desde el módulo PyQt6.QtWidgets:

1
from PyQt6.QtWidgets import QApplication, QWidget

A continuación, esta línea es un requisito de QT. Inicializará PyQT. El sys.argv contiene una lista de todos los argumentos de la línea de comandos pasados ​​a la aplicación. Cada aplicación GUI que cree debe tener exactamente una instancia de QApplication.

Ahora, dado que QApplication es responsable de la inicialización de la mayoría de los elementos involucrados en el desarrollo de aplicaciones PyQt, primero querremos instanciarlo. El constructor acepta un argumento sys.argv ya que también puedes pasar argumentos de la línea de comandos:

1
app = QApplication(sys.argv)

Ahora, QApplication en sí no tiene ninguna ventana. Ejecutar la aplicación sin ventana producirá un resultado invisible para nosotros. Para introducir realmente una ventana a la mezcla, crearemos un Raíz Widget, que también se conoce como Window Widget. En cualquier caso, representa el Widget más bajo al que agregaremos otros componentes:

1
root = QWidget()

Establezcamos un título de ventana personalizado usando setWindowTitle():

1
root.setWindowTitle('A Simple PyQt6 App')

El método setGeometry() acepta 4 argumentos: x_coordinate, y_coordinate, width & height. La coordenada_x y la coordenada_y definen el punto de origen de la Ventana cuando se muestra:

1
root.setGeometry(100, 100, 280, 80)

Ahora, para mostrar la GUI creada en la pantalla llamamos al método show() en la raíz:

1
root.show()

Finalmente, ejecutamos la aplicación a través de app.exec(), y ejecutamos el ciclo principal de la aplicación hasta que el usuario la cierre:

1
sys.exit(app.exec())

Administradores de diseño {#administradores de diseño}

Los administradores de diseño de PyQt nos brindan una forma productiva de organizar PyQt Widgets en una GUI. Cuanto mejor diseñemos nuestros Widgets, más pulida y profesional se verá nuestra aplicación GUI. Tener espacios grandes e incómodos entre los botones sin utilizar mucho el espacio no es muy fácil de usar. Lo mismo ocurre al revés: si colocamos los botones demasiado cerca, se volverán incómodamente fáciles de hacer mal.

Las clases de administrador de diseño de PyQt más populares son:

  1. QVBoxLayout organiza los widgets verticalmente.
  2. QHBoxLayout organiza los widgets horizontalmente.
  3. QGridLayout organiza los widgets en una cuadrícula.
  4. QFormLayout organiza los widgets en dos columnas.

Al crear aplicaciones GUI con PyQt, a menudo usará más de uno de los cuatro administradores de diseño de propósito general, incluso en la misma aplicación, para diferentes tipos de ventanas.

Aprovechemos el ejemplo anterior y actualicémoslo agregando varios Widgets como QPushButtons y administrándolos a través de un Administrador de diseño.

1. QVBoxLayout

Un Administrador de diseño de cajas (tanto QVBox como QHBox) usa todo el espacio que obtiene de su diseño principal o Widget y lo divide en varias cajas.

Cada widget administrado por el administrador llenará una casilla.

QVBoxLayout nos permite organizar nuestros Widgets verticalmente. El diseño agrega los Widgets a sí mismo de arriba a abajo, secuencialmente. Entonces, el primer Widget agregado en su código será el Widget que se encuentra en la parte superior y el último Widget agregado en su código será el que se encuentra en la parte inferior del diseño.

Agreguemos varios botones a nuestra aplicación, a través de un QVBoxLayout:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget

# Create the Qt Application
app = QApplication(sys.argv)

# Create the root Widget/Window
window = QWidget()

# Create the Vertical Box Layout Manager, setting `window` as parent by passing it in the constructor.
layout = QVBoxLayout(window)

# Create and add the QPushButton Widgets to the `layout`
layout.addWidget(QPushButton('One'))
layout.addWidget(QPushButton('Two'))
layout.addWidget(QPushButton('Three'))
layout.addWidget(QPushButton('Four'))
layout.addWidget(QPushButton('Five'))

# Show the parent Widget
window.show()

# Run the main Qt loop and allow safe exiting
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo QVBoxLayout

Esta ventana contiene 5 botones que están dispuestos verticalmente, de arriba a abajo. No sucede nada cuando hacemos clic en ellos porque aún no hemos agregado ninguna lógica para ellos.

2. QHBoxLayout

QHBoxLayout es un Diseño de Caja que nos permite organizar nuestros Widgets horizontalmente. El diseño agrega los Widgets a sí mismo de izquierda a derecha. Entonces, el primer Widget agregado en su código será el Widget más a la izquierda y el último Widget agregado en su código será el Widget más a la derecha en el diseño.

Cambiemos el cuadro vertical por uno horizontal:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QHBoxLayout, QWidget

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget of the Widgets added to the layout
window = QWidget()

# Create the Horizontal Box Layout Manager, setting `window` as parent by passing it in the constructor
layout = QHBoxLayout(window)

# Create and add the QPushButton Widgets to the `layout`
layout.addWidget(QPushButton('One'))
layout.addWidget(QPushButton('Two'))
layout.addWidget(QPushButton('Three'))
layout.addWidget(QPushButton('Four'))
layout.addWidget(QPushButton('Five'))

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo QHBoxLayout

Esta ventana contiene 5 botones que están dispuestos horizontalmente, de izquierda a derecha.

3. QGridLayout

Se utiliza un QGridLayout cuando queremos organizar los widgets en una cuadrícula de filas y columnas. En esta grilla, usando coordenadas podemos definir la posición relativa de cada Widget como: (fila, columna).

Nota: Tanto la fila como la columna deben ser números enteros.

QGridLayout también usa todo el espacio que obtiene del diseño o Widget de su padre y lo divide en varios cuadros. Al igual que con los administradores de diseño anteriores, cada widget va en su propia caja. El número de casillas se calcula automáticamente en función del número de Widgets y sus coordenadas.

Usemos un QGridLayout en lugar del diseño de cuadro horizontal:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/python

# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QGridLayout, QWidget

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget
window = QWidget()

# Create the buttons
button1 = QPushButton('One')
button2 = QPushButton('Two')
button3 = QPushButton('Three')
button4 = QPushButton('Four')
button5 = QPushButton('Five')

# Create the QGrid Layout Manager
layout = QGridLayout(window)

# Add button Widgets to the QGridLayout
# addWidget([object], [row number], [column number])
layout.addWidget(button1,0,0)
layout.addWidget(button2,1,0)
layout.addWidget(button3,2,0)
layout.addWidget(button4,0,1)
layout.addWidget(button5,0,2)

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo QGridLayout

Esta ventana contiene 5 botones que están dispuestos como especificamos en el método addWidget(). El método en sí acepta 3 argumentos:

  1. El widget que debe colocarse en la cuadrícula.
  2. La fila en la que debe colocarse.
  3. La columna en la que debe colocarse.

Hay un cuarto argumento opcional, alineación, que define la opción de alineación de cada Widget dentro de su cuadro. El valor predeterminado (es Qt.Alignment.AlignCenter) significa que cada Widget debe llenar su cuadro completo desde el centro hacia afuera. Más sobre el módulo Qt en secciones posteriores.

Finalmente, también hay un argumento columnSpan y rowSpan, que definen si un widget abarca varias filas o columnas:

1
addWidget(Widget, fromRow, fromColumn, rowSpan, columnSpan, Qt.Alignment)

Establezcamos los intervalos de filas y columnas, así como un Qt.Alignment (antes de PyQt6, esto sería Qt.AlignLeft):

1
2
3
4
5
6
7
8
9
# New import other than the ones already present
from PyQt6.QtCore import Qt

# addWidget([object], [row number], [column number], [columnSpan], [rowSpan], Qt.Alignment)
layout.addWidget(button1, 0, 0, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button2, 1, 0, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button3, 2, 0, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button4, 0, 1, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button5, 0, 2, 1, 1, Qt.Alignment.AlignLeft)

Puede AlignLeft, AlignTop, AlignBottom, AlignRight y AlignCenter. Cambiando el tamaño de la ventana, veremos que cada botón está alineado a la izquierda de su propio cuadro, en lugar del centro:

Mientras que, si usamos AlignCenter o lo dejamos como predeterminado:

4. QFormLayout

QFormLayout facilita la producción de diseños de formularios para aplicaciones de escritorio. Consta de dos columnas: una para las etiquetas y otra para las entradas.

Normalmente, el widget de entrada es QLineEdit, QSpinBox, QComboBox o widgets de entrada similares. Vamos a crear un QFormLayout:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/python

# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit

def addLabel(layout, text):
    layout.addWidget(QLabel(text))

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget and the QVBoxLayout Layout Manager
window = QWidget()
layout = QVBoxLayout(window)

# Create a label Widget and add it to the layout
label = QLabel('Enter some text!')
layout.addWidget(label)

line_edit = QLineEdit()
layout.addWidget(line_edit)

# Create a QPushButton object with a caption on it
qbtn= QPushButton('Add Label')

# Add the QPushButton to the layout
layout.addWidget(qbtn)

# Close the application when the button is pressed
# Here I am using slots & signals, which I will demonstrate later in this tutorial
qbtn.clicked.connect(lambda:addLabel(layout, line_edit.text()))

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo de QFormLayout

Esta ventana contiene 2 etiquetas y 2 campos QLineEdit agregados mediante el método addRow(). addRow() acepta 2 argumentos:

  1. Texto de etiqueta (cadena)
  2. Widget de entrada (QWidget)

El método creará y agregará automáticamente un nuevo objeto QLabel con nuestro labelText como texto. Además, también puede agregar un argumento QLabel en lugar de una cadena, lo que omite la conversión automática:

1
2
layout.addRow(QLabel('Nickname:'), QLineEdit())
layout.addRow(QLabel('Score:'), QLineEdit())

Esto también resulta en:

Widgets

Ahora que estamos familiarizados con los administradores de diseño que ofrece PyQt, pasemos a lo que administran. Los widgets son un concepto clave de Qt y, en consecuencia, de PyQt.

Un widget refleja un componente gráfico de la interfaz de usuario. Una interfaz de usuario se compone de varios widgets, dispuestos dentro de la ventana. Cada Widget contiene una serie de atributos y métodos que nos permiten modelar su apariencia y comportamiento.

PyQt6 actualmente ofrece más de 40 widgets, e incluso puede crear sus widgets personalizados.

Desde PyQt5, ha habido una reorganización de las clases base en diferentes módulos. Hay un par de módulos fundamentales de alto nivel utilizados por PyQt6, que incluyen:

  • Qt: Todos los módulos mencionados a continuación se pueden encontrar empaquetados en este único módulo.
  • QtCore: El módulo QtCore contiene todos los módulos básicos no gráficos, utilizados por otros módulos. Señales, Slots, etc... se implementan en este módulo.
  • QtWidgets: este módulo contiene la mayoría de los Widgets disponibles en PyQt6.
  • QtGui: QtGui amplía el módulo QtCore y contiene componentes GUI.
  • QtSql: este módulo implementa la integración de bases de datos para bases de datos SQL.
  • QtMultimedia: la funcionalidad multimedia de bajo nivel se puede encontrar en este módulo.
  • QtNetwork: Las clases utilizadas para implementar la programación de red (Sockets, Manejo de SSL, Sesiones de red, DNS, ...) se pueden encontrar en este módulo.

En esta sección, nos centraremos en el módulo QtWidgets y los widgets que ofrece.

1. Etiquetas

El widget más popular, la etiqueta, se usa más comúnmente para explicar el propósito o el uso de su GUI, como anotar para qué sirve un campo. Podemos crear una etiqueta llamando a la clase QLabel. Tenga en cuenta que este Widget no proporciona ninguna interacción con el usuario.

Podemos cambiar la apariencia visual de una etiqueta de varias formas:

  • setAlignment() alineará el título según las constantes de alineación, que pueden ser las siguientes:
    • Alignment.AlignLeft
    • Alignment.AlignRight
    • Alignment.AlignCenter
    • Alignment.AlignJustify
  • Text() se usa para recuperar el título de una etiqueta.
  • setText(), en lugar de recuperar el título, establecerá el título de una etiqueta.
  • setIndent() establecerá la sangría.
  • setWordWrap() envolverá las palabras en una etiqueta, o no, dependiendo del booleano pasado.

Ahora, hagamos una pequeña aplicación PyQt6 usando solo etiquetas, para mostrar información sobre Bélgica:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
from PyQt6.QtCore import Qt

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget and the QVBoxLayout Layout Manager
window = QWidget()
layout = QVBoxLayout(window)

# Create a label beforehand
firstLabel = QLabel('Countrycode: BE')
secondLabel = QLabel('Brussels waffles are the best food ever.')

# Add labels to layout, creating an anonymous label while adding
layout.addWidget(firstLabel)
layout.addWidget(secondLabel, alignment = Qt.Alignment.AlignJustify)
layout.addWidget(QLabel('The Belgian flag consists of the colors black, yellow and red', wordWrap=True), alignment = Qt.Alignment.AlignLeft)

# using setText() we can change the caption of a label
firstLabel.setText('Belgium is a country located in Europe')
firstLabel.setAlignment(Qt.Alignment.AlignRight)

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Puede crear un QLabel de antemano como con firstLabel. Luego, incluso después de agregarlo a un diseño, puede manipularlo y configurar el texto, la alineación, etc. a través de sus métodos de configuración. Los últimos estados, establecidos por los setters, se dibujarán en la ventana al final.

Si desea evitar crear objetos de antemano y llamar a muchos métodos, simplemente puede crear un widget y agregarlo justo después, en la llamada addWidget(). Hemos configurado el argumento wordWrap del tercer QLabel como verdadero, ya que es un poco más largo que los otros dos y es posible que queramos envolver las palabras en caso de que sean más largas de lo que la ventana puede acomodar. por.

Nota: Desde PyQt6, Qt es parte de PyQt6.QtCore, y las opciones Align_ son parte de la clase Alignment, lo que da como resultado llamadas Qt.Alignment.Align_. Antes de PyQt6, Qt era parte del módulo PyQtX, no del módulo QtCore, y las opciones Align_ eran parte de Qt, por lo que las llamadas se verían más como - Qt.Align_ en cambio.

Si ejecutamos este código, veremos nuestras tres etiquetas, alineadas según nuestra configuración de Alineación:

Ejemplo de etiquetas

2. Señales y ranuras

Las señales y las ranuras en PyQt se utilizan para comunicarse entre objetos. Este mecanismo es una característica central del marco Qt.

Por ejemplo, si un usuario hiciera clic en un botón Eliminar, queremos que se llame a la función delete() de la ventana. Para ello, los 2 Widgets tienen que comunicarse entre sí.

Un evento será una acción realizada por un usuario en nuestra GUI.

Cuando ocurre un evento, el Widget correspondiente emite una señal. Los widgets disponibles en Qt tienen muchas señales predefinidas, pero siempre puede crear señales personalizadas adicionales.

Una ranura es una función que se llama en respuesta a una señal. Una vez más, los Widgets disponibles en Qt tienen muchos espacios predefinidos, pero es una práctica muy común crear los suyos propios.

Las funciones más útiles de Signals and Slots incluyen:

  • Una señal se puede conectar a otra señal
  • Una señal se puede conectar a una o varias ranuras
  • Una ranura puede estar conectada a una o varias señales

La sintaxis general para conectar una señal a una ranura es:

1
widget.signal.connect(slot_function) 

Este código conectará slot_function a Widget.signal, y cada vez que se emita la señal, se llamará a la función slot_function().

Para evitar un comportamiento inesperado, es importante anotar cada función de ranura con el decorador @pyqtSlot():

1
2
3
4
5
6
from PyQt6.QtCore import pyqtSlot

# Slot function - Note the @pyqtSlot() annotation!
@pyqtSlot()
def hello_world():
  print('Button is clicked, Hello World!')

Ahora, vamos a crear una aplicación que haga uso del mecanismo Signals and Slots, poniendo un botón que simplemente imprima un mensaje en la consola:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton
from PyQt6.QtCore import pyqtSlot

@pyqtSlot()
def hello_world():
  print('You shall not pass!')

# Create the Qt Application
app = QApplication(sys.argv)

# Create a QPushButton Object
button = QPushButton('Click me')

# Connect the button to the hello_world slot function
button.clicked.connect(hello_world)

# Show the button to the user
button.show()

# Run the main Qt loop
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo corto de señales y tragamonedas

Después de ejecutar este código y hacer clic en el botón, muestra el siguiente texto en la consola:

1
You shall not pass!

3. Botones

Ahora que podemos etiquetar otros componentes GUI en una aplicación, echemos un vistazo al primer componente interactivo que implementaremos: un QButton. Los botones conducen a resultados; en nuestro caso, se pueden usar para invocar ciertas funciones. Hay algunos botones predeterminados predefinidos que son Aceptar, Sí, No, Cancelar, Aplicar y Cerrar, aunque también puede agregarles texto personalizado.

Puede adjuntar un controlador de eventos a un botón que activa una función o cualquier otra pieza de código cuando se presiona un botón. Vamos a crear un botón que permita al usuario agregar un QLabel a la pantalla.

Una vez que un usuario ingresa un texto en un QLineEdit y se detecta que se presionó un botón, recopilaremos los datos de un QLineEdit y usaremos ese texto para configurar el texto de un nuevo QLabel, que es luego agregado al diseño.

Dado que los botones esperan que se pase una función invocable como controlador de eventos de clic, definiremos una nueva función add_label() que se puede usar para agregar cualquier QLabel al diseño especificado:

1
2
def addLabel(layout, text):
    layout.addWidget(QLabel(text))

Ahora, escribamos nuestra GUI y llamemos a esta función con el texto proporcionado por el usuario:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit

def addLabel(layout, text):
    layout.addWidget(QLabel(text))
    
# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget and the QVBoxLayout Layout Manager
window = QWidget()
layout = QVBoxLayout(window)

# Create a Qlabel Widget and add it to the layout
label = QLabel('Enter some text!')
layout.addWidget(label)

# Create a QLineEdit to collect user data
line_edit = QLineEdit()
layout.addWidget(line_edit)

# Create a QPushButton object with a caption on it
qbtn= QPushButton('Add Label')
layout.addWidget(qbtn)

# When clicked, perform a callable function - `addLabel()`
qbtn.clicked.connect(lambda:addLabel(layout, line_edit.text()))

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Una vez que ejecutamos este código, podemos escribir algo de texto en el campo QLineEdit, que se agrega al diseño como QLabel una vez que presionamos Agregar etiqueta:

Ejemplo de Botones

4. Ediciones de línea

Hemos echado un breve vistazo al widget QLineEdit dos veces - tomemos un momento para ver lo que ofrece. Como se vio anteriormente, permite a los usuarios ingresar una línea de texto; esta es la forma rudimentaria de recopilar datos del usuario, ya sea para agregar o editar datos ya existentes. Por supuesto, podemos realizar operaciones comunes como copiar, pegar, deshacer, rehacer mientras escribimos texto dentro de ellas.

Algunos de los métodos comunes que usará con ellos son:

  • setAlignment() una vez más, alineará el título según las constantes de alineación
  • setMaxLength() establece un número máximo de caracteres que el usuario no puede superar
  • text() - recupera el texto dentro de un QLineEdit
  • setText() - establece el texto en un QLineEdit
  • clear() borrará todo el contenido de QLineEdit

Reescribamos el ejemplo anterior, pero esta vez ya tenemos un texto predefinido en un QLineEdit, cambiamos un QLabel ya existente en lugar de agregar uno nuevo y exploramos brevemente el uso de PyQt Slots, que se tratará con más detalle más adelante en la guía.

Crearemos una aplicación de cotización rudimentaria, que tiene un corpus de citas famosas y le brinda una aleatoria a pedido. Puede expandir esta lista agregando una nueva y haciendo clic en Agregar cotización, que luego se incluye en el grupo de cotizaciones cuando decide obtener una nueva cotización aleatoria a través de Obtener cotización aleatoria:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit
from PyQt6.QtCore import pyqtSlot
import random

# Deifne helper functions as PyQt Slots
@pyqtSlot()
def randomQuote():
  # Set label to random quote from the list
  quoteLabel.setText(random.choice(quotes))

@pyqtSlot()
def addQuote():
    # Add new quote to the list and clear the input field
    quotes.append(newQuoteLineEdit.text())
    newQuoteLineEdit.clear()

app = QApplication(sys.argv)
window = QWidget()
layout = QVBoxLayout(window)

# Default quote list
quotes = ['Do or do not, there is no try.', 'The meaning of life is 42']

# Get a random quote for the user
quoteLabel = QLabel(random.choice(quotes))

# QLineEdit field to collect new quote information, and a button for it
newQuoteLineEdit = QLineEdit('Add new quote...')
addQuoteButton = QPushButton('Add New Quote')

# Button to get random quote
getQuoteButton = QPushButton('Get Random Quote')

# Add the previous Widgets to the layout
layout.addWidget(newQuoteLineEdit)
layout.addWidget(quoteLabel)
layout.addWidget(addQuoteButton)
layout.addWidget(getQuoteButton)

# On click - call the slots (functions)
getQuoteButton.clicked.connect(randomQuote)
addQuoteButton.clicked.connect(addQuote)

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Esto resulta en:

5. Cuadros combinados

Los cuadros combinados permiten a los usuarios elegir de una lista de opciones, similar a la etiqueta <seleccionar> en HTML. Estos se pueden lograr a través del widget QComboBox. El QComboBox básico es un widget de solo lectura, lo que significa que el usuario tiene que elegir exclusivamente de la lista predefinida y no puede agregar sus propias opciones. Sin embargo, también pueden ser editables, lo que permite al usuario agregar una nueva opción si ninguna se ajusta a sus necesidades.

A continuación se muestran los métodos más utilizados de la clase QComboBox:

  • addItem() agrega una cadena a la colección
  • addItems() agregará cada una de las cadenas en la lista dada a la colección
  • Clear() se usa para eliminar todos los elementos de la colección
  • count() se usa para recuperar la cantidad de elementos en la colección
  • currentText() se usa para recuperar el texto del elemento actualmente seleccionado
  • itemText() acepta un argumento index y devuelve el texto de ese elemento
  • currentIndex() devuelve el índice del elemento actualmente seleccionado

Vamos a crear una mini aplicación de pedidos, donde un usuario selecciona un artículo de un menú e ingresa un comentario para el restaurante. Luego, cuando se hace clic en un botón, se muestra este orden al usuario:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox
from PyQt6.QtCore import pyqtSlot

@pyqtSlot()
def placeOrder():
    order_format = "Placed order for {} with comment '{}'"
    layout.addWidget(QLabel(order_format.format(comboBox.currentText(), commentLineEdit.text())))

app = QApplication(sys.argv)

window = QWidget()
layout = QVBoxLayout(window)

label1 = QLabel('Pick one of the following options:')
comboBox = QComboBox()
comboBox.addItems(['Pasta', 'Pizza', 'Lasagna'])

layout.addWidget(label1)
layout.addWidget(comboBox)

commentLineEdit = QLineEdit('Comment for the restaurant...')
placeOrderButton = QPushButton('Place order')

layout.addWidget(commentLineEdit)
layout.addWidget(placeOrderButton)

placeOrderButton.clicked.connect(placeOrder)

window.show()
sys.exit(app.exec())

Ahora, hagamos un pedido y adjuntemos una solicitud:

Ejemplo de cuadros combinados

6. Botones de radio y casillas de verificación

Los botones de radio y las casillas de verificación se utilizan principalmente para el mismo propósito: permitir que alguien seleccione una opción entre varias. La única diferencia es que las casillas de opción se usan cuando deseamos limitar al usuario a seleccionar una opción, mientras que las casillas de verificación se usan cuando deseamos permitir que el usuario seleccione varias opciones.

Por ejemplo, podríamos obligar al usuario a seleccionar entre ser un cliente nuevo o antiguo (no pueden ser ambos al mismo tiempo), pero permitirles seleccionar varios servicios en los que les gustaría suscribirse.

Estos se implementan como QRadioButton y QCheckBox, naturalmente. Podemos verificar si están marcados, configurarlos como marcados o desmarcados, configurar su texto y recuperar el texto de sus etiquetas:

  • setChecked() comprueba el botón de radio o la casilla de verificación
  • setText() establece la etiqueta asociada con el botón o la casilla de verificación
  • text() recuperará la etiqueta del botón/casilla de verificación
  • isChecked() verifica si el botón/casilla de verificación está seleccionado o no

Vamos a crear una aplicación simple que permita a los usuarios seleccionar entre algunos de los servicios que ofrece un lavado de autos imaginario:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QRadioButton, QCheckBox

app = QApplication(sys.argv)
window = QWidget()
layout = QVBoxLayout(window)

label_customer = QLabel('Pick one of the following options:')

# Create two radio buttons for the customer, assuming they might be a new customer
qradioButton = QRadioButton('Old Customer')
qradioButton2 = QRadioButton('New Customer')
qradioButton2.setChecked(True)

layout.addWidget(label_customer)
layout.addWidget(qradioButton)
layout.addWidget(qradioButton2)

label_service = QLabel("Pick the services you'd like:")
qCheckBox = QCheckBox('Car Wash')
qCheckBox2 = QCheckBox('Car Polish')
qCheckBox3 = QCheckBox('Vacuuming')

layout.addWidget(label_service)
layout.addWidget(qCheckBox)
layout.addWidget(qCheckBox2)
layout.addWidget(qCheckBox3)

window.show()
sys.exit(app.exec())

Esto nos muestra una aplicación similar a una encuesta que nos permite elegir si somos un cliente antiguo o nuevo, y nos permite elegir entre los servicios que ofrece el lavado de autos:

Ejemplo de botones de radio

7. Visualización de datos mediante un widget de tabla {#7visualización de datos mediante un widget de tabla}

QTableWidget es un widget que, sin tener que lidiar con mucha configuración, nos permite crear impresionantes tablas tipo Excel en PyQt, en las que podemos mostrar datos.

Cada tabla es una tabla basada en elementos, con filas y columnas.

Tenga en cuenta que usar un QTableWidget no es la única forma de mostrar información en las tablas. Los modelos de datos también se pueden crear y mostrar utilizando el widget QTableView. Sin embargo, QTableWidget usa inherentemente un QTableView debajo del capó para crear una tabla, por lo que usaremos el enfoque de nivel superior de usar la tabla desde el principio.

Dada la naturaleza columna-fila de las tablas, podemos crear cómodamente diccionarios para contener datos para ellas o incluso listas de listas. Al crear una tabla, querremos establecer el recuento de columnas y filas antes de agregarle datos, y luego simplemente completarla a través de bucles:

  • setRowCount() establece el número de filas
  • setColumnCount() establece el número de columnas
  • setHorizontalHeaderLabels() establece las etiquetas de los encabezados horizontales

Ahora, creemos una aplicación simple que contenga una tabla con varios países nórdicos y sus capitales:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import (QApplication, QTableWidget, QTableWidgetItem)
from PyQt6.QtGui import QColor

# Declare our table values
nordic_countries = [('Norway', 'Oslo', 'Yes'),
          ('Iceland', 'Reykjavik', 'Yes'),
          ('Denmark', 'Copenhagen', 'Yes'),
          ('Belgium', 'Brussels','No')]
          
# Create the Qt Application
app = QApplication(sys.argv)
table = QTableWidget()

# Configure QTableWidget to have a number of rows equivalent to the amount of items from the nordic_countries struct
table.setRowCount(len(nordic_countries))

# Since every country in our 'nordic_countries' variable has the same amount of attributes
# we take the amount (3) of the first country and use this as the number of columns
table.setColumnCount(len(nordic_countries[0]))

# Set the Horizontal headers using setHorizontalHeaderLabels()
table.setHorizontalHeaderLabels(['Country', 'Capital', 'Scandinavian?'])

# Loop through every country in our 'nordic_countries' variable
for i, (country, capital, scandinavian_bool) in enumerate(nordic_countries):

    # Make a QTableWidgetItem --> acts as an item in a table
    item_country = QTableWidgetItem(country)
    item_capital = QTableWidgetItem(capital)
    item_scandinavian_bool = QTableWidgetItem(scandinavian_bool)

    # Set the items: item, index, QTableWidgetItem
    table.setItem(i, 0, item_country)
    table.setItem(i, 1, item_capital)
    table.setItem(i, 2, item_scandinavian_bool)

# Finally show the table
table.show()

# Launch the application
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo de tabla de widgets

8. Visualización de datos mediante un widget de árbol {#8visualización de datos mediante un widget de árbol}

Los widgets de árbol son realmente útiles para mostrar estructuras en forma de árbol, como jerarquías de archivos o sublistas pertenecientes a listas específicas de elementos. Para adaptarse a este tipo de Widget, PyQt ofrece QTreeWidget.

Similar a cómo QTableWidget se construye sobre QTableView - QTreeWidget se construye sobre QTreeView.

El árbol consta de encabezados y elementos. Los encabezados son los nombres de las columnas. Cada elemento puede tener varios elementos asignados. Un elemento, por ejemplo, puede ser un directorio, mientras que sus propios elementos son los archivos dentro de ese directorio, o un elemento puede ser una tarea, y sus elementos son las personas asignadas a esa tarea.

Algunos de los métodos comunes que usaremos para trabajar con Tree Widgets son:

  • setHeaderLabels() establece el nombre de la columna para el Tree Widget
  • clear() para borrar todos los datos del árbol
  • editItem() para editar un determinado elemento en el Árbol
  • addTopLevelItem() para agregar un elemento de nivel superior
  • addTopLevelItems() para agregar una lista de elementos de nivel superior

Cada elemento se puede asignar a un componente principal. Para un elemento de nivel superior, podemos asignarlo al propio QTreeWidget, mientras que para elementos de nivel inferior, podemos asignarlos a elementos de nivel superior ya existentes. Puede ir tan lejos como desee con esto, sin embargo, tener demasiados niveles puede ser confuso para el usuario.

Avancemos y hagamos una lista de compras rápida, con “Comida” y “Muebles” como los artículos de nivel superior (categorías de artículos que nos gustaría comprar) y sus hijos serán los artículos mismos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6 import QtWidgets

app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(window)

# Create the QTreeWidget Widget
tree_widget = QtWidgets.QTreeWidget()

# Set the column name for the Tree Widget
tree_widget.setHeaderLabels(['Items', 'Total Cost'])

# Populate first tree with QTreeWidgetItem objects
foodList = QtWidgets.QTreeWidgetItem(tree_widget, ['Food', '€ 15'])
QtWidgets.QTreeWidgetItem(foodList, ['Apples', '€ 6'])
QtWidgets.QTreeWidgetItem(foodList, ['Pears', '€ 4'])
QtWidgets.QTreeWidgetItem(foodList, ['Oranges', '€ 5'])

# Populate second tree with QTreeWidgetItem objects
furnitureList = QtWidgets.QTreeWidgetItem(tree_widget, ['Furniture', '€ 225'])
QtWidgets.QTreeWidgetItem(furnitureList, ['Table', '€ 150'])
QtWidgets.QTreeWidgetItem(furnitureList, ['Chairs', '€ 75'])

layout.addWidget(tree_widget)

window.show()
sys.exit(app.exec())

Una vez ejecutamos este código podremos ver la siguiente ventana en nuestra pantalla:

Ejemplo de widget de árbol

Conclusión

En esta guía, saltamos a PyQt, el contenedor de Python para la popular biblioteca Qt.

Echamos un vistazo a algunos de los conceptos clave de la biblioteca y nos pusimos a trabajar con ellos a través de los administradores de diseño, nos familiarizamos con los widgets y creamos varias aplicaciones de demostración realmente simples que muestran cómo puede usarlos. cómo puede usarlos.