I am largely following the instructions here for using properties and I could just use the Person object given there as the backend, but that is not very useful. I am trying to figure out how to do the following two things:
- Use multiple instances of multiple such classes in a backend and connect them in a way that PySide/QML doesn't complain
- Allow customization of the backend by modules to be determined at runtime (i.e. I eventually want to componentize the application - have different components implementing an interface, the component contributes separately to both the GUI and the backend; but this question concerns only the backend)
This is in contrast to simply defining all of these Properties along with their setter and getter on the main backend class (which I was able to do), which is what I mean by modularize in the question.
I modify the Person example from the link to make it something the UI can change and give it an extra attribute for kicks...
person.py
from PySide2.QtCore import QObject, Signal, Property
class Person(QObject):
def __init__(self, name, age):
QObject.__init__(self)
self._name = name
self._age = age
def getName(self):
return self._name
def setName(self, name):
print(f"Setting name to {name}")
self._name = name
def getAge(self):
return self._age
def setAge(self, age):
print(f"Setting age to {age}")
self._age = age
@Signal
def name_changed(self):
pass
@Signal
def age_changed(self):
pass
name = Property(str, getName, setName, notify=name_changed)
age = Property(str, getAge, setAge, notify=age_changed)
Just as an example I'll create two instances of Person. The first instance I have created as a class member. This is not really what I want, but closer resembles the way properties were used in the link. The second instance is what I really want which is that the properties are instance members, so that I can add them from elsewhere in the application at runtime. Neither method currently works
main.py
import sys
from os.path import abspath, dirname, join
from PySide2.QtCore import QObject, Property, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from person import Person
class Backend(QObject):
def __init__(self):
QObject.__init__(self)
def registerProperty(self, name : str, prop):
setattr(self, name, prop)
person1 = Person("Jill", 29)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
context = engine.rootContext()
# Instance of the Python object
backend = Backend()
# simulate properties added by another module
backend.registerProperty("person2", Person("Jack", 30))
qmlFile = join(dirname(__file__), 'view3.qml')
engine.load(abspath(qmlFile))
# Expose the Python object to QML
context.setContextProperty("backend", backend)
# I tried this but it did nothing
# context.setContextProperty("backend.jack", backend.jack)
# context.setContextProperty("backend.jill", backend.jill)
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
finally the view3.qml file is simply
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
ApplicationWindow {
visible: true
ColumnLayout {
TextField {
implicitWidth: 200
onAccepted: {
backend.person1.name = text
}
}
TextField {
implicitWidth: 200
onAccepted: {
backend.person1.age = text
}
}
TextField {
implicitWidth: 200
onAccepted: {
backend.person2.name = text
}
}
TextField {
implicitWidth: 200
onAccepted: {
backend.person2.age = text
}
}
}
}
When I try to set any of the values in the UI the error is always the same (the error appears against the QML file)
TypeError: Value is undefined and could not be converted to an object
Ultimately I would like to have such objects nested to any arbitrary depth. Is there a way to achieve what I am trying to do here? Or am I maybe completely off track with the way I'm setting about this?