为什么动态由C++生成的QML项目不知道其他预先存在的项目?

gre*_*fab 3 qt qml qtquick2

我有一个QQuickView显示QML文件,它本身由几个QML项目(在单独的文件中)组成.我想使用C++代码动态添加Items.动态添加的项目应与父一个调整大小,即widthheight属性参考的父.

例如,我在QML中的目标Item看起来像这样:

// TargetContainer.qml

Grid {
    id: fillMeWithItemsContainer
    objectName: "fillMeWithItemsContainer"
}
Run Code Online (Sandbox Code Playgroud)

我想动态添加的项目(可能是多次)看起来像这样:

// DynamicItem.qml

Rectangle {
    color: "white"

    height: fillMeWithItemsContainer.height
    width: height * 4/3
}
Run Code Online (Sandbox Code Playgroud)

请注意,矩形引用了有关高度的容器.

quickView填充有TargetContainer:

QQuickView *quickView = new QQuickView();
quickView->setSource(QUrl("qrc:/foo/bar/TargetContainer.qml"));
Run Code Online (Sandbox Code Playgroud)

所以我加载了一个组件

QQmlComponent dynamicallyLoadedComponent(
                quickView->engine(),
                QUrl("qrc:/foo/bar/DynamicItem.qml")
                );
Run Code Online (Sandbox Code Playgroud)

我创建了一个Object.

QObject *dynamicallyLoadedObject = dynamicallyLoadedComponent.create();
Run Code Online (Sandbox Code Playgroud)

这里我得到一个错误(在应用程序输出视图中):

DynamicItem.qml:4: ReferenceError: fillMeWithItemsContainer is not defined
Run Code Online (Sandbox Code Playgroud)

quickView应该知道fillMeWithItemsContainer它的存在,因为它之前已经被创造过了.但是,fillMeWithItemsContainer不是dynamicallyLoadedObject(还)的父母,这可能是问题所在.

所以我找到了目标项目

QQuickItem *targetItem = quickView->rootObject()->findChild<QQuickItem*>("fillMeWithItemsContainer");
Run Code Online (Sandbox Code Playgroud)

并重新显示以前创建的对象

dynamicallyLoadedObject->setProperty("parent", QVariant::fromValue<QObject*>(targetItem ));
Run Code Online (Sandbox Code Playgroud)

注意:我dynamicallyLoadedObject->setParent()之前尝试过,但这似乎是一种不同的父类(QObject与父属性相对).

但是,宽度和高度属性dynamicallyLoadedObject设置为0(因为参考错误,我假设)并且不会更改.即使我以编程方式再次设置它们

dynamicallyLoadedObject->setProperty("height", "fillMeWithItemsContainer.height;");
dynamicallyLoadedObject->setProperty("width", "height * 4/3");
Run Code Online (Sandbox Code Playgroud)

没有什么变化.

如果我直接在QML中定义DynamicItem,它可以工作:

Grid {
    id: fillMeWithItemsContainer
    objectName: "fillMeWithItemsContainer"

    DynamicItem {}
}
Run Code Online (Sandbox Code Playgroud)

如何确保动态添加的项目可以访问以前在QML视图中的项目?或者:我做错了什么?

Tho*_*ire 5

dynamicallyLoadedObject->setProperty("height", "fillMeWithItemsContainer.height;");
dynamicallyLoadedObject->setProperty("width", "height * 4/3");
Run Code Online (Sandbox Code Playgroud)

这实际上不会在属性上设置JavaScript绑定.相反,它会尝试将字符串分配给"fillMeWithItemsContainer.height;"属性,这将失败,因为属性是类型int,而不是类型QString.在C++中实际上不可能为属性分配绑定(有一些例外QQmlBinding).

dynamicallyLoadedObject->setProperty("parent", QVariant::fromValue<QObject*>(targetItem ));
Run Code Online (Sandbox Code Playgroud)

正如谢尔盖所​​说,你需要打电话QQuickItem::setParentItem而不是设置parent财产.这也比一般的基于字符串的setPropertyAPI 更安全.如果没有父项,则QQuickItem将不可见.重新显示只会更改父项,这将影响布局和其他一些事项.它不会改变对象的上下文.上下文定义了范围内的哪些对象/ ID.创建项目后,无法更改上下文.即使更改父级将更改上下文,也为时已晚 - 已创建对象,并且仅在创建阶段查找ID /对象.

解决方案是将正确的上下文传递给QQmlComponent::create()实际具有可选参数的上下文.你需要在上下文中创建你的项目fillMeWithItemsContainer,所以你需要获得一个指向它的指针(你已经这样做了findChild),然后检索它的上下文,这是可能的QQmlEngine::contextForObject().这应该足以让你弄明白如何让它发挥作用.

我同意谢尔盖,你应该更喜欢用JavaScript动态创建对象.在C++中更改QML是一种分层违规,你永远不应该从C++访问QML,只是相反,才能在UI和程序逻辑之间实现更好的分离.