Search code examples
pythonqmlpyside6

Initialize and Customize QML Components with Python


Is there a way for me to treat QML components as objects, and initialize them in Python? For instance, here's a simplified QML for a box: I want to be able to replicate what a constructor method can do in Java. I want to be able to customize the text on each box through the Python script and also, at the same time, create multiple box instances that are separate from each other.

import QtQuick 2.0
import QtQuick.Controls 2.0

Item {
    id: boxItem
    width: 800
    height: 118

    Rectangle {

        id: boxRect
        height: 118
        color: "#55f555"
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: parent.top

        Text {
            id: textItem
            width: 463
            height: 43
            color: "#000000"
            text: qsTr("Header Text")
            anchors.left: parent.left
            anchors.top: parent.top
            font.pixelSize: 38
            verticalAlignment: Text.AlignVCenter
            font.family: "Roboto"
            textFormat: Text.AutoText
            anchors.leftMargin: 20
            anchors.topMargin: 20
        }
    }

}

This is my current Python script modified from Qt's template version:

import os
import sys
from pathlib import Path

import PySide6.QtQml
from PySide6.QtQuick import QQuickView
from PySide6.QtCore import Qt, QUrl
from PySide6.QtGui import QGuiApplication


if __name__ == '__main__':

    #Set up the application window
    app = QGuiApplication(sys.argv)
    view = QQuickView()
    view.setResizeMode(QQuickView.SizeRootObjectToView)

    #Load the QML file
    qml_file = Path(__file__).parent / "Main.qml"
    view.setSource(QUrl.fromLocalFile(os.fspath(qml_file.resolve())))

    #Show the window
    if view.status() == QQuickView.Error:
        sys.exit(-1)
    view.show()

    #execute and cleanup
    app.exec()
    del view

Quick clarification: I am working with custom built QML components, not trying to edit pre-existing ones made by QT.


Solution

  • Applying the concepts of another technology (programming language, library, framework) are often a bad approach to use some other technology. Each technology has its own methodology and good practices to implement any requirement.

    In the case of QML the other languages such as C++, python, etc. are generally considered to implement business logic (something like a backend) and QML takes care of the view and the operation. In this case, it is recommended to create a QObject that provides other QObjects that create, modify, etc. the data and this can be reflected in the view. In this particular case, for the requirement, it is enough to use a model and a custom qml Item.

    Box.qml

    import QtQuick 2.0
    import QtQuick.Controls 2.0
    
    Item {
        id: boxItem
        width: 800
        height: 118
    
        property alias text: textItem.text
    
        Rectangle {
    
            id: boxRect
            height: 118
            color: "#55f555"
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.top: parent.top
    
            Text {
                id: textItem
                width: 463
                height: 43
                color: "#000000"
                anchors.left: parent.left
                anchors.top: parent.top
                font.pixelSize: 38
                verticalAlignment: Text.AlignVCenter
                font.family: "Roboto"
                textFormat: Text.AutoText
                anchors.leftMargin: 20
                anchors.topMargin: 20
            }
        }
    }
    

    main.qml

    import QtQuick 2.0
    import QtQuick.Controls 2.0
    
    Item {
        id: root
        ScrollView 
        {
            anchors.fill: parent
            Column{
                Repeater{
                    model: manager.model
                    Box{
                        text: model.display
                    }
                }
            }
        }
    }
    

    main.py

    import os
    import sys
    from pathlib import Path
    
    
    from PySide6.QtCore import Property, QObject, Qt, QUrl
    from PySide6.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
    from PySide6.QtQuick import QQuickView
    
    
    class Manager(QObject):
        def __init__(self, parent=None):
            super().__init__(parent)
            self._model = QStandardItemModel()
    
        @Property(QObject, constant=True)
        def model(self):
            return self._model
    
    
    if __name__ == "__main__":
    
        # Set up the application window
        app = QGuiApplication(sys.argv)
        view = QQuickView()
        view.setResizeMode(QQuickView.SizeRootObjectToView)
    
        manager = Manager()
        view.rootContext().setContextProperty("manager", manager)
    
        qml_file = Path(__file__).parent / "main.qml"
        view.setSource(QUrl.fromLocalFile(os.fspath(qml_file.resolve())))
    
        if view.status() == QQuickView.Error:
            sys.exit(-1)
        view.resize(640, 480)
        view.show()
    
        for i in range(20):
            item = QStandardItem(f"item-{i}")
            manager.model.appendRow(item)
    
        app.exec()