QAbstractTable subclass. Data doesn't update.
-
I'm having a bit of a time understanding the abstract table model. I implemented a subclass with the following code in the data() and setData() functions.
QVariant SecureRoomModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); SecureRoomControl *ctl = m_deviceInfoList[index.row()]; int column = index.column(); qDebug() << "data" << role; switch (role) { case Qt::DisplayRole: qDebug() << "Qt DisplayRole"; switch (column) { case HEADER_IP: qDebug() << "IP DisplayRole" << ctl->secureRoomData()->ipAddress(); return ctl->secureRoomData()->ipAddress(); case HEADER_SERIAL: return genSerialImage(ctl); default: return QVariant(); } default: return QVariant(); } } bool SecureRoomModel::setData(const QModelIndex &index, const QVariant &value, int role) { qDebug() << "setData"; int row = index.row(); int column = index.column(); if (row != m_deviceInfoList.size() - 1) return false; SecureRoomControl *ctl = m_deviceInfoList[row]; switch(role) { case Qt::EditRole: if (column == HEADER_IP) ctl->ipAddressChanged(value.toString()); else if (column == HEADER_SERIAL) ctl->serialNumberChanged(value.toString()); emit dataChanged(index, index, QVector<int>() << Qt::DisplayRole); break; default: break; } return true; }
When I edit data, it disappears from the table view. In other words, I double click in the HEADER_IP column cell of a row, type in information and press Enter, the backing data of my model was changed, but it disappears in the table view. It seems that all the rest of the data calls are called with the Qt::EditRole.
Is this enough code to help? Any help appreciated.
-
Hi,
if (row != m_deviceInfoList.size() - 1) << looks fishy return false;
Shouldn't that be
if (row >= m_deviceInfoList.size())
? -
@SGaist Thanks for the reply. The reason I have that check there is to only have the last row editable.
My next attempt is to show some new qDebug messages.
QVariant SecureRoomModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); SecureRoomControl *ctl = m_deviceInfoList[index.row()]; int column = index.column(); qDebug() << "data" << role; switch (role) { case Qt::DisplayRole: switch (column) { case HEADER_IP: qDebug() << "IP DisplayRole" << ctl->secureRoomData()->ipAddress(); return ctl->secureRoomData()->ipAddress(); case HEADER_SERIAL: return genSerialImage(ctl); ... default: return QVariant(); } default: return QVariant(); } } bool SecureRoomModel::setData(const QModelIndex &index, const QVariant &value, int role) { int row = index.row(); qDebug() << "setData" << row << "of" << m_deviceInfoList.size() - 1; int column = index.column(); if (row != m_deviceInfoList.size() - 1) return false; SecureRoomControl *ctl = m_deviceInfoList[row]; switch(role) { case Qt::EditRole: if (column == HEADER_IP) ctl->ipAddressChanged(value.toString()); else if (column == HEADER_SERIAL) ctl->serialNumberChanged(value.toString()); emit dataChanged(index, index, QVector<int>() << Qt::DisplayRole); break; default: break; } return true; } void SecureRoomModel::onDataChanged(int row) { //update everything on that row QModelIndex top = createIndex(row, 0); QModelIndex bottom = createIndex(row, HEADER_STATUS); qDebug() << "onDataChanged row=" << row << "ip=" << m_deviceInfoList[row]->secureRoomData()->ipAddress(); emit dataChanged(top, bottom, QVector<int>() << Qt::DisplayRole); emit saveRequired(); } bool SecureRoomModel::addRow(RestaurantSettings *restaurantSettings) { int row = m_deviceInfoList.size(); SecureRoomControl *ctl = new SecureRoomControl(restaurantSettings, row); QObject::connect(ctl, &SecureRoomControl::dataChanged, this, &SecureRoomModel::onDataChanged); QObject::connect(ctl, &SecureRoomControl::dataDeviceInfoComplete, this, &SecureRoomModel::onDataDeviceInfoComplete); QObject::connect(ctl, &SecureRoomControl::validIpAddress, this, &SecureRoomModel::onValidIpAddress); QObject::connect(ctl, &SecureRoomControl::validSerialNumber, this, &SecureRoomModel::onValidSerialNumber); QObject::connect(this, &SecureRoomModel::destroyed, ctl, &SecureRoomControl::deleteLater); QThread *thr = new QThread; QObject::connect(ctl, &SecureRoomControl::destroyed, thr, &QThread::quit); QObject::connect(thr, &QThread::finished, thr, &QThread::deleteLater); ctl->moveToThread(thr); thr->start(); beginInsertRows(QModelIndex(), row, row); m_deviceInfoList.append(ctl); m_ipCellEnabled = true; m_serialCellEnabled = false; endInsertRows(); emit dataChanged(this->createIndex(row, 0), this->createIndex(row, this->columnCount(QModelIndex()))); return true; }
The onDataChanged() function is a slot for handling signals by SecureRoomControl, which runs in a different thread, to notify the Model that data was changed by the controller and the model should update all of the columns. I wonder if it should be in the same thread.
I fire up the table view, double click on the HEADER_IP column cell in the first row. I get the following output after double clicking.
data 3 data 10 data 2 data 6 data 7 data 9 data 10 data 1 data 0 IP DisplayRole "" data 8 data 2
Then I type in 192.168.14.50 and press the Enter key and see the following output.
setData 0 of 0 SecureRoomControl::ipAddressChanged "192.168.14.50" acquiringMac data 2 onDataChanged row= 0 ip= "192.168.14.50" data 2 data 2 onDataChanged row= 0 ip= "192.168.14.50" setMac onDataChanged row= 0 ip= "192.168.14.50" onDataChanged row= 0 ip= "192.168.14.50" onDataChanged row= 0 ip= "192.168.14.50"
The 2 value for the role debug is Qt::EditRole. No Qt::DisplayRole calls to data().
-
Then you should also reimplement the flags method. It will be cleaner and make things clearer when reading your code.
As for the dataChanged, I'd start with the default value, you can always optimise later on.
If you have multiple threads accessing the model (that's completely valid), you should protect these access like for any resources accessed by multiple threads.
-
Try passing your model through the Model Test it's much easier to detect what you are doing wrong that way
@SGaist said in QAbstractTable subclass. Data doesn't update.:
Then you should also reimplement the flags method
That is the correct way to restrict what indexes can be edited and not.
If you have multiple threads accessing the model (that's completely valid)
It's valid if the model itself lives in the GUI thread. The views calls methods on the model directly so unless you lock everything down you'll have a race condition.
-
@SGaist Thanks for your help. Great suggestions! I decided to start over with a new model and add the features step by step according to the tutorials/examples. Got it working. Marking the issue as solved.
For anyone who reads this, if you are having trouble with the model, start with the simple read/write examples, getting them to work. If you have specialized controller needs, add them in step by step, checking as you go.