Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    QAbstractItemModel property buinding

    QML and Qt Quick
    qml model-view
    2
    8
    3409
    Loading More Posts
    • 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.
    • O
      olejl77 last edited by

      I have sub-classed a QAbstractTableModel which contains a database of live value changing continuously. Included in this post is a modification of abstractitemmodel example to illustrate my problem and what I want to achieve.

      When using the ListView in QML, the values updates according to changes in the model. How can I receive these updates when using a "Text QML Type" (or any other Types)?

      view.qml

      import QtQuick 2.4
      
      Rectangle {
          Text {
              text: myModel.get(0,0)
          }
      
          ListView {
              width: 200; height: 250
              y: 20
      
              model: myModel
              delegate: Text { text: "Animal: " + type }
          }
      
      }
      

      model.h:

      #include <QAbstractListModel>
      #include <QStringList>
      #include <QObject>
      
      class Animal : public QObject
      {
          Q_OBJECT
          Q_PROPERTY(QString type READ type WRITE setType NOTIFY typeChanged)
      public:
          Animal(const QString type, QObject *parent = 0);
          QString type() const;
          void setType(QString s);
      
      signals:
          void typeChanged(QString);
      
      private:
          QString m_type;
      };
      
      class AnimalModel : public QAbstractListModel
      {
          Q_OBJECT
      public:
          enum AnimalRoles {
              TypeRole = Qt::UserRole + 1,
          };
      
          AnimalModel(QObject *parent = 0);
      
          void addAnimal(QString type);
          int rowCount(const QModelIndex & parent = QModelIndex()) const;
          QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
          // Qt 5.5 helper functions
          Q_INVOKABLE QVariant get(const QByteArray& roleName, int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
          Q_INVOKABLE QVariant get(int role, int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
      
      public slots:
          void onUpdate(QString v);
      
      protected:
          QHash<int, QByteArray> roleNames() const;
      private:
          QList<Animal *> m_animals;
      };
      

      model.cpp

      #include "model.h"
      
      Animal::Animal(const QString type, QObject *parent): QObject(parent)
      {
          setType(type);
      }
      
      QString Animal::type() const
      {
          return m_type;
      }
      
      void Animal::setType(QString s)
      {
          m_type = s;
          emit typeChanged(s);
      }
      
      AnimalModel::AnimalModel(QObject *parent)
          : QAbstractListModel(parent)
      {
      }
      
      void AnimalModel::onUpdate(QString newtype)
      {
          Animal *a = m_animals[0];
          a->setType(newtype);
          QModelIndex top = createIndex(0, 0);
          QModelIndex bottom = createIndex(m_animals.count(), 1);
          emit dataChanged(top, bottom);
      }
      
      void AnimalModel::addAnimal(QString type)
      {
          Animal *a = new Animal(type);
          beginInsertRows(QModelIndex(), rowCount(), rowCount());
          m_animals.append(a);
          endInsertRows();
      }
      
      int AnimalModel::rowCount(const QModelIndex & parent) const {
          Q_UNUSED(parent);
          return m_animals.count();
      }
      
      QVariant AnimalModel::data(const QModelIndex & index, int role) const {
          if (index.row() < 0 || index.row() >= m_animals.count())
              return QVariant();
      
          Animal *a = m_animals[index.row()];
          if (role == TypeRole)
              return a->type();
          if (role == 0)
              return a->type();
          return QVariant();
      }
      
      //![0]
      QHash<int, QByteArray> AnimalModel::roleNames() const {
          QHash<int, QByteArray> roles;
          roles[TypeRole] = "type";
          return roles;
      }
      //![0]
      
      /*!
      \since 5.5
      Helper function to access elements in the model for a given \a roleName,
      and an index defined by its \a row, \a column and \a parent.
      \sa data()
      */
      QVariant AnimalModel::get(const QByteArray& roleName, int row, int column, const QModelIndex& parent) const
      {
          int role = roleNames().key(roleName, -1);
          if (role < 0) {
              return QVariant();
          }
          return data(index(row, column, parent), role);
      }
      
      /*!
      \since 5.5
      Helper function to access elements in the model for a given \a role,
      and an index defined by its \a row, \a column and \a parent.
      \sa data()
      */
      QVariant AnimalModel::get(int role, int row, int column, const QModelIndex& parent) const
      {
          return data(index(row, column, parent), role);
      }
      

      main.cpp

      #include "model.h"
      
      #include <QGuiApplication>
      #include <qqmlengine.h>
      #include <qqmlcontext.h>
      #include <qqml.h>
      #include <QtQuick/qquickitem.h>
      #include <QtQuick/qquickview.h>
      #include <QThread>
      #include "worker.h"
      
      int main(int argc, char ** argv)
      {
          QGuiApplication app(argc, argv);
      
          AnimalModel model;
          model.addAnimal("Wolf");
          model.addAnimal("Polar bear");
          model.addAnimal("Quoll");
      
          QThread *thread = new QThread();
          Worker *worker = new Worker();
          worker->moveToThread(thread);
          QObject::connect(thread, SIGNAL(started()), worker, SLOT(process()));
          QObject::connect(worker, SIGNAL(updatevar(QString)), &model, SLOT(onUpdate(QString)));
          thread->start();
      
          QQuickView view;
          view.setResizeMode(QQuickView::SizeRootObjectToView);
          QQmlContext *ctxt = view.rootContext();
          ctxt->setContextProperty("myModel", &model);
      
          view.setSource(QUrl("qrc:view.qml"));
          view.show();
      
          return app.exec();
      }
      

      worker.h

      #ifndef WORKER_H
      #define WORKER_H
      
      #include <QObject>
      
      class Worker : public QObject
      {
          Q_OBJECT
      public:
          explicit Worker(QObject *parent = 0);
      
      signals:
          void completed(const char *v);
          void updatevar(QString v);
      
      public slots:
          void process();
      
      private:
          QList<QString> m_animalTypes;
      
      };
      
      #endif // WORKER_H
      

      worker.cpp

      #include "worker.h"
      #include "QThread"
      
      Worker::Worker(QObject *parent) :
          QObject(parent)
      {
          m_animalTypes.append("Polar bear");
          m_animalTypes.append("Quoll");
          m_animalTypes.append("Mouse");
          m_animalTypes.append("Squirrel");
          m_animalTypes.append("Fox");
          m_animalTypes.append("Wolf");
      }
      
      void Worker::process()
      {
          for (int i = 0; i < m_animalTypes.count(); i++)
          {
              QThread::sleep(2);
              emit updatevar(m_animalTypes[i]);
          }
          emit completed("Thread ended");
      }
      
      p3c0 1 Reply Last reply Reply Quote 0
      • p3c0
        p3c0 Moderators @olejl77 last edited by

        @olejl77 You can use onAdd signal handler in your delegate where you can update the Text item.

        import QtQuick 2.4
        
        Rectangle {
            Text {
                id: myText
                //text: myModel.get(0,0)
            }
            ListView {
                width: 200; height: 250
                y: 20
                model: myModel
                delegate: Text { 
                    text: "Animal: " + type 
                    ListView.onAdd: myText.text = type
                }
            }
        }
        

        Is this what you wanted ?

        157

        1 Reply Last reply Reply Quote 0
        • O
          olejl77 last edited by

          Your code doesn't seem to work. When I run it the Text is empty.

          Also the ListView was only added to show that it is working in the list view. I don't want to use a ListView. I only want to show a specific index in the model in the Text property.

          Showing the type was working in my original code, but when the model emit typeChanged(), the Text is not updated.

          I want only the Text

          p3c0 1 Reply Last reply Reply Quote 0
          • p3c0
            p3c0 Moderators @olejl77 last edited by p3c0

            @olejl77 Ok. Well then you just have to notify the updates in QML. One of the way is to use
            Connections. target it to your model myModel and when the signal typeChanged() it can be caught in its corresponding handler onTypeChanged. You can modify signal to allow it to pass an argument so that it can be directly received in the handler. So for eg:

            Text {
                id: myText
            }
            
            Connections {
                target: myModel
                onTypeChanged: myText.text = type
            }
            

            This will work if you pass for eg. type as an argument in signal as typeChanged(QString type).

            Or if you don't then you already have received signal using Connections where you can update the Text

            Connections {
                target: myModel
                onTypeChanged: myText.text = myModel.get(0,0)
            }
            

            Hope this is helps...

            157

            1 Reply Last reply Reply Quote 0
            • O
              olejl77 last edited by olejl77

              @p3c0 Thanks for trying to assist, but I can get it to work. I get the following error message in both your cases:
              QML Connections: Cannot assign to non-existent property "onTypeChanged"

              p3c0 1 Reply Last reply Reply Quote 0
              • p3c0
                p3c0 Moderators @olejl77 last edited by

                @olejl77 That means either there is no signal named typeChanged in your model or there's case problem. Meaning to have onTypeChanged the signal name should be typeChanged. More info here in first paragraph. Make sure they are accordingly changed.

                157

                1 Reply Last reply Reply Quote 0
                • O
                  olejl77 last edited by

                  @p3c0 It finally dawned for me. My problem here is that "myModel" doesn't have a typeChanged. typeChanged is originating from the Animal object inside "myModel". What I want is a way to bind to a a specific object inside the model, and not to the model itself.

                  Is there a "proper" way to do that? I could use the dataChanged signal and figure out from there if the change was from the object I want, but it sound unessessary complicated. How can I find out how the ListView component handles this?

                  p3c0 1 Reply Last reply Reply Quote 0
                  • p3c0
                    p3c0 Moderators @olejl77 last edited by

                    @olejl77 You can add typeChanged signal and emit it from the model itself when new item is added to model or as per your requirement (i.e when specific item is added).

                    What I want is a way to bind to a a specific object inside the model, and not to the model itself.

                    AFAIK the only way to access the items of model is through model itself. The example I posted earlier will always get the Item at 0 index and not any other when.

                    157

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post