在没有 ObjectName 的情况下从 C++ 访问 QML 子组件?

Access QML Child Component from C++ without ObjectName?

我正在 QML 中创建绘图组件。我的 QML 组件的结构如下所示:

Rectangle {
  id: canvas
  objectName: "myPlot"
  Rectangle {
    id: plot
    PlotArea {
      id: pa
    } // this is my c++ QQuickItem
    MouseArea {} // for handling interaction with the plot, like zooming
    XAxis{}
    YAXis{}
  }
}

这里PlotArea是我的c++class。我需要从 C++ 与这个 QML 组件交互,更准确地说,我需要调用 PlotArea 的成员函数来向绘图添加数据。通常的方法是使用 findChild<QObject*>("objectName"),但由于组件将被重用,我不能给出 PlotArea 和对象名称。

如果我有指向 "myPlot" 的指针,我如何访问 PlotArea?我试过了

QObject *plot = rootObjects.value(0)->findChild<QObject*>("plotObject");
PlotArea * myplot = (plot->findChild<PlotArea*>("pa"));

但这不起作用。但是,如果我这样做,上面的方法是有效的

PlotArea {
  id: pa
  objectName: "pa"
} // this is my c++ QQuickItem

但我想知道这是否安全,因为我的应用程序中将有多个 PlotAreas,并且它们都具有名称 "pa"。

除非您正在编写单元测试,否则不应从 C++ 与 QML 交互。有关解释,请参阅 this documentation

更好的方法是将 C++ 类型公开给 QML。

单例

例如,您可以将添加绘图数据的 C++ class 注册为 singleton:

qmlRegisterSingletonType<PlotDataAdder>("App", 1, 0, "PlotDataAdder", plotDataAdderCreationFunction);

那么你可以在 QML 中这样做:

import App 1.0

PlotArea {
    id: plotArea
    Component.onCompleted: PlotDataAdder.addData(plotArea)
}

这是命令式方法。

命名类型

如果您的情况可行,您可以使用 qmlRegisterType():

以声明方式进行
qmlRegisterType<PlotDataSource>("App", 1, 0, "PlotDataSource");

然后,在 QML 中:

import App 1.0

Window {
    PlotDataSource {
        id: plotDataSource
    }

    PlotArea {
        id: plotArea
        plotData: plotDataSource
    }
}

你说绘图数据是由硬件动态生成的,所以这种方法适用于那种情况,而使用单例的第一种方法会更困难。

上下文 属性

或者,如果您无法创建 C++ class on-demand(例如,如果它来自第三方库),请使用 setContextProperty():

engine.rootContext()->setContextProperty("plotDataSource", plotDataSource);

然后,在 QML 中:

import App 1.0

Window {
    PlotArea {
        id: plotArea
        plotData: plotDataSource
    }
}

下面的代码只是问题的一种解决方案,但不是正确答案,因为它打乱了 QML 对象的所有权。 PlotAreaQML 引擎生成,但在 C++ 端使用。要正确解决问题,建议重新设计 PlotArea.

C++:

QObject* test = engine.rootObjects()[0]->findChild<QObject*>("test");
PlotArea* plotArea = test->property("plotArea").value<PlotArea*>();

QML:

Item{
    objectName: "test"
    property var plotArea : plotArea
    Item{
        PlotArea{
            id: plotArea
        }
    }
}