Diseño de aplicaciones PyQt6: hojas de estilo QSS predeterminadas y personalizadas

En esta guía, veremos cómo diseñar una aplicación PyQt utilizando las hojas de estilo QSS predeterminadas y personalizadas.

Introducción

En esta guía, veremos cómo diseñar una aplicación PyQt.

If you're unfamiliar with PyQt, read our Guía para trabajar con el marco PyQT de Python

Comenzaremos con los temas predeterminados de PyQt y cómo cambiarlos, antes de continuar con el uso de diferentes tipos de estilos personalizados.

Temas predeterminados de PyQt

PyQt funciona con temas predeterminados basados ​​en el sistema operativo. Esto significa que no especificar un tema le dará a la aplicación un aspecto diferente en diferentes sistemas.

Su aplicación se verá diferente en una máquina con Windows 10 que en una máquina con Linux.

Hay muchos estilos o temas que se envían con PyQt, además de los temas predeterminados.

Para mantener una apariencia uniforme en nuestra aplicación mientras la distribuimos a múltiples sistemas operativos, querremos cambiar el tema predeterminado con uno preconstruido o construir uno propio. Alternativamente, puede mantener un aspecto nativo según el sistema operativo.

Comprobar todos los estilos de sistema disponibles

Dado que los estilos de sistema predeterminados difieren de un sistema a otro, no todos los sistemas tendrán los mismos estilos predeterminados disponibles.

Afortunadamente, PyQt tiene una función integrada para recuperar todos los estilos disponibles, guardados en un diccionario. Echemos un vistazo a todos los estilos de sistema disponibles:

1
2
3
# The QStyleFactory object holds all the default system styles.
from PyQt6.QtWidgets import QStyleFactory
print(QStyleFactory.keys())

En una máquina con Windows, esto devolverá los tres estilos siguientes:

1
['windowsvista', 'Windows', 'Fusion']

En Linux, por otro lado, devolverá:

1
['Breeze', 'Oxygen', 'QtCurve', 'Windows', 'Fusion']

Para averiguar qué estilo predeterminado se aplica a una aplicación existente, puede acceder a objectName() a través de app.style():

1
2
3
4
5
import sys
from PyQt6.QtWidgets import QApplication

app = QApplication(sys.argv)
print(app.style().objectName())

El resultado, de nuevo, depende de tu sistema operativo:

1
windowsvista

Aplicación de estilos de sistema a aplicaciones PyQt6

Para cambiar el estilo predeterminado del sistema a otro estilo, podemos usar el método setStyle() en la instancia QApplication, con otro estilo como argumento.

Establezcamos el estilo predeterminado en Fusion en una pequeña aplicación:

 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
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
app = QApplication(sys.argv)

# Set the 'Fusion' system style
app.setStyle('Fusion')

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

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

# Create the button Widgets we will add to the layout.
# Add the button Widgets to the VerticalBoxLayout
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()

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

Ahora, esto aplica la hoja de estilo Fusion a nuestros elementos, cambiando su apariencia:

fusion stylesheet pyqt6

Estilos PyQt6 personalizados

Aunque estos estilos son realmente agradables, es posible que tenga una visión diferente para su aplicación. ¿Qué sucede si desea cambiar los botones para que sean rojos, pero mantener aplicado el resto de la hoja de estilos de Fusion?

De la misma manera que puede estilizar páginas HTML, también puede estilizar aplicaciones PyQt, en línea y a través de QSS Stylesheets.

Las hojas de estilo QSS se inspiran en las hojas de estilo CSS y tienen una sintaxis prestada con un pequeño ajuste.

Agregar estilo en línea a las aplicaciones PyQt6

Cuando la cantidad de código de estilo no garantiza un archivo QSS independiente e independiente, es más fácil escribir algún código de estilo en línea, es decir, dentro del archivo de Python en el que reside su aplicación.

Similar al diseño de páginas HTML en línea, no se considera la mejor práctica, pero está bien para la creación de prototipos, pruebas o ajustes realmente pequeños.

Cada QWidget acepta una función setStyleSheet(), que acepta una cadena CSS:

1
2
3
4
5
widget.setStyleSheet(
    """
    CSS SYNTAX
    """
)

Por ejemplo, si queremos aplicar un color a un widget, establecer su familia de fuentes y tamaño, simplemente podemos usar la sintaxis CSS demasiado familiar:

1
2
3
4
5
6
7
8
9
...
button = QPushButton('One')
button.setStyleSheet(
    "background-color: #262626; "
    "font-family: times; "
    "font-size: 20px;"
)

layout.addWidget(button)

Si reemplazáramos el código de adición del botón original con este, la aplicación se vería así:

stylized button

Ahora, si queremos darle estilo a otro componente, le aplicamos la función setStyleSheet(). Esto se convierte rápidamente en un problema si desea diseñar varios componentes...

Aunque puede aplicar la función setStyleSheet() a la instancia de QApplication y aplicar estilo globalmente a toda la aplicación. Sin embargo, si ya está allí, también podría crear una nueva hoja de estilo personalizada y usarla en lugar del código de estilo en línea.

Creación de hojas de estilo personalizadas

Los estilos del sistema operativo son solo hojas de estilo presentes en diferentes envíos de PyQt. No hay nada que le impida definir su propia hoja de estilo QSS hecha específicamente para su aplicación.

Además, el uso de hojas de estilo soluciona una gran cantidad de problemas que pueden surgir al diseñar sus componentes en línea, además del hecho de que tiene sentido separar la lógica comercial del código de estilo.

Los archivos QSS son muy similares a los archivos CSS: la única diferencia es cómo identificas los elementos que te gustaría diseñar. No puede asignar una ID única a un determinado widget para diseñarlo individualmente. Tendrás que agrupar manzanas con manzanas y diseñar tipos de widgets de la misma manera.

Opcionalmente, también puede diseñar objetos dentro de los widgets individualmente desde el propio widget:

1
2
3
4
5
6
7
QPushButton {
    background-color: blue;
}

QLabel#title {
    font-size: 15px;
}

El primer estilo definirá nuestro color de fondo para todos los objetos QPushButton en la aplicación.

El segundo estilo solo diseñará el objeto de título de un QLabel.

Para incluir un archivo QSS en su aplicación, puede aplicarlo leyendo el archivo y usando la función QApplication.setStyleSheet(str):

1
2
3
4
5
6
# Open the qss styles file and read in the css-alike styling code
with open('styles.qss', 'r') as f:
    style = f.read()

    # Set the stylesheet of the application
    app.setStyleSheet(style)

En realidad, estos archivos son solo el contenedor de nuestras cadenas de estilos.

Diseño de una aplicación de demostración

Con todo esto en mente, creemos una hoja de estilo QSS simple y aplíquela a una aplicación de demostración simple:

 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
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)

# Create the QTableWidget Widget
table = QTableWidget()

# Set the row count of the table to the length of the 'nordic_countries' variable
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 value of the items
    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()

# Open the sqq styles file and read in the css-alike styling code
with open('styles.qss', 'r') as f:
    style = f.read()
    # Set the stylesheet of the application
    app.setStyleSheet(style)

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

Y dentro del archivo styles.qss:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
QTableWidget {
    font-family: Titillium;
    font-size: 20px;
}

QTableWidget::item {
    background-color: #D3D3D3;

}

QTableWidget::item:hover {
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1);
    border: 1px solid #bfcde4;
}

stylized pyqt6 application with custom qss stylesheet

Conclusión

En esta guía, hemos echado un vistazo a cómo diseñar aplicaciones PyQt6 en Python. Hemos explorado los estilos estándar del sistema operativo, cómo diseñarlos usando código en línea y cómo crear hojas de estilo QSS personalizadas.