numberCruncher
numberCruncher

Reputation: 645

Access QML Child Component from C++ without ObjectName?

I am creating a plotting component in QML. The structure of my QML component looks like this:

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{}
  }
}

here PlotArea is my c++ class. I need to interact with this QML component from C++, more precisely, I need to call a member function of PlotArea to add data to the plot. The usual way is to use findChild<QObject*>("objectName"), but as the component will get reused, I cannot give PlotArea and object name.

How can I access PlotArea if I have a pointer to "myPlot"? I tried

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

but this does not work. However, what works is the above approach if I do

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

But I wonder if this is safe, as there will be serveal PlotAreas in my application, and all of them have the name "pa".

Upvotes: 0

Views: 1372

Answers (2)

JustWe
JustWe

Reputation: 4484

The code in below only one solution to the problem, but not the correct answer because it messes up the ownership of QML objects. The PlotArea generate from QML engine but using in the C++ side. To correctly solving the problem recommend redesigning the 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
        }
    }
}

Upvotes: 1

Mitch
Mitch

Reputation: 24416

You shouldn't interact with QML from C++ unless you're writing unit tests. See this documentation for an explanation.

The better approach is to expose a C++ type to QML.

Singleton

For example, you could register the C++ class that adds the plot data as a singleton:

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

Then you could do this in QML:

import App 1.0

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

This is an imperative approach.

Named Type

If it's possible in your situation, you could do it declaratively instead with qmlRegisterType():

qmlRegisterType<PlotDataSource>("App", 1, 0, "PlotDataSource");

Then, in QML:

import App 1.0

Window {
    PlotDataSource {
        id: plotDataSource
    }

    PlotArea {
        id: plotArea
        plotData: plotDataSource
    }
}

You said the plot data is dynamically generated by hardware, so this approach would work in that case, whereas the first approach using singletons would be more difficult.

Context Property

Or, if you can't create the C++ class on-demand (if it comes from a third party library, for example), use setContextProperty():

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

Then, in QML:

import App 1.0

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

Upvotes: 5

Related Questions