Python 如何在PySide中模块化属性创建

Python 如何在PySide中模块化属性创建,python,qt,qml,pyside2,Python,Qt,Qml,Pyside2,我在很大程度上遵循使用属性的说明,我可以使用那里给出的Person对象作为后端,但这不是很有用。我正试图找出如何做以下两件事: 在后端使用多个此类类的多个实例,并以PySide/QML不会抱怨的方式连接它们 允许在运行时根据要确定的模块定制后端(即,我最终希望将应用程序组件化-使用不同的组件实现一个接口,该组件分别对GUI和后端做出贡献;但这个问题只涉及后端) 这与简单地在主后端类上定义所有这些属性以及它们的setter和getter(我能够做到)形成对比,这就是我在问题中所说的模块化 我从链接

我在很大程度上遵循使用属性的说明,我可以使用那里给出的Person对象作为后端,但这不是很有用。我正试图找出如何做以下两件事:

  • 在后端使用多个此类类的多个实例,并以PySide/QML不会抱怨的方式连接它们
  • 允许在运行时根据要确定的模块定制后端(即,我最终希望将应用程序组件化-使用不同的组件实现一个接口,该组件分别对GUI和后端做出贡献;但这个问题只涉及后端)
  • 这与简单地在主后端类上定义所有这些属性以及它们的setter和getter(我能够做到)形成对比,这就是我在问题中所说的模块化

    我从链接中修改了Person示例,使其成为UI可以更改的内容,并为其提供一个额外的kicks属性

    person.py

    作为示例,我将创建两个Person实例。我作为类成员创建的第一个实例。这不是我真正想要的,但更接近于链接中使用属性的方式。第二个实例是我真正想要的,即属性是实例成员,因此我可以在运行时从应用程序的其他位置添加它们。目前这两种方法都不起作用

    main.py

    最后,简单介绍了view3.qml文件

    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
                }
            }
        }
    }
    
    当我尝试在UI中设置任何值时,错误总是相同的(错误出现在QML文件中)


    最终,我希望将这些对象嵌套到任意深度。有没有办法实现我在这里的目标?或者我可能完全偏离了我的设置方式?

    我不知道我是否有资格就GUI应用程序的总体架构设计向您提供建议,但我想我可以解释出哪里出了问题,并建议一种方法来完成您描述的工作。您的
    registerProperty
    方法添加了一个Python属性,但正如您所看到的,这并不能使它从QML中可见

    坏消息:Qt属性在对象创建后无法添加到对象。
    好消息:您可以创建一个Qt属性,它是一个列表(或字典),并添加到其中

    要注意的一个陷阱是,要向QML公开列表,您需要将其类型指定为
    'QVariantList'
    。(对于字典,请使用
    'QVariantMap'
    ,并确保键是字符串。)

    下面是一个后端类的外观示例。(使用
    super()
    访问父类意味着不必将
    self
    传递给其初始值设定项。)

    从Pyside2.QtCore导入QObject、属性、信号
    类后端(QObject):
    定义初始化(自):
    super()。\uuuu init\uuuuu()
    self.people=[]
    人员变更=信号('QVariantList')
    @属性('QVariantList',notify=people\u changed)
    def人员(自我):
    回归自我
    @价值设定者
    def人员(自我、新员工):
    self.\u people=新人
    self.people\u changed.emit(新的人)
    def add_人员(本人、姓名、年龄):
    self.people.append(Person(姓名、年龄、self))
    #注意:只有分配才会自动触发更改的
    #信号。如果你附加,你必须自己发射它。
    self.people\u changed.emit(self.people)
    
    这将使QML在您添加人员时保持最新;您还可以创建类似的方法来删除人员。将每个人作为后端对象的子对象可以确保只要后端仍然存在,Qt就会保留对他们的引用

    对于一个真正的动态属性集合,也许您可以为顶级后端对象提供一个字典,供其他组件添加到其中。因此,
    backend.people
    将成为
    backend.properties['people']
    ,一个特定的模块将负责将该键添加到
    properties
    字典中,然后向其中添加和删除条目


    指定所有这些getter和setter都很麻烦,不是吗?我花了很长时间觉得一定有更好的方法,直到最近才在堆栈溢出问题上找到了解决方案。使用,您的Person类和我在上面发布的后端可以简化为:

    从PySide2.QtCore导入QObject
    #假设将链接代码另存为properties.py:
    从属性导入属性数据,属性
    类Person(QObject,元类=PropertyMeta):
    名称=属性(str)
    年龄=财产(整数)
    定义初始(自我、姓名、年龄、父母=无):
    super()。\uuuu init\uuuu(父级)
    self.name=名称
    self.age=年龄
    类后端(QObject,元类=PropertyMeta):
    人员=财产(列表)
    定义初始化(自):
    super()。\uuuu init\uuuuu()
    self.people=[]
    def add_人员(本人、姓名、年龄):
    self.people.append(Person(姓名、年龄、self))
    #自动发出更改的信号,即使是就地更改
    

    (我也把
    age
    改为
    int
    ,但如果你需要的话,它仍然可以是
    str

    我不知道我是否有资格就GUI应用程序的总体架构设计向你提供建议,但我想我可以解释出哪里出了问题,并建议一种方法来完成你描述的工作。您的
    registerProperty
    方法添加了一个Python属性,但正如您所看到的,这并不能使它从QML中可见

    坏消息:Qt属性在对象创建后无法添加到对象。
    好消息:您可以创建一个Qt属性,它是一个列表(或字典),并添加到其中

    要注意的一个陷阱是,要向QML公开列表,您需要将其类型指定为
    'QVariantList'
    。(对于字典,请使用
    'QVariantMap'
    ,并确保键是字符串。)

    这是考试
    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_())
    
    
    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
                }
            }
        }
    }
    
    TypeError: Value is undefined and could not be converted to an object