Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Copying C++ Object Data to QML Object (Example: Q3DScatter to Scatter3D)



  • Hello all,

    I was working on an app that uses Q3DScatter visualization but want to duplicate it as a Scatter 3D QML object because I require a legend, which is not easy to do with Q3DScatter since in order to display it, an embedded window must be used, and this window stays on top of everything.

    It might be too much to ask, but I was wondering if there was an easy way to copy all the data from my Q3DScatter graph C++ object to a Scatter3D QML graph object, such that any changes to the C++ graph are reflected/mirrored by the QML graph?

    I'm hoping for something like:

    Scatter3D {
       this = C_Graph; //name of C graph object via QQmlContext->setContextProperty()
    }
    

    I've tried looking for an example and could not find anything so I have been doing it the long way and have a QQuickWidget in my UI loaded with an external QML file, like so:

    Scatter3D {
                id: qml_graph
                objectName: "qml_graph"
     ...
                axisX: C_Graph.axisX
                axisY: C_Graph.axisY
                axisZ: C_Graph.axisZ
    }
    

    I copy the series from the C++ object graph like so:

    void duplicateGraph(Q3DScatter& c_graph, QObject* qml_graph)
    {
        for(QScatter3DSeries* c_series : c_graph.seriesList())
        {
           QScatter3DSeries* qml_series = new QScatter3DSeries;
           //copy series meshes, colors etc from c_series to qml_series
           QMetaObject::invokeMethod(qml_graph, "addSeries", Q_ARG(QScatter3DSeries*, qml_series));
        }
    }
    

    Note: I do not copy the series pointers directly as it was causing display issues.

    I almost have the QML graph completely mirroring the C++ graph, however I have run into an issue: I have to change the min and max values of each axis since the data changes depending on the data I'm feeding the graph. The C++ graph object is fine with this, however if I have data loaded into the QML graph and change the axis min/max values, the program crashes with a segmentation fault. This only happens if there is data loaded into the QML graph. I'm guessing it has something to do with the property bindings.

    Something similar was happening with the C++ graph and the solution was to empty the graph (remove all series) before loading any new series or data. I can't seem to find a way to do this with QML graph. I know I have to use invokeMethod with "removeSeries" but I can not access seriesList property from the QML graph in order to use a loop.

    I've tried returning the list:

    //QML
    function getSeriesList() {
           console.log("seriesList length:", qml_graph.seriesList.length);
           return  qml_graph.seriesList;
    }
    
    //C++
    QList<QScatter3DSeries*> qml_series_list;
    QMetaObject::invokeMethod(qml_graph, "getSeriesListLength", Q_RETURN_ARG(Qlist<QScatter3DSeries*>, qml_series_list));
    

    But it returns false and qml_series_list is not populated. I can't even return the size of the list because invokeMethod will return false always. The only way i does anything is if I use invokeMethod without Q_RETURN_ARG but that will of course only print out the list length in the console. I've tried all connection flags as well.

    I am new to using QML so forgive me if I am missing something fundamental. Should I create a function in the QML code to empty the graph instead? Why won't invokeMethod work? Or how else could I access seriesList? So I decouple the axis of the C++ graph and QML graph?

    Any advice is appreciated!