Hi,

I'm having difficulty getting a QAbstractListModel-derived model to update the QML ListView it is a model of when the user performs a certain action.

Qt Code:
  1. // visitormodel.h
  2. class VisitorModel : public QAbstractListModel
  3. {
  4. Q_OBJECT
  5. Q_PROPERTY(int count READ getCount() NOTIFY countChanged())
  6.  
  7. public:
  8. explicit VisitorModel(VisitorItem* prototype, QObject* parent = 0);
  9. ~VisitorModel();
  10. int rowCount(const QModelIndex &parent = QModelIndex()) const;
  11. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
  12. void appendRow(VisitorItem* item);
  13. void appendRows(const QList<VisitorItem*> &items);
  14. void insertRow(int row, VisitorItem* item);
  15. bool removeRow(int row, const QModelIndex &parent = QModelIndex());
  16. bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
  17. VisitorItem* takeRow(int row);
  18. VisitorItem* find(const QString &id) const;
  19. QModelIndex indexFromItem( const VisitorItem* item) const;
  20. void clear();
  21. int getCount() const;
  22.  
  23. Q_INVOKABLE QVariant get(int row);
  24.  
  25. private slots:
  26. void handleItemChange();
  27.  
  28. public slots:
  29. void anprCompleted(const QString& licensePlateNumber, const QDateTime& timestamp);
  30. void visitorImageReceived(const QString& vehicleImageSource, const QDateTime& timestamp);
  31. void anprFailed(const QDateTime& timestamp);
  32. void vinScanned(int modelIndex, const QString& vin, const QDateTime& timestamp);
  33.  
  34. signals:
  35. void visitorAdded();
  36. void countChanged();
  37.  
  38. private:
  39. VisitorItem* m_prototype;
  40. QList<VisitorItem*> m_list;
  41. int m_count;
  42. };
To copy to clipboard, switch view to plain text mode 

Qt Code:
  1. // visitormodel.cpp
  2. #include "visitormodel.h"
  3.  
  4. VisitorModel::VisitorModel(VisitorItem* prototype, QObject *parent) :
  5. QAbstractListModel(parent), m_prototype(prototype)
  6. {
  7. setRoleNames(m_prototype->roleNames());
  8. }
  9.  
  10. int VisitorModel::rowCount(const QModelIndex &parent) const
  11. {
  12. Q_UNUSED(parent);
  13. return m_list.size();
  14. }
  15.  
  16. QVariant VisitorModel::data(const QModelIndex &index, int role) const
  17. {
  18. if(index.row() < 0 || index.row() >= m_list.size())
  19. return QVariant();
  20. return m_list.at(index.row())->data(role);
  21. }
  22.  
  23. VisitorModel::~VisitorModel()
  24. {
  25. delete m_prototype;
  26. clear();
  27. }
  28.  
  29. void VisitorModel::appendRow(VisitorItem *item)
  30. {
  31. appendRows(QList<VisitorItem*>() << item);
  32. }
  33.  
  34. void VisitorModel::appendRows(const QList<VisitorItem *> &items)
  35. {
  36. beginInsertRows(QModelIndex(), rowCount(), rowCount()+items.size()-1);
  37. foreach(VisitorItem *item, items)
  38. {
  39. connect(item, SIGNAL(dataChanged()), SLOT(handleItemChange()));
  40. m_list.append(item);
  41. emit visitorAdded();
  42. emit countChanged();
  43. }
  44. endInsertRows();
  45. }
  46.  
  47. void VisitorModel::insertRow(int row, VisitorItem *item)
  48. {
  49. beginInsertRows(QModelIndex(), row, row);
  50. connect(item, SIGNAL(dataChanged()), SLOT(handleItemChange()));
  51. m_list.insert(row, item);
  52. emit countChanged();
  53. endInsertRows();
  54. }
  55.  
  56. void VisitorModel::handleItemChange()
  57. {
  58. VisitorItem* item = static_cast<VisitorItem*>(sender());
  59. QModelIndex index = indexFromItem(item);
  60. if(index.isValid()) {
  61. qDebug() << "Emitting data changed for " << index;
  62. emit dataChanged(index, index);
  63. }
  64. }
  65.  
  66. void VisitorModel::vinScanned(int modelIndex, const QString &vin, const QDateTime &timestamp)
  67. {
  68. qDebug() << "Got vinScanned with " << modelIndex << vin << timestamp;
  69. m_list.at(modelIndex)->setVehicleVin(vin);
  70. }
  71.  
  72. VisitorItem * VisitorModel::find(const QString &id) const
  73. {
  74. foreach(VisitorItem* item, m_list)
  75. {
  76. if(item->id() == id) return item;
  77. }
  78. return 0;
  79. }
  80.  
  81. QModelIndex VisitorModel::indexFromItem(const VisitorItem *item) const
  82. {
  83. Q_ASSERT(item);
  84. for(int row=0; row<m_list.size(); ++row)
  85. {
  86. if(m_list.at(row) == item) return index(row);
  87. }
  88. return QModelIndex();
  89. }
  90.  
  91. void VisitorModel::clear()
  92. {
  93. qDeleteAll(m_list);
  94. m_list.clear();
  95. emit countChanged();
  96. }
  97.  
  98. int VisitorModel::getCount() const
  99. {
  100. return rowCount();
  101. }
  102.  
  103. QVariant VisitorModel::get(int row)
  104. {
  105. if(row >= 0)
  106. {
  107. VisitorItem * item = m_list.at(row);
  108. QMap<QString, QVariant> itemData;
  109. QHashIterator<int, QByteArray> hashItr(item->roleNames());
  110. while(hashItr.hasNext())
  111. {
  112. hashItr.next();
  113. itemData.insert(hashItr.value(),item->data(hashItr.key()).toString());
  114. }
  115. return QVariant(itemData);
  116. }
  117. return QVariant();
  118. }
  119.  
  120. bool VisitorModel::removeRow(int row, const QModelIndex &parent)
  121. {
  122. Q_UNUSED(parent);
  123. if(row < 0 || row >= m_list.size()) return false;
  124. beginRemoveRows(QModelIndex(), row, row);
  125. delete m_list.takeAt(row);
  126. endRemoveRows();
  127. return true;
  128. }
  129.  
  130. bool VisitorModel::removeRows(int row, int count, const QModelIndex &parent)
  131. {
  132. Q_UNUSED(parent);
  133. if(row < 0 || (row+count) >= m_list.size()) return false;
  134. beginRemoveRows(QModelIndex(), row, row+count-1);
  135. for(int i=0; i<count; ++i)
  136. {
  137. delete m_list.takeAt(row);
  138. }
  139. endRemoveRows();
  140. return true;
  141. }
  142.  
  143. VisitorItem * VisitorModel::takeRow(int row)
  144. {
  145. beginRemoveRows(QModelIndex(), row, row);
  146. VisitorItem* item = m_list.takeAt(row);
  147. emit countChanged();
  148. endRemoveRows();
  149. return item;
  150. }
To copy to clipboard, switch view to plain text mode 

In QML, I expose this model using setContextProperty and call it "_arrivingVisitorsModel". I then have a text element as shown below:

Qt Code:
  1. Text
  2. {
  3. text: _arrivingVisitorsModel.get(visitorQueueListView.currentIndex).vin
  4. onTextChanged: console.log("TEXT CHANGED TO " + text)
  5. }
To copy to clipboard, switch view to plain text mode 

I have another button in QML that causes the VisitorModel::vinScanned slot to be called. I've confirmed that this does successfully update the vin field and the handleItemChange is called and the dataChanged signal is emitted, however it is not reflected in the Text element in QML (onTextChanged never gets called). It's only after I change the visitorQueueListView.currentIndex and then come back that the onTextChanged gets called and the text in my Text element gets changed. Why is this the case if the dataChanged signal is being emitted by the VisitorModel?

Thanks!