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. QAbstractItemModel property buinding
QtWS25 Last Chance

QAbstractItemModel property buinding

Scheduled Pinned Locked Moved QML and Qt Quick
qmlmodel-view
8 Posts 2 Posters 3.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.
  • O Offline
    O Offline
    olejl77
    wrote on last edited by
    #1

    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");
    }
    
    p3c0P 1 Reply Last reply
    0
    • O olejl77

      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");
      }
      
      p3c0P Offline
      p3c0P Offline
      p3c0
      Moderators
      wrote on last edited by
      #2

      @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
      0
      • O Offline
        O Offline
        olejl77
        wrote on last edited by
        #3

        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

        p3c0P 1 Reply Last reply
        0
        • O olejl77

          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

          p3c0P Offline
          p3c0P Offline
          p3c0
          Moderators
          wrote on last edited by p3c0
          #4

          @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
          0
          • O Offline
            O Offline
            olejl77
            wrote on last edited by olejl77
            #5

            @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"

            p3c0P 1 Reply Last reply
            0
            • O 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"

              p3c0P Offline
              p3c0P Offline
              p3c0
              Moderators
              wrote on last edited by
              #6

              @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
              0
              • O Offline
                O Offline
                olejl77
                wrote on last edited by
                #7

                @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?

                p3c0P 1 Reply Last reply
                0
                • O olejl77

                  @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?

                  p3c0P Offline
                  p3c0P Offline
                  p3c0
                  Moderators
                  wrote on last edited by
                  #8

                  @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
                  0

                  • Login

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