Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. How to get a ListModel to update from a signal
Forum Updated to NodeBB v4.3 + New Features

How to get a ListModel to update from a signal

Scheduled Pinned Locked Moved Solved QML and Qt Quick
7 Posts 5 Posters 6.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • F Offline
    F Offline
    flowolf
    wrote on last edited by flowolf
    #1

    So I have a ListView with delegates of Rectangles that contain Text. It uses a ListModel that which when completed, appends more ListElements that is read from a C++ class method which iterates a QStringList. Essentially it is a Logger that functions well at the start but only at the start of the application. I'm not sure if I want to use Component.onCompleted and need to use a signal from the C++ class to continue to update the ListModel. I've tried using onLogLinesChanged() as a signal but keep getting an error "Non-existent attached object". Here is a portion of my code so far.

    ListView {
    id: logList_Id
    boundsBehavior: Flickable.StopAtBounds
    model: listModel
    clip: true
    anchors.fill: parent
    delegate: Rectangle {

                    id: rectangle
                    width: logList_Id.width
                    height: textLogs_Id.height
                    color: index % 2 == 0 ? "#bdbdbd" : "#ffffff"
    
                    Text {
                        
                        id: textLogs_Id
                        text: logString
                        wrapMode: Text.WordWrap
                        width: parent.width
                    }
                }
    

    }

    ListModel {

    //    Connections {
    //                    target: LibXpcWorkerApi
    //                    onlogLinesChanged: {
    //                        listModel.append()
    //                    }
    //                }
    
          id: listModel
          Component.onCompleted: {
                    LibXpcWorkerApi.setLogLines();
                    for(var i=0; i<LibXpcWorkerApi.logLinesSize(); i++)
                        append(createListElement(i));
                }
    
                function createListElement(i) {
                    return { logString: LibXpcWorkerApi.logLinesAt(i)};
                }
    
    //          LibXpcWorkerApi.onLogLinesChanged: listModel.append(LibXpcWorkerApi.logLinesAt(0));
    

    }

    E 1 Reply Last reply
    0
    • dheerendraD Offline
      dheerendraD Offline
      dheerendra
      Qt Champions 2022
      wrote on last edited by
      #2

      You can expose the model from c++ itself. Please look at examples directory of qt installations. It is easy.

      Dheerendra
      @Community Service
      Certified Qt Specialist
      http://www.pthinks.com

      1 Reply Last reply
      3
      • F Offline
        F Offline
        flowolf
        wrote on last edited by flowolf
        #3

        That is an incredibly vague answer and does not help me whatsoever. I already have the signals connected and can call class members from qml. I do not know nor can find any information on what is the correct qml implementation for updating a ListModel once it receives a signal.

        1 Reply Last reply
        0
        • RajeeshRaveendranR Offline
          RajeeshRaveendranR Offline
          RajeeshRaveendran
          wrote on last edited by
          #4

          Hi,

          I have created a sample application. Hope this will help you to get what u want.

          /// main.cpp

          #include <QApplication>
          #include <QQmlApplicationEngine>
          #include <QQmlContext>
          #include "myviewmodel.h"

          int main(int argc, char *argv[])
          {
          QApplication app(argc, argv);

          QQmlApplicationEngine engine;
          
          QQmlContext* context = engine.rootContext();
          MyViewModel myModel;
          context->setContextProperty("myViewModelInstance", &myModel);
          
          engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
          
          return app.exec();
          

          }

          /// myviewmodel.h
          #ifndef MYVIEWMODEL_H
          #define MYVIEWMODEL_H

          #include <QObject>

          class MyViewModel : public QObject
          {
          Q_OBJECT
          public:
          Q_PROPERTY(QStringList dataList READ dataList NOTIFY dataListChanged)
          explicit MyViewModel(QObject *parent = 0);
          Q_INVOKABLE void setLogLines();
          Q_INVOKABLE void updateModel(const QString& value);
          QStringList dataList();

          signals:
          void getLatest();
          void dataListChanged();

          public slots:
          void onGetLatest();

          private:
          QStringList m_myData;
          };

          #endif // MYVIEWMODEL_H

          /// myviewmodel.cpp

          #include <QVariant>
          #include "myviewmodel.h"

          MyViewModel::MyViewModel(QObject *parent) : QObject(parent)
          {
          connect( this, SIGNAL( getLatest()),
          this, SLOT( onGetLatest()),
          Qt::DirectConnection );
          }

          void MyViewModel::setLogLines()
          {
          QString item("Name: %1");
          for(int i = 0; i < 5; ++i)
          {
          m_myData.append(item.arg(i+1));
          }
          emit dataListChanged();
          }

          void MyViewModel::updateModel(const QString& value)
          {
          m_myData.append(value);
          emit dataListChanged();
          }

          void MyViewModel::onGetLatest()
          {
          /// Update the model and emit the signal. So that QML will update.
          updateModel("from C++");
          }

          QStringList MyViewModel::dataList()
          {
          return m_myData;
          }

          /// main.qml
          import QtQuick 2.5
          import QtQuick.Controls 1.4
          import QtQuick.Dialogs 1.2

          ApplicationWindow {
          visible: true
          width: 640
          height: 480
          title: qsTr("Hello World")

          ListView {
          id: logList_Id
          boundsBehavior: Flickable.StopAtBounds
          model: myViewModelInstance.dataList /// Propery exposed from C++
          clip: true
          anchors.fill: parent
          delegate: Rectangle {
          
                          id: rectangle
                          width: logList_Id.width
                          height: textLogs_Id.height
                          color: index % 2 == 0 ? "#bdbdbd" : "#ffffff"
          
                          Text {
          
                              id: textLogs_Id
                              text: modelData
                              wrapMode: Text.WordWrap
                              width: parent.width
                          }
                      }
          }
          
          ListModel {
                id: listModel
                Component.onCompleted: {
                          //LibXpcWorkerApi.setLogLines();
                          myViewModelInstance.setLogLines();
                          for(var i=0; i<10/*LibXpcWorkerApi.logLinesSize()*/; i++)
                              append(createListElement(i));
                      }
          
                      function createListElement(i) {
                          //return { logString: LibXpcWorkerApi.logLinesAt(i)};
                          return ({"action": "preset"});
                      }
          
          }
          Button {
              id: button1
              width: 100
              height: 50
              text: "ADD"
              anchors.bottom: parent.bottom
              onClicked: {
                  myViewModelInstance.updateModel("from QML"); // Add an item in the list.
              }
          }
          Button {
              id: button2
              width: 100
              height: 50
              text: "ADD"
              anchors.bottom: parent.bottom
              anchors.left: button1.right
              onClicked: {
                  myViewModelInstance.getLatest(); /// Trigger model c++ class to update the list.
              }
          }
          

          }

          1 Reply Last reply
          0
          • F flowolf

            So I have a ListView with delegates of Rectangles that contain Text. It uses a ListModel that which when completed, appends more ListElements that is read from a C++ class method which iterates a QStringList. Essentially it is a Logger that functions well at the start but only at the start of the application. I'm not sure if I want to use Component.onCompleted and need to use a signal from the C++ class to continue to update the ListModel. I've tried using onLogLinesChanged() as a signal but keep getting an error "Non-existent attached object". Here is a portion of my code so far.

            ListView {
            id: logList_Id
            boundsBehavior: Flickable.StopAtBounds
            model: listModel
            clip: true
            anchors.fill: parent
            delegate: Rectangle {

                            id: rectangle
                            width: logList_Id.width
                            height: textLogs_Id.height
                            color: index % 2 == 0 ? "#bdbdbd" : "#ffffff"
            
                            Text {
                                
                                id: textLogs_Id
                                text: logString
                                wrapMode: Text.WordWrap
                                width: parent.width
                            }
                        }
            

            }

            ListModel {

            //    Connections {
            //                    target: LibXpcWorkerApi
            //                    onlogLinesChanged: {
            //                        listModel.append()
            //                    }
            //                }
            
                  id: listModel
                  Component.onCompleted: {
                            LibXpcWorkerApi.setLogLines();
                            for(var i=0; i<LibXpcWorkerApi.logLinesSize(); i++)
                                append(createListElement(i));
                        }
            
                        function createListElement(i) {
                            return { logString: LibXpcWorkerApi.logLinesAt(i)};
                        }
            
            //          LibXpcWorkerApi.onLogLinesChanged: listModel.append(LibXpcWorkerApi.logLinesAt(0));
            

            }

            E Offline
            E Offline
            Eeli K
            wrote on last edited by
            #5

            @flowolf I think "Non-existent attached object" comes because you have
            target: LibXpcWorkerApi
            LibXpcWorkerApi.setLogLines();
            LibXpcWorkerApi.logLinesAt(0)

            which begin with a capital letter. Either your LibXpcWorkerApi should be a type which actually has attached properties or it should be an object called libXpcWorkerApi.

            W 1 Reply Last reply
            0
            • E Eeli K

              @flowolf I think "Non-existent attached object" comes because you have
              target: LibXpcWorkerApi
              LibXpcWorkerApi.setLogLines();
              LibXpcWorkerApi.logLinesAt(0)

              which begin with a capital letter. Either your LibXpcWorkerApi should be a type which actually has attached properties or it should be an object called libXpcWorkerApi.

              W Offline
              W Offline
              Wolosocu
              wrote on last edited by
              #6

              @Eeli-K said in How to get a ListModel to update from a signal:

              @flowolf I think "Non-existent attached object" comes because you have
              target: LibXpcWorkerApi
              LibXpcWorkerApi.setLogLines();
              LibXpcWorkerApi.logLinesAt(0)

              which begin with a capital letter. Either your LibXpcWorkerApi should be a type which actually has attached properties or it should be an object called libXpcWorkerApi.

              This capital letter requirement has gotten me a few times. It's one of the things I wish would change about QML.

              1 Reply Last reply
              0
              • F Offline
                F Offline
                flowolf
                wrote on last edited by flowolf
                #7

                I actually figured it out on my own but thanks for taking the time to help me out.

                I did not use QQml Context, rather qmlRegisterSingletonType. Inside the LibXpcWorkerApi ctor I connect a signal to a slot which also passes a string. Inside slot, it takes that string and modifies a private member QString mLog and it ends with emitting a signal that the log has changed.
                Inside qml, for whatever reason, Connections can't be too far embedded in a qml file rather needs to be further out of scope towards the top.
                Eeli K, your right about the capital letter. So for anyone else attempting to append strings to a qml component based on signals, here's the short.

                LibXpcWorkerApi ctor { connect(mXpcLib, SIGNAL(signalLogLineAdded(const QString), this, SLOT(slotLogAdded(const QString))); }

                void LibXpcWorkerApi::slotLogAdded(const QString logline)
                {
                if(mLog != "") mLog= "";
                mLog = logline;
                emit logChanged();
                }

                qml file:
                Connections
                {
                target: LibXpcWorkerApi
                onLogChanged: listModel_Id.append(showLogsRect_Id.addLogElement());
                }

                function addLogElement()
                {
                return { logString: LibXpcWorkerApi.getLog()};
                }

                append will not take strings, only objects so you have to set text inside a delegate as logString or whatever matches in your return statement.
                I stored logs one string at a time for better memory management, for what I'm doing isn't required to stay in memory in a c++ class.

                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved