Congratulations to our 2022 Qt Champions!

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
      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); 
      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,

    [edit: added missing coding tags ``` SGaist]

  • Lifetime Qt Champion

    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
    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; };

    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) :
    ui(new Ui::workItem)
    (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 );


    the over-arching class, which is the one with the table view widget in it:
    class workItem : public QWidget

    QString tabLabel;
    int sessionItemIndex;
    workItemListProvenance listProvenance;
    QList<workItemListEntry> workItemListEntries;

    explicit workItem(QWidget *parent = 0);
    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); }

    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; };

    QString workItemListFileName;

  • 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 :-)

  • Lifetime Qt Champion

    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 :)

Log in to reply