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 update QAbstractItemModel view when a Data is updated
QtWS25 Last Chance

How to update QAbstractItemModel view when a Data is updated

Scheduled Pinned Locked Moved Solved QML and Qt Quick
6 Posts 4 Posters 10.5k 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
    Fheanor
    wrote on last edited by Fheanor
    #1

    I use the Qt example for QAbstractItemModel and I try to update an Item to a given index.

    I tried to use emit DataChangedbut it doesn't work, the view is not updated.

    Here is an example:

    What I want: When you click on the button, it will update Data at index 0, the type of the animal will be changed, it will become a Lion.

    #include <QAbstractListModel>
    #include <QStringList>
    #include <qqmlcontext.h>
    //![0]
    class Animal
    {
    public:
        Animal(const QString &type, const QString &size);
    //![0]
    
        QString type() const;
        QString size() const;
    
        void setType(QString q) {
            m_type = q;
        }
    
    private:
        QString m_type;
        QString m_size;
    //![1]
    };
    
    class AnimalModel : public QAbstractListModel
    {
        Q_OBJECT
    public:
    
        Q_INVOKABLE void test() ;
        void setName(const QString &name);
        enum AnimalRoles {
            TypeRole = Qt::UserRole + 1,
            SizeRole
        };
    
        AnimalModel(QObject *parent = 0);
    //![1]
    //!
    //!
        void setContext(QQmlContext *ctx) {
            m_ctx = ctx;
        }
    
        void addAnimal(const Animal &animal);
    
        int rowCount(const QModelIndex & parent = QModelIndex()) const;
    
        QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
        QHash<int, QByteArray> roleNames() const;
    
    protected:
    
    private:
        QList<Animal> m_animals;
        QQmlContext*  m_ctx;
    
    signals:
        void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
    //![2]
    };
    //![2]
    

    model.h

    #include "model.h"
    #include "qDebug"
    Animal::Animal(const QString &type, const QString &size)
        : m_type(type), m_size(size)
    {
    }
    
    QString Animal::type() const
    {
        return m_type;
    }
    
    QString Animal::size() const
    {
        return m_size;
    }
    
    
    
    
    AnimalModel::AnimalModel(QObject *parent)
        : QAbstractListModel(parent)
    {
    }
    
    void AnimalModel::addAnimal(const Animal &animal)
    {
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        m_animals << animal;
        endInsertRows();
    }
    
    void AnimalModel::test() {
    
      m_animals[0].setType("Lion");
      emit dataChanged(QModelIndex(),QModelIndex());
      //I also tried:
      QModelIndex topLeft = createIndex(0,0);
      emit dataChanged(topLeft, topLeft);
    
    
    
     }
    
    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();
    
        const Animal &animal = m_animals[index.row()];
        if (role == TypeRole)
            return animal.type();
        else if (role == SizeRole)
            return animal.size();
        return QVariant();
    }
    
    //![0]
    QHash<int, QByteArray> AnimalModel::roleNames() const {
        QHash<int, QByteArray> roles;
        roles[TypeRole] = "type";
        roles[SizeRole] = "size";
        return roles;
    }
    //![0]
    

    model.cpp

    #include "model.h"
    
    #include <QGuiApplication>
    #include <qqmlengine.h>
    #include <qqmlcontext.h>
    #include <qqml.h>
    #include <QtQuick/qquickitem.h>
    #include <QtQuick/qquickview.h>
    
    //![0]
    int main(int argc, char ** argv)
    {
        QGuiApplication app(argc, argv);
    
        AnimalModel model;
        model.addAnimal(Animal("Wolf", "Medium"));
        model.addAnimal(Animal("Polar bear", "Large"));
        model.addAnimal(Animal("Quoll", "Small"));
    
        QQuickView view;
        view.setResizeMode(QQuickView::SizeRootObjectToView);
        QQmlContext *ctxt = view.rootContext();
        ctxt->setContextProperty("myModel", &model);
    //![0]
    
        view.setSource(QUrl("qrc:view.qml"));
    
    
        view.show();
    
        return app.exec();
    }
    

    main.cpp

    import QtQuick 2.0
    import QtQuick 2.4
    import QtQuick.Controls 1.3
    import QtQuick.Window 2.2
    import QtQuick.Dialogs 1.2
    import QtQuick.Layouts 1.2
    import QtQml.Models 2.1
    import QtQuick.Controls.Styles 1.2
    
    
    //![0]
    ListView {
        width: 200; height: 250
    
        model: myModel
        delegate: Text { text: "Animal: " + type + ", " + size }
    
        MouseArea {
            anchors.fill: parent
            cursorShape: Qt.PointingHandCursor
            onClicked: {
    
            }
        }
        Button {
            anchors.bottom: parent.bottom
            width:50; height:50
            text:"click"
            onClicked: {
                myModel.test()
            }
    
        }
    
    }
    //![0]
    

    View.qml

    Do you have any idea why it doesn't work ?
    Thanks a lot !

    1 Reply Last reply
    0
    • R Offline
      R Offline
      Ray Gray
      wrote on last edited by Ray Gray
      #2

      The easiest solution:

      void AnimalModel::test() {
         layoutAboutToBeChanged();
         m_animals[0].setType("Lion");
         layoutChanged();
      }
      
      1 Reply Last reply
      -2
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #3

        Hi,

        Just a quick thought, try adding the role parameter to your dataChanged call and put the type role in it.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        1
        • R Offline
          R Offline
          Ray Gray
          wrote on last edited by Ray Gray
          #4

          Also, don't declare your dataChanged signal - emit the 'built-in' AbstractItemModel::dataChanged signal! As SGaist already mentioned, this should do the trick:

          emit dataChanged(topLeft, topLeft, { TypeRole }); 
          
          F 1 Reply Last reply
          3
          • VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by VRonin
            #5
            • remove void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight); from model.h
            • in AnimalModel::rowCount replace Q_UNUSED(parent); with if(parent.isValid()) return 0;
            • in AnimalModel::data add if(!index.isValid()) return QVariant(); Q_ASSERT(index.model()==this); before anything else
            • AnimalModel::test should be:
            m_animals[0].setType("Lion");
            const QModelIndex idx = index(0);
            emit dataChanged(idx ,idx );
            
            • createIndex should only be called inside the index method
            • for efficiency you might want to store the role names as a private member const QHash<int, QByteArray> roles = {std::pair<int,QByteArray>(TypeRole,"type"),std::pair<int,QByteArray>(SizeRole,"size")}; and AnimalModel::roleNames becomes just QHash<int, QByteArray> AnimalModel::roleNames() const {return roles;}

            For cases like this one, implementing a custom model is more a waste of time than anything else, QStandardItemModel could have done the trick effortlessly


            @SGaist said in How to update QAbstractItemModel view when a Data is updated:

            try adding the role parameter to your dataChanged call and put the type role in it.

            Unfortunately that parameter has been forgotten in the darkness. even built in Qt models do not use it

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            1 Reply Last reply
            4
            • R Ray Gray

              Also, don't declare your dataChanged signal - emit the 'built-in' AbstractItemModel::dataChanged signal! As SGaist already mentioned, this should do the trick:

              emit dataChanged(topLeft, topLeft, { TypeRole }); 
              
              F Offline
              F Offline
              Fheanor
              wrote on last edited by
              #6

              @Ray-Gray Thanks a lot, I will apply this to my code. I based my project on the QT example, and they didn't do that, I don't know why.

              Now it works fine, thanks all ! :)

              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