Solved QTableView not updating despite attention to flags and explicit emit of dataChanged
-
Hello,
My first forum post so hopefully I will be clear.
I have an editable QTableView, or so I am trying to have.
I can select and type in fields, and internal debug tells me that setData is being called and the model internal values are updated. I do emit dataChanged, or so I think I do, but the data function never gets called and the values don't change.Here is my model class:
class workItemListViewingModel : public QAbstractTableModel { Q_OBJECT public: workItemListViewingModel(workItem *workItemParent) { parent = workItemParent; }; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) Q_DECL_OVERRIDE; Qt::ItemFlags flags(const QModelIndex & index) const Q_DECL_OVERRIDE { return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | QAbstractTableModel::flags(index) | QAbstractItemModel::flags(index); } signals: private: workItem *parent; };
Note that I do not have any slots... this seems wrong to me, since I guess data is a slot? But none of the online examples show exploit indication of this. I have surmised it is due to the fact that the setData can actually trigger multiple data calls, with a range of cells rather than just the one it was called with, by setting this range on the emit call. (In fact I need to use this, so great.)
Here is my setData code:
bool workItemListViewingModel::setData(const QModelIndex & index, const QVariant & value, int role) { workItemListEntry entry = parent->workItemList().at(index.row()); if (role == Qt::EditRole) { switch (index.column()) { case subjectID_col: { entry.setSubjectID(value.toString()); break; } case indication_col: { entry.setIndication(value.toString()); break; } default: { QErrorMessage *error = new QErrorMessage (0); error->showMessage(tr("Warning: Cannot edit this field in the application, need to change it at the source.")); return false; } } } emit dataChanged(index,index); return true; }
Can anyone help?
Many thanks,
Andy[edit: added missing coding tags ``` SGaist]
-
Hi and welcome to devnet,
No, setData is not a slot. You can see that in the documentation.
As for your code, are you sure that setData is called for the Edit Role ? In any case, you are modifying the local copy of entry, not what you have in workItemList.
On a side note, in your flags override, you should only call QAbstractTableModel::flags(index), that's the base class of your model.
-
@SGaist Thank you. Yes, I am sure that setData is being called, I have debug statement sheer. Entry is in workItem List; the table rows are entries, and the columns are fields in each entry.
Here is a little more of the code to try to give more to go on, since I still have the problem.
First is workItemListEntry class:
class workItemListEntry
{
public:
workItemListEntry() {};void setSubjectID(QString str) { subjectID = str; qDebug() << "in setSubjecId " << subjectID;};
const QString getSubjectID() const { return subjectID; qDebug() << "in getSubjecId " << subjectID;};void setIndication(QString str) { indication = str; };
const QString getIndication() const { return indication; };private:
QString subjectID, indication;
};and here is the "data" function in the model:
QVariant workItemListViewingModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
switch (index.column()) {
case subjectID_col: return parent->workItemList().at(index.row()).getSubjectID();
case indication_col: return parent->workItemList().at(index.row()).getIndication();
(there are more cases and a default, but I am trying to simplify to focus on the issue)
}
}
return QVariant();
}The code which sets the model in the first place is in the constructor of an over-arching class which has the workItemList:
workItem::workItem(QWidget *parent) :
QWidget(parent),
ui(new Ui::workItem)
{
ui->setupUi(this);
workItemListEntries.clear();
(more in here, which sets up the list and gets the first view of the table coming out perfectly, before trying to edit it)
workItemListViewingModel *wiListModel = new workItemListViewingModel(this);
ui->workItemList->setModel( wiListModel );
ui->workItemList->selectionModel()->selectedRows();
ui->workItemList->verticalHeader()->setVisible(false);
ui->workItemList->horizontalHeader()->setVisible(true);
ui->workItemList->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->workItemList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);}:
the over-arching class, which is the one with the table view widget in it:
class workItem : public QWidget
{
Q_OBJECTQString tabLabel;
int sessionItemIndex;
workItemListProvenance listProvenance;
QList<workItemListEntry> workItemListEntries;public:
explicit workItem(QWidget *parent = 0);
~workItem();
Ui::workItem *ui;
QList<workItemListEntry> &workItemList() { return workItemListEntries; };
void setSessionItemIndex(int index) { sessionItemIndex = index; }
void newWorkItemList();
enum saveFormat { Json, Binary };
bool loadWorkItemListFromFile(saveFormat saveFormat);
bool saveWorkItemListToFile(saveFormat saveFormat) const;
void readWorkItemList(const QJsonObject &json);
void writeWorkItemList(QJsonObject &json) const;
void logListProvenanceUpdate(QString step);public slots:
QString getTabLabel() { return(tabLabel); }signals:
void selectWorkItemButton_clicked(int sessionItemIndex);private slots:
void on_selectWorkItemButton_clicked() { emit selectWorkItemButton_clicked(sessionItemIndex); }
void on_targetPath_clicked() { QString pathFile=QFileDialog::getOpenFileName(this, tr("Path File"), "", tr("Path files (*.pth)")); };
void on_seriesFolder_clicked() { QString seriesFolder=QFileDialog::getExistingDirectory(this, tr("Series Folder"), ""); };
void on_seriesTypeSelector_activated(const QString &arg1) { std::cout << "selected " << std::endl; };
void on_addWorkItemButton_clicked() {};
void on_listFileNameButton_clicked() ;
void on_labelTabsWithWorkItemID_clicked() { labelTabsWith = WORKITEMID; };
void on_labelTabsWithPatientID_clicked() { labelTabsWith = PATIENTID; };
void on_labelTabsWithPatientName_clicked() { labelTabsWith = PATIENTNAME; };
void on_labelTabsWithSubjectID_clicked() { labelTabsWith = SUBJECTID; };
void on_labelTabsWithAlsoKnownAs_clicked() { labelTabsWith = ALSOKNOWNAS; };private:
QString workItemListFileName;
enum field { WORKITEMID, PATIENTID, PATIENTNAME, SUBJECTID, ALSOKNOWNAS } labelTabsWith;
}; -
In your setData overide:
workItemListEntry entry = parent->workItemList().at(index.row());
is making a copy so you are not updating your underlying data for your model.
Try using:
auto& entry = parent->workItemList()[index.row()];
-
@bsomervi wow... that is great. Of course I see it now, but would not have without your help. Both of you who replied! Thank you :-)
-
You're welcome !
Since you have it working now, please mark the thread as solved using the "Topic Tool" button so that other forum users may know a solution has been found :)
Also, while browsing the forum, consider up-voting answers that helped you, it will make them easier to find for other forum users :)