QML Integration Tutorial

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 through a context property. 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 PySide2 Python packages.

The following step-by-step process will guide you through the key elements of the QML based application and PySide2 integration:

  1. First, let’s start with the following QML-based UI:

    ../../_images/textproperties_default.png

    The design is based on a GridLayout, containing two ColumnLayout. Inside the UI you will find many RadioButton, Button, and a Slider.

  2. With the QML file in place, you can load it from Python:

     1    # Instance of the Python object
     2    bridge = Bridge()
     3
     4    # Expose the Python object to QML
     5    context = engine.rootContext()
     6    context.setContextProperty("con", bridge)
     7
     8    # Get the path of the current directory, and then add the name
     9    # of the QML file, to load it.
    10    qmlFile = join(dirname(__file__), 'view.qml')
    11    engine.load(abspath(qmlFile))
    

    Notice that we specify the name of the context property, con, and also we explicitly load our QML file.

  3. Define the Bridge class, containing all the logic for the context property:

     1class Bridge(QObject):
     2
     3    @Slot(str, result=str)
     4    def getColor(self, color_name):
     5        if color_name.lower() == "red":
     6            return "#ef9a9a"
     7        elif color_name.lower() == "green":
     8            return "#a5d6a7"
     9        elif color_name.lower() == "blue":
    10            return "#90caf9"
    11        else:
    12            return "white"
    13
    14    @Slot(float, result=int)
    15    def getSize(self, s):
    16        size = int(s * 42) # Maximum font size
    17        if size <= 0:
    18            return 1
    19        else:
    20            return size
    21
    22    @Slot(str, result=bool)
    23    def getItalic(self, s):
    24        if s.lower() == "italic":
    25            return True
    26        else:
    27            return False
    28
    29    @Slot(str, result=bool)
    30    def getBold(self, s):
    31        if s.lower() == "bold":
    32            return True
    33        else:
    34            return False
    35
    36    @Slot(str, result=bool)
    37    def getUnderline(self, s):
    38        if s.lower() == "underline":
    39            return True
    40        else:
    41            return False
    
  4. Now, go back to the QML file and connect the signals to the slots defined in the Bridge class:

    1                text: "Bold"
    2                onToggled: {
    3                    leftlabel.font.italic = con.getItalic(bold.text)
    4                    leftlabel.font.bold = con.getBold(bold.text)
    5                    leftlabel.font.underline = con.getUnderline(bold.text)
    6                }
    7            }
    8            RadioButton {
    9                id: underline
    

    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 context 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.

  5. Each slot verifies if the selected option contains the text associated to the property:

    1    @Slot(str, result=bool)
    2    def getBold(self, s):
    3        if s.lower() == "bold":
    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 * 42) # Maximum font size
    4        if size <= 0:
    5            return 1
    6        else:
    7            return size
    
  6. Now, for changing the look of our application, you have two options:

    1. Use the command line: execute the python file adding the option, –style:

      python main.py --style material
      
    2. 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, pyside2-rcc style.qrc > style_rc.py And finally import it from your main.py script.

    1import sys
    2from os.path import abspath, dirname, join
    3
    4from PySide2.QtCore import QObject, Slot
    5from PySide2.QtGui import QGuiApplication
    6from PySide2.QtQml import QQmlApplicationEngine
    7
    8from style_rc import *
    

    You can read more about this configuration file here.

    The final look of your application will be:

    ../../_images/textproperties_material.png

You can view.qml and main.py to try this example.