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