将属性注入QML组件

时间:2018-03-21 22:40:59

标签: python pyqt qml pyqt5 qqmlcomponent

我对Qt / PyQt相当新,目前正在努力解决一些基本功能。我想要做的是,从python动态加载QML-Views(* .qml)文件并动态替换特定内容。例如,选中一个复选框,并将当前视图的一部分替换为另一个qml文件。首先我想通过PyQt提供这个逻辑,但似乎StackView是一个更好的主意(multiple qml files in pyqt)。

但是,在这种情况下,我无法将属性注入我的QML文件中。我只能将属性注入rootContext。然而,这限制了我的QML-Views的使用,因为我一次只能使用一个视图(相同类型)。我想动态地将属性注入QML视图,并使它们仅对此特定视图可见。在这种情况下,我可以在后端使用多个对象多次使用相同的视图来捕获信号。

这是我的SimpleMainWindow.qml文件(主视图:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Controls 1.4

ApplicationWindow {
    id: window
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    objectName : "WINDOW"
    property ApplicationWindow appWindow : window

}

这里我尝试加载的文件(TestViewButton.qml):

import QtQuick 2.9
import QtQuick.Controls 1.4

Button {
    id: test
    text: "test"
    objectName : "Button"
    onClicked: {
        configurator.sum(1, 2)
        configurator.info("TestOutput")
    }
}

Python文件加载QML-View(组件)

from PyQt5.QtCore import QObject, QUrl
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine, QQmlComponent

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

    engine.load("qml/SimpleMainWindow.qml")
    engine.quit.connect(app.quit)

    rootWindow = engine.rootObjects()[0].children()[0].parent()

    # create component
    component = QQmlComponent(engine)
    component.loadUrl(QUrl("qml/TestViewButton.qml"))

    configurator = SignalHandler()
    component.setProperty("configurator", configurator)

    itm = component.create()
    itm.setParentItem(rootWindow.children()[0])

    itm.setProperty("configurator", configurator)

    app.exec_()

我用来处理来自视图的信号的python对象(SignalHandler.py):

from PyQt5.QtCore import QObject, pyqtSlot

class SignalHandler(QObject):

    @pyqtSlot(int, int)
    def sum(self, arg1, arg2):
        print("Adding two numbers")

    @pyqtSlot(str)
    def info(self, arg1):
        print("STR " + arg1)

按钮加载正常(顺便说一下,有没有更好的方法来识别我想要添加我的按钮的父级,是不是看了findChild)。什么是无效的是component.setProperty ....部分。如果我在引擎的rootContext中设置属性,它可以正常工作(调用SignalHandler方法)。已经检查过类似的主题(例如Load a qml component from another qml file dynamically and access that component's qml type properties ...)

这可能,或者我在这里弄错了吗?

感谢

1 个答案:

答案 0 :(得分:2)

根据我的理解,您只想在TestViewButton.qml中加载配置对象,而SimpleMainWindow.qml中不会显示该配置对象。

要执行此操作,TestViewButton.qml在加载时必须有自己的QQmlContext且不是rootContext()

为了测试我的响应并观察该行为,我们将创建一个尝试使用configurator的类似按钮,如果按下此按钮,则应该抛出错误,注意该属性不存在但是如果加载的按钮是由QQmlComponent按下应该正常工作。

<强> QML / SimpleMainWindow.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Controls 1.4

ApplicationWindow {
    id: window
    visible: true
    width: 640
    color: "red"
    height: 480
    title: qsTr("Hello World")

    Button {
        x: 100
        y: 100
        text: "ApplicationWindow"
        onClicked: {
            console.log("ApplicationWindow")
            configurator.sum(1, 2)
            configurator.info("TestOutput")
        }
    }
}

正如我之前评论的那样,我添加了一个带有新上下文的组件:

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

    configurator = SignalHandler()

    engine.load("qml/SimpleMainWindow.qml")
    engine.quit.connect(app.quit)

    rootWindow = engine.rootObjects()[0]
    content_item = rootWindow.property("contentItem")

    context = QQmlContext(engine)
    component = QQmlComponent(engine)
    component.loadUrl(QUrl("qml/TestViewButton.qml"))
    itm = component.create(context)
    context.setContextProperty("configurator", configurator)
    itm.setProperty("parent", content_item)

    sys.exit(app.exec_())

最后我们得到以下输出:

qml: Component
Adding two numbers
STR TestOutput
qml: ApplicationWindow
file:///xxx/qml/SimpleMainWindow.qml:20: ReferenceError: configurator is not defined
qml: Component
Adding two numbers
STR TestOutput
qml: ApplicationWindow
file:///xxx/qml/SimpleMainWindow.qml:20: ReferenceError: configurator is not defined

我们观察到期望的行为。完整示例可在以下link中找到。