0

I want to use Python to dynamically add custom components onto my view.qml, but I am not sure about my method because I can't see the Button.qml component in the resulting window. Ideally, I hope to be able to instantiate several rows of buttons into the ColumnLayout. By the way, Button.qml custom quick example/demo button whose's source code I have included too below. It is not QtQuick Button.qml from the PySide6 library

I thought I could just call functions from the view.qml but apparently not? I have seen another method that involves using a separate Javascript file, but I would like to avoid doing that if possible.

Main.py

import os
from pathlib import Path
import sys
from PySide6.QtCore import  QUrl, QObject
from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView

class CreateWidgets(QObject):
    
    def instantiate_widgets(self, root, widgetsNeeded):
        #for i in widgetsNeeded:
            root.doSomething
    
if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    view = QQuickView()
    view.setResizeMode(QQuickView.SizeRootObjectToView);

    qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
    view.setSource(QUrl.fromLocalFile(qml_file))
    if view.status() == QQuickView.Error:
        sys.exit(-1)
    
    root = view.rootObject()
    widgetCreator = CreateWidgets()
    widgetCreator.instantiate_widgets(root, 6)
    
    view.show()
    res = app.exec()
    # Deleting the view before it goes out of scope is required to make sure all child QML instances
    # are destroyed in the correct order.
    del view
    sys.exit(res)

view.qml

import QtQuick 2.0
import QtQuick.Layouts 1.12

Item{
    function doSomething(){
        var component = Qt.createComponent("Button.qml");
        if (component.status === Component.Ready) {
            var button = component.createObject(colLayout);
            button.color = "red";
        }
        console.log("Button created");
    }
    ColumnLayout{
        id: colLayout
        Rectangle {
            id: page           
            width: 500; height: 200
            color: "lightgray"  
         }   
    }
}

Button.qml

import QtQuick 2.0

Rectangle { width: 80; height: 50; color: "red"; anchors.fill: parent}

(Code reference for questions in comments section)

Main.py
import os
import random
import sys
from pathlib import Path

from PySide6.QtCore import Property, QUrl, QObject, Qt
from PySide6.QtGui import QColor, QGuiApplication, QStandardItem, QStandardItemModel
from PySide6.QtQuick import QQuickView

ColorRole = Qt.UserRole
BorderRole = Qt.UserRole

class Manager(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model = QStandardItemModel()
        self._model.setItemRoleNames({Qt.DisplayRole: b"display", ColorRole: b"custom", BorderRole: b"custom2"})

    @Property(QObject, constant=True)
    def model(self):
        return self._model

    def add_button(self, text, color, bColor):
        item = QStandardItem(text)
        item.setData(color, ColorRole)
        item.setData(bColor, BorderRole)
        self._model.appendRow(item)


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)

    manager = Manager()

    view = QQuickView()
    view.rootContext().setContextProperty("manager", manager)
    view.setResizeMode(QQuickView.SizeRootObjectToView)

    qml_file = os.fspath(Path(__file__).resolve().parent / "view.qml")
    view.setSource(QUrl.fromLocalFile(qml_file))
    if view.status() == QQuickView.Error:
        sys.exit(-1)

    for i in range(6):
        color = QColor(*random.sample(range(0, 255), 3))
        border = QColor(*random.sample(range(0, 255), 3))
        manager.add_button(f"button-{i}", color, border)

    view.show()
    res = app.exec()
    sys.exit(res)

View.qml

import QtQuick 2.0
import QtQuick.Layouts 1.12

Item {
    ColumnLayout {
        id: colLayout
        anchors.fill: parent
        Repeater{
            model: manager.model
            Button{
                color: model.custom
                text: model.display
                border.color: model.custom2
            }
        }
    }
}

Button.qml

import QtQuick 2.0

Rectangle {
    id: root

    property alias text: txt.text
    width: 80
    height: 50
    color: "red"
    border.color: "black"

    Text{
        id: txt
        anchors.centerIn: parent
    }
}

1 Answer 1

2

The idea is that Python (or C++) provide the information to QML to create the items for example using a model and a Repeater.

On the other hand, if an item is going to be a child of a ColumnLayout then it should not use anchors since there will be conflicts since they both handle the geometry of the item.

Considering the above I have added more elements such as variable text, variable color, etc. to demonstrate the logic.

import os
import random
import sys
from pathlib import Path

from PySide6.QtCore import Property, QUrl, QObject, Qt
from PySide6.QtGui import QColor, QGuiApplication, QStandardItem, QStandardItemModel
from PySide6.QtQuick import QQuickView

ColorRole = Qt.UserRole


class Manager(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model = QStandardItemModel()
        self._model.setItemRoleNames({Qt.DisplayRole: b"display", ColorRole: b"custom"})

    @Property(QObject, constant=True)
    def model(self):
        return self._model

    def add_button(self, text, color):
        item = QStandardItem(text)
        item.setData(color, ColorRole)
        self._model.appendRow(item)


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)

    manager = Manager()

    view = QQuickView()
    view.rootContext().setContextProperty("manager", manager)
    view.setResizeMode(QQuickView.SizeRootObjectToView)

    qml_file = os.fspath(Path(__file__).resolve().parent / "view.qml")
    view.setSource(QUrl.fromLocalFile(qml_file))
    if view.status() == QQuickView.Error:
        sys.exit(-1)

    for i in range(6):
        color = QColor(*random.sample(range(0, 255), 3))
        manager.add_button(f"button-{i}", color)

    view.show()
    res = app.exec()
    sys.exit(res)
import QtQuick 2.0
import QtQuick.Layouts 1.12

Item {
    ColumnLayout {
        id: colLayout
        anchors.fill: parent
        Repeater{
            model: manager.model
            Button{
                color: model.custom
                text: model.display
            }
        }
    }
}
import QtQuick 2.0

Rectangle {
    id: root

    property alias text: txt.text
    width: 80
    height: 50
    color: "red"

    Text{
        id: txt
        anchors.centerIn: parent
    }
}

Update:

Each role must have a different numerical value since otherwise Qt cannot identify it, in your case you could change:

BorderRole = Qt.UserRole + 1
Sign up to request clarification or add additional context in comments.

7 Comments

Thank you for the solution! Just a quick question, in this line ` self._model.setItemRoleNames({Qt.DisplayRole: b"display", ColorRole: b"custom"}) ` where are the display and custom properties coming from? Are these Qt defined?
@SCP3008 You are creating roles so they can be any value, I use a classic value: "display" and a custom one: "custom"
@ellyanesc Hi, another question, is it possible for me to use more than role, like more than one UserRole? It seems to not work if I have more than 1, and the documentation seems to imply that you can only use 1 of each type of data role. If there's another custom property in the Button for instance, can I still use model to set that somehow?
@SCP3008 You can use as many roles as you want, where in the docs does it indicate that you can only use a single role? in the example I use 2 roles: display and custom
@SCP3008 Check out my update. I do not get used to answering new questions from the OP since otherwise I would never finish answering since there could be thousands of questions. In this case I have taken an exception.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.