It's possible to call c++ function from qml and update a ListModel?



  • I'm trying to update my ListModel but it is impossible.

    If I set on my main.cpp

    QStringList dataList;
    dataList.append("Item 222");
    QQmlContext *ctxt = engine.rootContext();
    ctxt->setContextProperty("myModel",  QVariant::fromValue(dataList));
    

    It's working.

    But if a do the same on a excel.h doesn't work

    QStringList dataList;
    dataList.append("Item 222");
    QQmlApplicationEngine engine;
    QQmlContext *classContext = engine.rootContext();
    classContext->setContextProperty("myModel", QVariant::fromValue(dataList));
    

    It is not updating my combobox.

    I need to open a excel and set combobox items but I can't.

    Thanks and sorry for my bad english


  • Moderators

    This is described in depth in the documentation: link

    Quoting:

    Note: There is no way for the view to know that the contents of a QStringList have changed. If the QStringList changes, it will be necessary to reset the model by calling QQmlContext::setContextProperty() again.


  • Moderators

    Hm actually you are calling setContextProperty() again, so it should work. Are you sure you are updating correct model ("myModel" - is it correct)?



  • @sierdzio Yep because if I do on Main.cpp it's working. But when I call from qml to this function on excel.h doesn't work.

    No errors, and no info on debugger.

    Also I've got a

    qmlRegisterType<Excel>("com.myself", 1, 0, "Excel");
    

    line on main.cpp to call those functions on excel.h



  • Have thought about your list gets out of scope?
    Cant see full sources, so cant say 100% sure, but it looks like it is



  • I feel the delay in answering...

    I can't paste my code here... I have this error message when I post it: Post content was flagged as spam by Akismet.com

    Edit:

    I suppose that I need "refresh" main.qml or reload it.

    Here is my main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QtQml>
    
    #include "Excel.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<Excel>("com.myself", 1, 0, "Excel");
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        QStringList dataList;
        dataList.append("wololo");
    
        engine.rootContext()->setContextProperty( "cppModel", QVariant::fromValue(dataList) );
    
        return app.exec();
    }
    

    Class of excel file. I'm loading xlsxdocument plugin.
    Excel.h

    #ifndef EXCEL_H
    #define EXCEL_H
    
    #include <QObject>
    #include "xlsx/xlsxdocument.h"
    #include "xlsx/xlsxabstractsheet.h"
    
    class Excel : public QObject
    {
        Q_OBJECT
    public:
        Q_INVOKABLE getExcel(QString path);
    
    signals:
    
    public slots:
    };
    
    #endif // EXCEL_H
    

    Here is my function that I'm trying to update my combobox model.
    Excel.cpp

    #include "Excel.h"
    #include <QDebug>
    #include <QQmlApplicationEngine>
    #include <QtQml>
    
    Excel::getExcel(QString path)
    {
        path.remove(0,8);
        qDebug() << path;
        QXlsx::Document xlsx(path);
        xlsx.selectSheet("MOULD DIMENSIONS");
        //etc..
    
        QQmlApplicationEngine engine;
        QStringList dataList;
        dataList.append("Item 2");
        engine.rootContext()->setContextProperty( "cppModel", QVariant::fromValue(dataList) );
    }
    

    And here is my main qml.
    main.qml

    import QtQuick 2.5
    import QtQuick.Window 2.2
    import QtQuick.Controls 1.4
    import QtQuick.Dialogs 1.2
    import com.myself 1.0
    
    
    Window {
        visible: true
    
        Excel {
            id: excel
        }
    
        Button {
            id: btnOpen
            width: 125
            text: qsTr("Open Excel")
            clip: false
        }
    
        ComboBox {
            id: combo
            model: cppModel
        }
    
        FileDialog {
            id: fileDialog
            title: "Please choose a file"
            folder: shortcuts.home
            onAccepted: {
                excel.getExcel(fileDialog.fileUrls.toString());
    
            }
        }
        Connections {
            target: btnOpen
            onClicked: fileDialog.visible = true
        }
    }
    

    When I run, the combobox has only 1 item. 'wololo' item.
    And when I open a Excel file, I need to change the combobox items. In this example I need to add 'Item 2' item to combobox.

    But is not working.


  • Moderators

    The 'engine' you create in getExcel() is not the same object as the one you create in main.cpp... so you are setting the root property on a different engine than the one you use to display QML :-)

    To fix this, just pass the engine (or the root context) pointer to your Excel class and use it in getExcel().


  • Moderators

    Another option is to use parent-child hierarchy instead of root properties.

    So, in QML:

    Excel {
      ComboBox {
        id: combo
        objectName: "combo"
      }
    }
    

    And then set model from CPP:

    // Pseudocode, written from memory. Please check the docs to be sure:
    QObject *combo = findChildren<QQuickItem*>("combo"); // combo is a child of Excel in QML!
    if (combo) {
      combo->setProperty("model", QVariant::fromValue(dataList) );
    }
    

    Not sure which one you'll find more aesthetic (also not sure if the second option would work at all ;-) ).



  • @sierdzio said:

    The 'engine' you create in getExcel() is not the same object as the one you create in main.cpp... so you are setting the root property on a different engine than the one you use to display QML :-)

    To fix this, just pass the engine (or the root context) pointer to your Excel class and use it in getExcel().

    Oh!! It's true... I supose that If I create a engine on main, I can use It on another cpp or h class...

    But now I have a another question... How I can pass engine, or root from main to qml and them from qml to function?

    Anyway, I will try the parent-child


  • Moderators

    @sdsoldi said:

    How I can pass engine, or root from main to qml and them from qml to function?

    You can create a singleton class in C++, pass the engine pointer to it in main.cpp, and then simply access it in your Excel class. This is probably the simplest solution, although singletons do have their drawbacks.

    Or add the engine as a context property to QML, then assign it to Excel object... although that seems awkward.



  • or use
    QQmlEngine *qmlEngine(const QObject *object)



  • @vladstelmahovsky Can you explain it please?


  • Moderators

    @sdsoldi

    It is a static method of the engine. SO you can probably use it like this (in Excel class):

        QQmlEngine *engine = qmlEngine(this); // because "this" is an item you have added to QML
        QStringList dataList;
        dataList.append("Item 2");
        engine.rootContext()->setContextProperty( "cppModel", QVariant::fromValue(dataList) );
    


  • @sierdzio @vladstelmahovsky
    Oh my god! It's as simple as that...

    Thank you very much!



  • yep, but dont forget to check for null


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.