QAbstractTableModel is not updating (TableView model)
-
Hello. I have an TableModelResults class (QAbstractTableModel subclass). This class is used to create an Table Model to display Results History as an Table - I need an option to add new elements to that Table. This class is included in Manager class (it will be an manager to everything in this project).
In Manager class there's an function (which can be called from QML) to add or remove, all or single element (this function is calling TableModelResults class function).
Everything works fine except refreshing the TableView model in QML. Why?
tablemodelresults.h
class TableModelResults : public QAbstractTableModel { Q_OBJECT public: TableModelResults(QObject *parent = nullptr); int rowCount(const QModelIndex & = QModelIndex()) const override; int columnCount(const QModelIndex & = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QHash<int, QByteArray> roleNames() const override; void addResult(QString timestamp, QString name, QString price, QString whisper); private: QVector<QVector<QString>> table; };
tablemodelresults.cpp (addResult is messy, I tried everything in this function lol)
TableModelResults::TableModelResults(QObject *parent) : QAbstractTableModel(parent) { addResult("1", "2", "3", "4"); addResult("1", "2", "3", "4"); addResult("1", "2", "3", "4"); } int TableModelResults::rowCount(const QModelIndex &) const { return table.size(); } int TableModelResults::columnCount(const QModelIndex &) const { return 4; } QVariant TableModelResults::data(const QModelIndex &index, int role) const { switch (role) { case Qt::DisplayRole: return table.at(index.row()).at(index.column()); default: break; } return QVariant(); } QHash<int, QByteArray> TableModelResults::roleNames() const { return { {Qt::DisplayRole, "display"} }; } void TableModelResults::addResult(QString timestamp, QString name, QString price, QString whisper) { emit layoutAboutToBeChanged(); int row = rowCount(QModelIndex()); beginInsertRows(QModelIndex(),row,row-1); insertRow(row); table.append({timestamp, name, price, whisper}); setData(index(row, 0), QVariant::fromValue<QString>(timestamp), Qt::DisplayRole); setData(index(row, 1), QVariant::fromValue<QString>(name), Qt::DisplayRole); setData(index(row, 2), QVariant::fromValue<QString>(price), Qt::DisplayRole); setData(index(row, 3), QVariant::fromValue<QString>(whisper), Qt::DisplayRole); endInsertRows(); emit layoutChanged(); emit dataChanged(index(0, 0), index(columnCount(), rowCount())); int idx = table.size() - 1; emit dataChanged(index(idx, 0), index(idx, 0)); qDebug() << table.size(); }
-
@BD9a
Well one thing at a time, you really need to read and understand what that subclassing subtopic is telling you, because at the moment you are all over the place not doing the right things. And I think you'll find when you do that will solve your "redraw" issues.For example, it tells you:
Models that provide interfaces to resizable data structures can provide implementations of insertRows(), ...
An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.
You must provide overrides of
insertRows()
etc. in your subclass. That is what would call yourtable.append()
. Then the outside world --- or some utility like youraddResult()
--- callsinsertRows()
orinsertRow()
. Then it will all work.As for your
emit dataChanged()
. That would belong only in your override ofsetData()
. It is only for changing existing data. When you insert or delete rows you do not emit it; instead that job is performed by thebeginInsertRows()
etc. you have in yourinsertRows()
implementation. Views noticedataChanged()
,beginInsertRows()
etc. and act accordingly to update.I will say one other thing. You seem to have a data model of rows of 4 string columns. I don't know what your objective/expertise is, but you could/might just use a
QStandardItemModel
and then you would not have to implement all this stuff. I don't know anything about QML or how this would interact with that, nor whether it's suitable for your case. -
@BD9a said in QAbstractTableModel is not updating (TableView model):
beginInsertRows(QModelIndex(),row,row-1);
your 'end' is wrong. See the documentation: https://doc.qt.io/qt-5/qabstractitemmodel.html#beginInsertRows
-
@Christian-Ehrlicher So, if Im inserting row by row, the "end" should be row or row+1? I guess row+1.
-
@JonB Okay so this part is fixed (I guess):
beginInsertRows(QModelIndex(),row,row); insertRow(row, QModelIndex()); table.append({timestamp, name, price, whisper}); endInsertRows();
I will use insertRow function (cuz I will add rows / data one by one).
Still doesnt work, GUI is not updating. Is this QModelIndex okay?
Code after changes:void TableModelResults::addResult(QString timestamp, QString name, QString price, QString whisper) { emit layoutAboutToBeChanged(); int row = rowCount(QModelIndex()); beginInsertRows(QModelIndex(),row,row); table.append({timestamp, name, price, whisper}); insertRow(row, QModelIndex()); setData(index(row, 0), QVariant::fromValue<QString>(timestamp), Qt::DisplayRole); setData(index(row, 1), QVariant::fromValue<QString>(name), Qt::DisplayRole); setData(index(row, 2), QVariant::fromValue<QString>(price), Qt::DisplayRole); setData(index(row, 3), QVariant::fromValue<QString>(whisper), Qt::DisplayRole); endInsertRows(); emit layoutChanged(); emit dataChanged(index(0, 0), index(columnCount(), rowCount())); int idx = table.size() - 1; emit dataChanged(index(idx, 0), index(idx, 0)); qDebug() << table.size(); }
-
@BD9a
I do not recognise what code model you are following. You havebeginInsertRows()
,insertRow()
and yourtable.append(()
all in the same function, not to mention liberalsetData()
s,layoutChanged()
anddataChanged()
.You are subclassing
QAbstractTableModel
so I'd expect you to be following https://doc.qt.io/qt-5/qabstractitemmodel.html#subclassing. -
@JonB Yeah, as I said it's messy (just little bit lol). I tried everything, but finally I guess I should use
beginInsertRows()
,insertRow()
(how to use it in my case?),endInsertRows()
but what abouttable.append()
when data is actually grabbed from this vector?Also, I tried to do it with:
beginRemoveRows(QModelIndex(), 0, 0); table.removeAt(0); endRemoveRows(); emit dataChanged(index(0,0), index(0, 4)); //index 0 3 didnt worked too
but nothing happened.
-
@BD9a
Well one thing at a time, you really need to read and understand what that subclassing subtopic is telling you, because at the moment you are all over the place not doing the right things. And I think you'll find when you do that will solve your "redraw" issues.For example, it tells you:
Models that provide interfaces to resizable data structures can provide implementations of insertRows(), ...
An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.
You must provide overrides of
insertRows()
etc. in your subclass. That is what would call yourtable.append()
. Then the outside world --- or some utility like youraddResult()
--- callsinsertRows()
orinsertRow()
. Then it will all work.As for your
emit dataChanged()
. That would belong only in your override ofsetData()
. It is only for changing existing data. When you insert or delete rows you do not emit it; instead that job is performed by thebeginInsertRows()
etc. you have in yourinsertRows()
implementation. Views noticedataChanged()
,beginInsertRows()
etc. and act accordingly to update.I will say one other thing. You seem to have a data model of rows of 4 string columns. I don't know what your objective/expertise is, but you could/might just use a
QStandardItemModel
and then you would not have to implement all this stuff. I don't know anything about QML or how this would interact with that, nor whether it's suitable for your case. -
@JonB Okay, I found an issue.
Finally I will use this QStandardItemModel.The problem was that. I have Model class included to Manager class. Manager class was included into main.
In main I declared an Manager class andqmlRegisterType<TableModelResults>("com.results.table", 1, 0, "TableModel");
. In QML as model I used TableModel, but to modify data I used an function from Manager class.First time used
qmlRegisterType
, thank You for help.