Adriano Leal
Adriano Leal

Reputation: 326

How to insert ListElement in a QML ListModel using C++?

Well, i'm learning to work with QML and i have one doubt. In my example, i have a ListModel with ListElements at QML and i have a QML main file with a rectangle, PathView etc.

I have a QWidget also, than is my main window. In this QWidget i include the QML UI like a component. Ok!

How can I handle QML ListElements by using C++?
Note: when I say "to handle", I want to say include an element for example.

Below are some parts of my code...

QML containing my ListElement, called "Menu1":

import QtQuick 1.0

ListModel {
    id: listMovieModel
    ListElement { name: "Image 1"; iconSource: "pics/image_1.jpg" }
    ListElement { name: "Image 2"; iconSource: "pics/image_2.jpg" }
    ListElement { name: "Image 3"; iconSource: "pics/image_3.jpg" }
    ListElement { name: "Image 4"; iconSource: "pics/image_4.jpg" }
    ListElement { name: "Image 5"; iconSource: "pics/image_5.jpg" }
    ListElement { name: "Image 6"; iconSource: "pics/image_6.jpg" }
}

My main QML:

Rectangle {
    width: 500
    height: 600
    color: "transparent"

    PathView {
        id: view
        focus: true
        width: parent.width
        height: parent.height + y
        y: -150
        model: Menu1 {} //First QML showed
        delegate: Image {
            source: iconSource
            width: 64
            height: 90
            scale: PathView.isCurrentItem ? 3.5 * y / parent.height : 2.0 * y / parent.height
            z: y
            smooth: true
        }
        path: MyGeometricFigure { //This a another file, but is confidential
            width: view.width
            height: view.height
        }
        preferredHighlightBegin: 0
        preferredHighlightEnd: 0
        highlightRangeMode: PathView.StrictlyEnforceRange
        Keys.onLeftPressed: decrementCurrentIndex()
        Keys.onRightPressed: incrementCurrentIndex()
    }
}

And as I use QML like a component for my QWidget:

MainForm::MainForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainForm)
{
    ui->setupUi(this);
    this->resize(1024, 576);

    QDeclarativeView *myQMLTest = new QDeclarativeView(QUrl::fromLocalFile("myMainQML.qml"));
    myQMLTest->setStyleSheet(QString("background: transparent; width: 600px"));

    this->ui->frameListVideoGallery->layout()->addWidget(myQMLTest);
    myQMLTest->setFocus();
    myQMLTest->installEventFilter(this);
}

I saw some articles about this, but I am not able to change my LisModel using C++. I saw here http://doc.qt.nokia.com/4.7/qdeclarativemodels.html#c-data-models and here in examples using PathView http://doc.qt.nokia.com/4.7/qdeclarativeexamples.html

Can someone help me?

Thanks!

Upvotes: 6

Views: 17991

Answers (3)

Aquarius_Girl
Aquarius_Girl

Reputation: 22906

Here is one more complete example of filling data in QML TableView from C++. Please pardon the bad variable names. I was making it for testing purpose only.

I have used QVariantList and filled it with QVariantMap objects.


Here, multiple data has been filled with a for loop in two columns of the QML's table, namely what and session_name by putting them in a QVariantMap and then inserting that QVariantMap in QVariantList.

HH.h

#ifndef HH_H
#define HH_H

#include <QVariant>

class AA : public QObject
{
    Q_OBJECT
    QVariantList m_gname;

public:
    Q_PROPERTY(QVariantList gname READ gname WRITE setGname NOTIFY gnameChanged)

    AA()
    {
        QVariantList q;

        for(int i = 0; i < 10; i++)
        {
            QVariantMap p;
            p.insert("what", "qq");
            p.insert("session_name", "aa");

            q.push_back(p);
        }
        setGname(q);
    }

    QVariantList gname() const
    {
        return m_gname;
    }

public slots:

    void setGname(QVariantList gname)
    {
        if (m_gname == gname)
            return;

        m_gname = gname;
        emit gnameChanged(m_gname);
    }

signals:
    void gnameChanged(QVariantList gname);
};


#endif // HH_H

Here I have use qmlRegisterType to register the class AA which I created above.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "HH.h"
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterType<AA>("ZZZ", 1, 0, "AA");


    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

In QML I have created a TableView by QtQuick.Controls 1.4. It has two columns namely what and session_name. I have created an object of the class AA here and on its Component.onCompleted called <model_id> dot append( <QVariantList_name> )

like this:

mymodel.append( gname )

main.qml

import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 1.4 as OldControls

import ZZZ 1.0

Window 
{
    visible: true
    width: 1000
    height: 1000
    title: qsTr("Hello World")

    AA
    {
        Component.onCompleted:
        {
            mymodel.append(gname)
        }
    }


    Rectangle
    {
        id: head
        anchors.centerIn: parent; height:  500; width:  500; border.color: "red"

            OldControls.TableView
            {
                anchors.fill: parent
                verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
                horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOn

                OldControls.TableViewColumn
                {
                    role: "what"
                    title: "What"
                }
                OldControls.TableViewColumn
                {
                    role: "session_name"
                    title: "Session Name"
                }
                model: ListModel
                        {
                            id: mymodel
                        }
            }
     }
}

Upvotes: 0

meolic
meolic

Reputation: 1207

I want to make the answer from @GooRoo more complete/usefull to Qt beginners. If you use the Qt Creator, you will start with a template using QmlApplicationViewer. To apply the answer from GooRoo you have to do something like this (thanks to http://www.lothlorien.com/kf6gpe/?p=309):

CPP:

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));
    QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create());

    viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer->setMainQmlFile(QLatin1String("qml/mydemo/main.qml"));
    viewer->showExpanded();

    QMetaObject::invokeMethod(
        (QObject *)viewer->rootObject()->findChild<QObject *>("myModel"), // QML item
        "test"                           // name of the method to call
                                         // without parameters
    );

    return app->exec();
}

QML:

PageStackWindow {
    id: mainApp
    ...

    ListModel {
        id: myModel
        objectName: myModel //THIS IS NEEDED BY findChild()
        ...
        function test() {
            console.log("TEST OK");
        }
    }

    Page {
        ...
    }
}

Upvotes: 2

GooRoo
GooRoo

Reputation: 661

Alright. I think you can do something like this:

In your main QML-file add simple function.

Rectangle {
    // ...

    function append(newElement) {
        view.model.append(newElement)
    }

    PathView {
        id: view

        // ...

        model: Menu1 {} //First QML showed

        // ...
    }
}

This method will just call a corresponding method from ListModel. More supported methods you can find there.

Then all you need is to call this method from C++ code. You can do this in such manner:

MainForm::MainForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainForm)
{
    ui->setupUi(this);
    this->resize(1024, 576);

    QDeclarativeView *myQMLTest = new QDeclarativeView(QUrl::fromLocalFile    ("myMainQML.qml"));
    myQMLTest->setStyleSheet(QString("background: transparent; width: 600px"));

    QVariantMap newElement;  // QVariantMap will implicitly translates into JS-object
    newElement.insert("name",       "Image 13"         );
    newElement.insert("iconSource", "pics/image_13.jpg");

    QMetaObject::invokeMethod(
        myQMLTest->rootObject(),                          // for this object we will call method
        "append",                                         // actually, name of the method to call
        Q_ARG(QVariant, QVariant::fromValue(newElement))  // method parameter
    );

    this->ui->frameListVideoGallery->layout()->addWidget(myQMLTest);
    myQMLTest->setFocus();
    myQMLTest->installEventFilter(this);
}

You should check this for more information.

Also you can choose another approach: to pass some data via qml properties (using QDeclarativeEngine and QDeclarativeContext) and then handle this data and your list-model right in JavaScript code.

Upvotes: 9

Related Questions