现在在我的C ++ / QML应用程序中,即使每个QML视图仅使用所有可用属性的子集,所有这些属性都通过QDeclarativeContext::setContextProperty
的根QDeclarativeEngine
中的QDeclarativeView
公开。 import QtQuick 1.0
Rectangle {
visible: true
width: 800
height: 600
Text {
text: message.text
anchors.centerIn: parent
}
}
显示QML文件 - 这有点难看。
应该只对子组件实例可用的附加数据添加到根上下文的子上下文中。
所以我想这样做。但是,我没有找到进一步的文档或信息如何实际使用子上下文。不幸的是,仅仅创建一个新的子上下文是不够的。
我有一个非常简单的QML文件
message
访问名为main
的上下文属性,该属性设置在int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget widget{nullptr, Qt::FramelessWindowHint};
widget.resize(800, 600);
MessageHolder message{"Hello World"};
// ceeate the new context and add the message to context properties
QDeclarativeContext message_context{view.engine()};
message_context.setContextProperty("message", &message);
// what needs to go here so that the new context is actually used?
view.setSource(QUrl{"path/to/main.qml"});
view.show();
widget.show();
return app.exec();
}
中QDeclarativeView引擎的根上下文的子上下文中:
MessageHolder
上面使用的class MessageHolder : public QObject {
Q_OBJECT
Q_PROPERTY(QString text READ text CONSTANT)
public:
MessageHolder(QString text);
QString text();
private:
QString _text;
};
是一个简单的类,除了提供在构造这样一个对象时设置的常量QString之外什么都不做:
message
如上所述,遗憾的是,这并不起作用,QML会发出警告
/path/to/main.qml:9:ReferenceError:无法找到变量:message`
如果我改为将viewmodel
属性添加到根上下文,一切正常。在创建上下文和设置视图源以使其工作之间我需要做什么?
在我的应用程序中,我使用MVVM架构,其中每个QML视图都有相应的C ++视图模型(在MVVM意义上而不是Qt术语)。
在任何给定时刻,许多不同的视图模型中只有一个是活动的,但是在应用程序启动期间它们都会被实例化,然后通过上下文属性公开给QML。
我遇到的核心问题是每个视图模型需要一个唯一的名称,因此也将QML视图强烈耦合到单个视图模型。拥有名为viewmodel
的单个通用上下文属性会更好。
但是,问题在于:当新的viewmodel变为活动状态时,我必须在更改QML视图之前或之后将Q_ASSERT
context属性重新设置为新的viewmodel。但是更改上下文属性会导致重新评估当前活动的所有绑定。因此,当我第一次更改context属性然后更改视图时,即使新的viewmodel没有所需的属性,旧视图也会尝试更新,如果我首先更改视图然后更改上下文属性view将在加载时评估其所有绑定,但由于context属性尚未更新,因此尚未提供必要的属性。
在视觉上这不是问题,因为在第一种情况下,现在无论如何都要卸载现在被破坏的视图,而在第二种情况下,一旦设置了新的上下文属性,新视图将立即重新评估所有属性。然而,在任何一种情况下,QML都会因为缺少属性而发出警告,这首先是不好的,因为它使日志文件混乱,其次因为我通过viewmodel
将警告视为调试版本中的致命错误,以便发现错误在我的持续集成系统的早期QML视图或视图模型中,无需人工检查。
但后来我偶然发现了Qt documentation中的以下事实:
虽然在上下文中实例化的QML对象并非由该上下文严格拥有,但它们的绑定是。如果上下文被破坏,未完成的QML对象的属性绑定将停止评估。
这正是我需要的,以阻止QML在垂死的视图中重新评估现在无效的绑定。因此,我想在子上下文中设置{{1}}上下文属性,只要视图发生变化,我就会销毁并重新创建。
答案 0 :(得分:1)
好吧,您可以将其添加到不是根对象的特定对象的上下文中,而不是将属性设置为根(对象)上下文。
您可以使用此静态方法获取任何对象的上下文:
auto ctx = QQmlEngine::contextForObject(someQObjectPtr);
每个QML对象都有它的上下文,显然如果你像在代码中那样创建了一些任意上下文,它与任何东西都不对应,可以理解它不会按预期工作。
虽然你想做的事听起来有些落后。
我要做的是拥有另一个模型或至少包含所有模型的基本列表,然后设置property model activeModel
并将其设置为我想要显示的模型,并使用activeModel
视图,因此更改活动模型将自动更新视图。如果您想更明确,可以在更改模型之前手动销毁视图。
此外,正如this recent question所示,对象破坏本身并不会立即发生在QML中,它会被延迟,并且仍然会导致对被认为不再相关的对象的绑定进行重新评估。所以你的原始计划甚至可能都不起作用,除了是一个你不应该在一开始就没有的设计问题的不必要的补丁。
总而言之,我不建议将上下文属性用于比将单个对象暴露给QML更动态的任何内容。当你有很多东西时,它只是不可扩展和可维护,每个东西都需要自己的代码行来注册和它自己的名字。
更新:
仅应对组件子集可用的其他数据 应将实例添加到根目录的子上下文中 上下文。
你错误地解释了这个。它并没有告诉你去手动创建自己的上下文。我不熟悉任何means to manually specify which context you want from QML,上下文是隐式的 - 您所在的上下文 - 当前对象的上下文,或者如果找不到该属性,则将上下文一直搜索到根目录上下文。它并不意味着您应该无所事事地创建上下文树,然后删除它们,在qml引擎下拉地毯。
因此,正如我的初步答案所述,如果您不想在根上下文中注册属性,则应选择分支的上下文。然后该属性仅对该子树中的对象可见。 然而这不会真正解决您遇到的问题,至少不能以合理,有效和优雅的方式解决。
解决方案冒着重复自己的风险,非常简单明了 - 折叠视图,更改模型,重建视图。没有必要尝试做一些你不了解的不可能的事情。销毁上下文的唯一方法是销毁其对象,当视图被销毁时,将没有对象用无效数据更新其绑定。粗略地从C ++中删除QML对象无助于您以任何方式避免QML的设计限制,它只会导致应用程序崩溃。
这个围绕generic object model构建的简单示例清楚地表明了那些"残余"绑定只是QML内部设计的产物,根据我的个人经验,它存在漏洞,漏洞和设计限制。我自己已经有了(相当不公平)的斗争和辛劳,但如果经验教会了我什么,那就是尝试解决它,因为有时候QML的工作方式与逻辑和理性无关,所以你的期望是多么合理和符合逻辑(如果在这个特定情况下完全无关)是完全无关紧要的。
List {
id: models
List {
property Component delegate : Rectangle {
width: 200
height: 50
color: object.apple
}
QtObject { property color apple: "green" }
QtObject { property color apple: "red" }
QtObject { property color apple: "yellow"}
}
List {
property Component delegate : Rectangle {
width: 200
height: 50
color: "red"
Text {
anchors.centerIn: parent
text: object.pepper
}
}
QtObject { property string pepper: "sweet" }
QtObject { property string pepper: "spycy" }
QtObject { property string pepper: "hot" }
}
}
Row {
ListView {
width: 200
height: 200
model: models
delegate: Rectangle {
width: 200
height: 50
color: "grey"
Text {
anchors.centerIn: parent
text: index
}
MouseArea {
anchors.fill: parent
onClicked: {
currentModel.model = null // remove line and suffer
currentModel.model = object
}
}
}
}
ListView {
id: currentModel
width: 200
height: 200
model: null
delegate: model ? model.delegate : null
}
}
实现是一个列表列表,或者是模型模型。模型列表中的每个模型都有自己的委托,以及具有自己的属性集的对象。有两个列表视图,第一个列出所有可用模型,第二个列出活动模型的内容(如果有)。如果没有currentModel.model = null
行,每次切换当前模型时,您都会收到有关未定义属性的控制台错误,因为这就是QML的工作原理。实际上非常幸运的是,在这种情况下,解决方案就像在切换到另一个之前null
模型一样简单,使用该行一切都很好,视图元素在模型切换之前被销毁,没有它项目会在一个事件循环周期中停留,足以产生错误,而那些不是来自当前列表视图项目,而是来自那些正在消失的项目,所以即使你delete
在C ++中的数据中,由列表视图管理的那些对象会徘徊不足以制造麻烦,而在delete
的情况下,它不会是控制台错误,而是硬应用程序崩溃。 / p>