Python-QML integration#
This tutorial provides a quick walk-through of a python application that loads, and interacts with a QML file. QML is a declarative language that lets you design UIs faster than a traditional language, such as C++. The QtQml and QtQuick modules provides the necessary infrastructure for QML-based UIs.
In this tutorial, you will learn how to integrate Python with a QML application. This mechanism will help us to understand how to use Python as a backend for certain signals from the UI elements in the QML interface. Additionally, you will learn how to provide a modern look to your QML application using one of the features from Qt Quick Controls 2.
The tutorial is based on an application that allow you to set many text properties, like increasing the font size, changing the color, changing the style, and so on. Before you begin, install the PySide6 Python packages.
The following step-by-step process will guide you through the key elements of the QML based application and PySide6 integration:
First, let’s start with the following QML-based UI:
The design is based on a GridLayout, containing two ColumnLayout. Inside the UI you will find many RadioButton, Button, and a Slider.
With the QML file in place, you can load it from Python:
1 2if __name__ == '__main__': 3 app = QGuiApplication(sys.argv) 4 QQuickStyle.setStyle("Material") 5 engine = QQmlApplicationEngine() 6 7 # Get the path of the current directory, and then add the name 8 # of the QML file, to load it. 9 qml_file = Path(__file__).parent / 'view.qml' 10 engine.load(qml_file) 11 12 if not engine.rootObjects(): 13 sys.exit(-1) 14
Notice that we only need a
QQmlApplicationEngine
toload
the QML file.Define the
Bridge
class, containing all the logic for the element that will be register in QML:1# To be used on the @QmlElement decorator 2# (QML_IMPORT_MINOR_VERSION is optional) 3QML_IMPORT_NAME = "io.qt.textproperties" 4QML_IMPORT_MAJOR_VERSION = 1 5 6 7@QmlElement 8class Bridge(QObject): 9 10 @Slot(str, result=str) 11 def getColor(self, s): 12 if s.lower() == "red": 13 return "#ef9a9a" 14 elif s.lower() == "green": 15 return "#a5d6a7" 16 elif s.lower() == "blue": 17 return "#90caf9" 18 else: 19 return "white" 20 21 @Slot(float, result=int) 22 def getSize(self, s): 23 size = int(s * 34) 24 if size <= 0: 25 return 1 26 else: 27 return size 28 29 @Slot(str, result=bool) 30 def getItalic(self, s): 31 if s.lower() == "italic": 32 return True 33 else: 34 return False 35 36 @Slot(str, result=bool) 37 def getBold(self, s): 38 if s.lower() == "bold": 39 return True 40 else: 41 return False
Notice that the registration happens thanks to the
QmlElement
decorator, that underneath uses the reference to theBridge
class and the variablesQML_IMPORT_NAME
andQML_IMPORT_MAJOR_VERSION
.Now, go back to the QML file and connect the signals to the slots defined in the
Bridge
class:Bridge { id: bridge }
Inside the
ApplicationWindow
we declare a component with the same name as the Python class, and provide anid:
. Thisid
will help you to get a reference to the element that was registered from Python.1 RadioButton { 2 id: italic 3 Layout.alignment: Qt.AlignLeft 4 text: "Italic" 5 onToggled: { 6 leftlabel.font.italic = bridge.getItalic(italic.text) 7 leftlabel.font.bold = bridge.getBold(italic.text) 8 leftlabel.font.underline = bridge.getUnderline(italic.text) 9 10 } 11 }
The properties Italic, Bold, and Underline are mutually exclusive, this means only one can be active at any time. To achieve this each time we select one of these options, we check the three properties via the QML element property as you can see in the above snippet. Only one of the three will return True, while the other two will return False, that is how we make sure only one is being applied to the text.
Each slot verifies if the selected option contains the text associated to the property:
1 @Slot(str, result=bool) 2 def getItalic(self, s): 3 if s.lower() == "italic": 4 return True 5 else: 6 return False
Returning True or False allows you to activate and deactivate the properties of the QML UI elements.
It is also possible to return other values that are not Boolean, like the slot in charge of returning the font size:
1 @Slot(float, result=int) 2 def getSize(self, s): 3 size = int(s * 34) 4 if size <= 0: 5 return 1 6 else:
Now, for changing the look of our application, you have two options:
Use the command line: execute the python file adding the option,
--style
:python main.py --style material
Use a
qtquickcontrols2.conf
file:1[Controls] 2Style=Material 3 4[Universal] 5Theme=System 6Accent=Red 7 8[Material] 9Theme=Dark 10Accent=Red
Then add it to your
.qrc
file:1<!DOCTYPE RCC><RCC version="1.0"> 2<qresource prefix="/"> 3 <file>qtquickcontrols2.conf</file> 4</qresource> 5</RCC>
Generate the rc file running,
pyside6-rcc style.qrc -o style_rc.py
And finally import it from yourmain.py
script.
1import sys 2from pathlib import Path 3 4from PySide6.QtCore import QObject, Slot 5from PySide6.QtGui import QGuiApplication 6from PySide6.QtQml import QQmlApplicationEngine, QmlElement 7from PySide6.QtQuickControls2 import QQuickStyle 8 9import style_rc
You can read more about this configuration file here.
The final look of your application will be: