将属性注入 QML 组件
Posted
技术标签:
【中文标题】将属性注入 QML 组件【英文标题】:Injecting Properties into QML-Component 【发布时间】:2018-03-21 22:40:59 【问题描述】:我对 Qt/PyQt 还很陌生,目前正在为一些基本功能而苦苦挣扎。我想要做的是,从 python 动态加载 QML-Views (*.qml) 文件并动态替换特定内容。例如,一个复选框被选中,我当前视图的一部分被另一个 qml 文件替换。首先我想通过 PyQt 提供这个逻辑,但 StackView 似乎是一个更好的主意 (multiple qml files in pyqt)。
但是,在这种情况下,我无法将属性注入到我的 QML 文件中。我只能将一个属性注入到 rootContext 中。然而,这限制了我的 QML 视图的使用,因为我一次只能使用一个视图(相同类型)。我想将属性动态注入到 QML-Views 中,并使它们只对这个特定的视图可见。在这种情况下,我可以在后端使用多个对象多次使用同一个视图来捕获信号。
这是我的 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 ...)
这可能吗,还是我这里有什么问题?
谢谢
【问题讨论】:
什么是 SignalHandler? 我也添加了 SignlaHandler 代码。它只是一个简单的 QObject,用于处理来自视图的一些调用。 【参考方案1】:据我了解,您只想在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找到。
【讨论】:
非常感谢。这正是我正在寻找的。另一个问题:让 python 加载 QML-Views 是一种好习惯(我要使用一种 MVC/MVVM 模式),还是应该以某种方式将它委托给 QML。有没有更简单的方法来识别我认为的元素? (目前使用 objectName 属性和 findChild-Method)。再次感谢 @spooky 实际上不推荐从 python/C++ 创建组件,而是创建新类型的项目或属性。例如,在python/C++中创建业务逻辑并注入到QML中,让GUI的主要任务去做QML。另一个问题,通过 objectName 和 findChildren 访问是访问元素的最佳方式。 我可能在这里遗漏了一些东西。我已经看到我可以注册自己的类型(通过 qmlRegisterType),如果我使用它,我的 QObject 的新对象(使用 pyqtProperty)就会被创建。但是,我的业务逻辑仍应决定创建哪个(以及何时)特定视图。然后我仍然需要从 python 加载 qml 文件或类型。也许你有一个链接/教程,我可以看看? 我建议您不要将 .py 与 .qml 混合太多,创建一个用 python 制作的对象,但管理 QML 中的可见性。我的意思是尽量在 QML 中做大部分事情,如果你不能在 .py 中做。要加载新组件,有 Loaders 或 Component。 QML 是准备做大部分事情的,实际上很多 QML 的东西都是用 python 写的。以上是关于将属性注入 QML 组件的主要内容,如果未能解决你的问题,请参考以下文章
27.Qt Quick QML-StateTransition
如何使用 Angular 中的依赖注入将属性指令实例传递给嵌套组件
是否可以将服务注入 Angular 1.5 组件的 templateUrl 属性?