Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Updating view based on model update on Qt
Forum Updated to NodeBB v4.3 + New Features

Updating view based on model update on Qt

Scheduled Pinned Locked Moved Solved General and Desktop
9 Posts 2 Posters 3.9k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ozcanayO Offline
    ozcanayO Offline
    ozcanay
    wrote on last edited by
    #1

    I am having trouble updating my qtableview when the data wrapped by the model the view uses changes. I am not talking about appending/adding data here, I am specifically talking about modifying existing data. View is only updated when I scroll or select cell(s). In other words, it is only updated when repainting is triggered (this is my guess actually). So, this made me think that calling update() on widget when the data is modified would suffice, but this was not the case.

    I am adding data to the model programmatically, user cannot modify or add data in the model.

    Here is the minimal, reproducible example:

    mymodel.h:

    #pragma once
    
    #include <QAbstractTableModel>
    #include <QVector>
    
    #include "myobject.h"
    
    class MyModel : public QAbstractTableModel
    {
        Q_OBJECT
    private:
        QVector<MyObject> my_objects_; /// underlying data structure for the model.
    public:
        MyModel(QObject * parent = {});
        int rowCount(const QModelIndex &) const override;
        int columnCount(const QModelIndex &) const override;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::EditRole) const override;
    
        void add(const MyObject& my_object);
        void edit(const MyObject& my_object);
    };
    

    mymodel.cpp:

    #include "mymodel.h"
    
    #include <QDebug>
    #include <iostream>
    
    MyModel::MyModel(QObject * parent)
        : QAbstractTableModel{parent}
    {
    }
    
    int MyModel::rowCount(const QModelIndex &) const
    {
        return my_objects_.count();
    }
    
    int MyModel::columnCount(const QModelIndex &) const {
        return 4;
    }
    
    QVariant MyModel::data(const QModelIndex &index, int role) const {
       const auto row = index.row();
       if(row == -1) {
           return {};
       }
    
       const auto my_object = my_objects_[row];
    
       if(role == Qt::DisplayRole) {
           switch (index.column()) {
               case 0: return my_object.id;
               case 1: return my_object.a;
               case 2: return my_object.b;
               case 3: return my_object.c;
               default: return {};
           };
       }
    
       return {};
    }
    
    QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const {
       if (orientation != Qt::Horizontal || role != Qt::DisplayRole) {
           return {};
       }
    
       switch (section) {
           case 0: return "id";
           case 1: return "a";
           case 2: return "b";
           case 3: return "c";
           default: return {};
       }
    }
    
    void MyModel::add(const MyObject &my_object)
    {
        beginInsertRows({}, my_objects_.count(), my_objects_.count());
        my_objects_.push_back(my_object);
        endInsertRows();
    }
    
    void MyModel::edit(const MyObject& my_object) {
        const auto id = my_object.id;
    
        for(auto& my_object_ : my_objects_) {
            if(my_object_.id == id) {
    
                my_object_.a = my_object.a;
                my_object_.b = my_object.b;
                my_object_.c = my_object.c;
                /// should I use dataChanged signal here? If so, how?
    
                break;
            }
        }
    }
    
    

    myobject.h:

    #pragma once
    
    #include <QString>
    
    struct MyObject {
        int id;
        int a;
        double b;
        QString c;
    };
    

    mainwindow.h:

    #pragma once
    
    #include <QMainWindow>
    #include "mymodel.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private slots:
        void onEditClicked();
    
    private:
        Ui::MainWindow *ui;
        MyModel my_model_;
    };
    

    mainwindow.cpp:

    #include "mainwindow.h"
    #include "./ui_mainwindow.h"
    
    const static QVector<MyObject> g_my_objects{
        {0, 1, 1.1, "object1"},
        {1, 11, 1.2, "object2"},
        {2, 12, 1.3, "object3"},
        {3, 13, 1.4, "object4"},
        {4, 14, 1.5, "object5"},
        {5, 15, 1.6, "object6"},
        {6, 16, 1.7, "object7"},
        {7, 17, 1.8, "object8"}
    };
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    
        ui->tableView->setModel(&my_model_);
    
        for(const auto& g_my_object : g_my_objects) {
            my_model_.add(g_my_object);
        }
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::onEditClicked()
    {
        qDebug() << __PRETTY_FUNCTION__;
    
        my_model_.edit({2, 22222, 2222.222, "myobject22222"});
    
        update(); /// seems to be doing nothing.
    }
    

    main.cpp:

    #include "mainwindow.h"
    
    #include <QApplication>
    #include <QVector>
    
    #include "myobject.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }
    

    mainwindow.ui:

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>800</width>
        <height>600</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="QPushButton" name="pushButton">
          <property name="text">
           <string>Edit</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QTableView" name="tableView"/>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>800</width>
         <height>23</height>
        </rect>
       </property>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
     </widget>
     <resources/>
     <connections>
      <connection>
       <sender>pushButton</sender>
       <signal>clicked()</signal>
       <receiver>MainWindow</receiver>
       <slot>onEditClicked()</slot>
       <hints>
        <hint type="sourcelabel">
         <x>604</x>
         <y>41</y>
        </hint>
        <hint type="destinationlabel">
         <x>951</x>
         <y>20</y>
        </hint>
       </hints>
      </connection>
     </connections>
     <slots>
      <slot>onEditClicked()</slot>
     </slots>
    </ui>
    

    CMakeLists.txt:

    cmake_minimum_required(VERSION 3.5)
    
    project(model_view_example VERSION 0.1 LANGUAGES CXX)
    
    set(CMAKE_INCLUDE_CURRENT_DIR ON)
    
    set(CMAKE_AUTOUIC ON)
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTORCC ON)
    
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
    find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
    
    set(PROJECT_SOURCES
            main.cpp
            mainwindow.cpp
            mymodel.cpp
            mainwindow.h
            mainwindow.ui
    )
    
    if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
        qt_add_executable(model_view_example
            MANUAL_FINALIZATION
            ${PROJECT_SOURCES}
        )
    # Define target properties for Android with Qt 6 as:
    #    set_property(TARGET model_view_example APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
    #                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
    # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
    else()
        if(ANDROID)
            add_library(model_view_example SHARED
                ${PROJECT_SOURCES}
            )
    # Define properties for Android with Qt 5 after find_package() calls as:
    #    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
        else()
            add_executable(model_view_example
                ${PROJECT_SOURCES}
            )
        endif()
    endif()
    
    target_link_libraries(model_view_example PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
    
    set_target_properties(model_view_example PROPERTIES
        MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
        MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
        MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
        MACOSX_BUNDLE TRUE
        WIN32_EXECUTABLE TRUE
    )
    
    if(QT_VERSION_MAJOR EQUAL 6)
        qt_finalize_executable(model_view_example)
    endif()
    

    So, why the view is not updated when I call update()? Is there something wrong about my implementation?

    Here, I clicked on edit button, but the view only got updated with its new value when I clicked on a cell:

    enter image description here

    I feel like Qt MVC examples do not really map well to how I do it here. I do not know how I can implement this logic in a more Qt-ish way if possible.

    Here is the github repo for the minimal project:

    https://github.com/ozcanay/qt_model_view_example

    JonBJ 1 Reply Last reply
    0
    • ozcanayO ozcanay

      What I changed with respect to your feedback is as follows:

      bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
      {
          qDebug() << __PRETTY_FUNCTION__ << ", row: " << index.row() << ", column:" << index.column();
      
          if (role == Qt::EditRole) {
              if (!checkIndex(index))
                  return false;
      
              my_objects_[index.row()] = qvariant_cast<MyObject>(value);
              emit dataChanged(index, index);
      
              return true;
          }
      
          return false;
      }
      

      myobject.h

      #pragma once
      
      #include <QString>
      #include <QMetaType>
      
      struct MyObject {
          int id;
          int a;
          double b;
          QString c;
      };
      
      Q_DECLARE_METATYPE(MyObject)
      

      However, still no improvement on the behavior. Btw, you are right that I meant to call ui->tableView->update(), but that does not work as well.

      setData method is not even called when I programmatically edit the model. However, when I make cells editable via:

      Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
      {
          return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
      }
      

      then of course setData method is called when I edit a cell because user is editing the model, however I do not want user to edit value in the table directly, it has to be edited by the underlying logic imposed by the code. Specifically, only Model::edit method should be editing the model.

      I feel like what I am trying to do should have been straightforward, what am I still missing here?

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #4

      @ozcanay
      You have written a setData(), but as you say you are not calling that from your MyModel::edit() method. That seems to look up some row via my_object_.id == id and changes it. But the model does not tell the view this has happened. Where you wrote

      /// should I use dataChanged signal here? If so, how?

      I said, yes, you must do that, passing the correct index to dataChanged() for whichever row item you alter.

      ozcanayO 1 Reply Last reply
      1
      • ozcanayO ozcanay

        I am having trouble updating my qtableview when the data wrapped by the model the view uses changes. I am not talking about appending/adding data here, I am specifically talking about modifying existing data. View is only updated when I scroll or select cell(s). In other words, it is only updated when repainting is triggered (this is my guess actually). So, this made me think that calling update() on widget when the data is modified would suffice, but this was not the case.

        I am adding data to the model programmatically, user cannot modify or add data in the model.

        Here is the minimal, reproducible example:

        mymodel.h:

        #pragma once
        
        #include <QAbstractTableModel>
        #include <QVector>
        
        #include "myobject.h"
        
        class MyModel : public QAbstractTableModel
        {
            Q_OBJECT
        private:
            QVector<MyObject> my_objects_; /// underlying data structure for the model.
        public:
            MyModel(QObject * parent = {});
            int rowCount(const QModelIndex &) const override;
            int columnCount(const QModelIndex &) const override;
            QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
            QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::EditRole) const override;
        
            void add(const MyObject& my_object);
            void edit(const MyObject& my_object);
        };
        

        mymodel.cpp:

        #include "mymodel.h"
        
        #include <QDebug>
        #include <iostream>
        
        MyModel::MyModel(QObject * parent)
            : QAbstractTableModel{parent}
        {
        }
        
        int MyModel::rowCount(const QModelIndex &) const
        {
            return my_objects_.count();
        }
        
        int MyModel::columnCount(const QModelIndex &) const {
            return 4;
        }
        
        QVariant MyModel::data(const QModelIndex &index, int role) const {
           const auto row = index.row();
           if(row == -1) {
               return {};
           }
        
           const auto my_object = my_objects_[row];
        
           if(role == Qt::DisplayRole) {
               switch (index.column()) {
                   case 0: return my_object.id;
                   case 1: return my_object.a;
                   case 2: return my_object.b;
                   case 3: return my_object.c;
                   default: return {};
               };
           }
        
           return {};
        }
        
        QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const {
           if (orientation != Qt::Horizontal || role != Qt::DisplayRole) {
               return {};
           }
        
           switch (section) {
               case 0: return "id";
               case 1: return "a";
               case 2: return "b";
               case 3: return "c";
               default: return {};
           }
        }
        
        void MyModel::add(const MyObject &my_object)
        {
            beginInsertRows({}, my_objects_.count(), my_objects_.count());
            my_objects_.push_back(my_object);
            endInsertRows();
        }
        
        void MyModel::edit(const MyObject& my_object) {
            const auto id = my_object.id;
        
            for(auto& my_object_ : my_objects_) {
                if(my_object_.id == id) {
        
                    my_object_.a = my_object.a;
                    my_object_.b = my_object.b;
                    my_object_.c = my_object.c;
                    /// should I use dataChanged signal here? If so, how?
        
                    break;
                }
            }
        }
        
        

        myobject.h:

        #pragma once
        
        #include <QString>
        
        struct MyObject {
            int id;
            int a;
            double b;
            QString c;
        };
        

        mainwindow.h:

        #pragma once
        
        #include <QMainWindow>
        #include "mymodel.h"
        
        QT_BEGIN_NAMESPACE
        namespace Ui { class MainWindow; }
        QT_END_NAMESPACE
        
        class MainWindow : public QMainWindow
        {
            Q_OBJECT
        
        public:
            MainWindow(QWidget *parent = nullptr);
            ~MainWindow();
        
        private slots:
            void onEditClicked();
        
        private:
            Ui::MainWindow *ui;
            MyModel my_model_;
        };
        

        mainwindow.cpp:

        #include "mainwindow.h"
        #include "./ui_mainwindow.h"
        
        const static QVector<MyObject> g_my_objects{
            {0, 1, 1.1, "object1"},
            {1, 11, 1.2, "object2"},
            {2, 12, 1.3, "object3"},
            {3, 13, 1.4, "object4"},
            {4, 14, 1.5, "object5"},
            {5, 15, 1.6, "object6"},
            {6, 16, 1.7, "object7"},
            {7, 17, 1.8, "object8"}
        };
        
        MainWindow::MainWindow(QWidget *parent)
            : QMainWindow(parent)
            , ui(new Ui::MainWindow)
        {
            ui->setupUi(this);
            ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
        
            ui->tableView->setModel(&my_model_);
        
            for(const auto& g_my_object : g_my_objects) {
                my_model_.add(g_my_object);
            }
        }
        
        MainWindow::~MainWindow()
        {
            delete ui;
        }
        
        void MainWindow::onEditClicked()
        {
            qDebug() << __PRETTY_FUNCTION__;
        
            my_model_.edit({2, 22222, 2222.222, "myobject22222"});
        
            update(); /// seems to be doing nothing.
        }
        

        main.cpp:

        #include "mainwindow.h"
        
        #include <QApplication>
        #include <QVector>
        
        #include "myobject.h"
        
        int main(int argc, char *argv[])
        {
            QApplication a(argc, argv);
            MainWindow w;
            w.show();
            return a.exec();
        }
        

        mainwindow.ui:

        <?xml version="1.0" encoding="UTF-8"?>
        <ui version="4.0">
         <class>MainWindow</class>
         <widget class="QMainWindow" name="MainWindow">
          <property name="geometry">
           <rect>
            <x>0</x>
            <y>0</y>
            <width>800</width>
            <height>600</height>
           </rect>
          </property>
          <property name="windowTitle">
           <string>MainWindow</string>
          </property>
          <widget class="QWidget" name="centralwidget">
           <layout class="QVBoxLayout" name="verticalLayout">
            <item>
             <widget class="QPushButton" name="pushButton">
              <property name="text">
               <string>Edit</string>
              </property>
             </widget>
            </item>
            <item>
             <widget class="QTableView" name="tableView"/>
            </item>
           </layout>
          </widget>
          <widget class="QMenuBar" name="menubar">
           <property name="geometry">
            <rect>
             <x>0</x>
             <y>0</y>
             <width>800</width>
             <height>23</height>
            </rect>
           </property>
          </widget>
          <widget class="QStatusBar" name="statusbar"/>
         </widget>
         <resources/>
         <connections>
          <connection>
           <sender>pushButton</sender>
           <signal>clicked()</signal>
           <receiver>MainWindow</receiver>
           <slot>onEditClicked()</slot>
           <hints>
            <hint type="sourcelabel">
             <x>604</x>
             <y>41</y>
            </hint>
            <hint type="destinationlabel">
             <x>951</x>
             <y>20</y>
            </hint>
           </hints>
          </connection>
         </connections>
         <slots>
          <slot>onEditClicked()</slot>
         </slots>
        </ui>
        

        CMakeLists.txt:

        cmake_minimum_required(VERSION 3.5)
        
        project(model_view_example VERSION 0.1 LANGUAGES CXX)
        
        set(CMAKE_INCLUDE_CURRENT_DIR ON)
        
        set(CMAKE_AUTOUIC ON)
        set(CMAKE_AUTOMOC ON)
        set(CMAKE_AUTORCC ON)
        
        set(CMAKE_CXX_STANDARD 17)
        set(CMAKE_CXX_STANDARD_REQUIRED ON)
        
        find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
        find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
        
        set(PROJECT_SOURCES
                main.cpp
                mainwindow.cpp
                mymodel.cpp
                mainwindow.h
                mainwindow.ui
        )
        
        if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
            qt_add_executable(model_view_example
                MANUAL_FINALIZATION
                ${PROJECT_SOURCES}
            )
        # Define target properties for Android with Qt 6 as:
        #    set_property(TARGET model_view_example APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
        #                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
        # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
        else()
            if(ANDROID)
                add_library(model_view_example SHARED
                    ${PROJECT_SOURCES}
                )
        # Define properties for Android with Qt 5 after find_package() calls as:
        #    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
            else()
                add_executable(model_view_example
                    ${PROJECT_SOURCES}
                )
            endif()
        endif()
        
        target_link_libraries(model_view_example PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
        
        set_target_properties(model_view_example PROPERTIES
            MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
            MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
            MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
            MACOSX_BUNDLE TRUE
            WIN32_EXECUTABLE TRUE
        )
        
        if(QT_VERSION_MAJOR EQUAL 6)
            qt_finalize_executable(model_view_example)
        endif()
        

        So, why the view is not updated when I call update()? Is there something wrong about my implementation?

        Here, I clicked on edit button, but the view only got updated with its new value when I clicked on a cell:

        enter image description here

        I feel like Qt MVC examples do not really map well to how I do it here. I do not know how I can implement this logic in a more Qt-ish way if possible.

        Here is the github repo for the minimal project:

        https://github.com/ozcanay/qt_model_view_example

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #2

        @ozcanay
        You do not seem to implement MyModel::setData() if your model is editable? https://doc.qt.io/qt-5/qabstracttablemodel.html#subclassing

        Editable models need to implement setData()

        Your MyModel::edit() changes the model data, but without setData() or emit dataChanged() the view does not know anything has changed. Implement that in the model, remove the update() in the view, behaviour better now?

        [BTW, your update() was on MainWindow. I think you meant to call it on ui->tableView.]

        /// should I use dataChanged signal here? If so, how?

        You must identify the (row,column) in the model for any item you change in your backing model. From that you can generate a QModelIndex index(row, column) in the model to pass to dataChanged().

        If you're implementing QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) essentially you should be calling emit dataChanged(index, index).

        1 Reply Last reply
        2
        • ozcanayO Offline
          ozcanayO Offline
          ozcanay
          wrote on last edited by ozcanay
          #3

          What I changed with respect to your feedback is as follows:

          bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
          {
              qDebug() << __PRETTY_FUNCTION__ << ", row: " << index.row() << ", column:" << index.column();
          
              if (role == Qt::EditRole) {
                  if (!checkIndex(index))
                      return false;
          
                  my_objects_[index.row()] = qvariant_cast<MyObject>(value);
                  emit dataChanged(index, index);
          
                  return true;
              }
          
              return false;
          }
          

          myobject.h

          #pragma once
          
          #include <QString>
          #include <QMetaType>
          
          struct MyObject {
              int id;
              int a;
              double b;
              QString c;
          };
          
          Q_DECLARE_METATYPE(MyObject)
          

          However, still no improvement on the behavior. Btw, you are right that I meant to call ui->tableView->update(), but that does not work as well.

          setData method is not even called when I programmatically edit the model. However, when I make cells editable via:

          Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
          {
              return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
          }
          

          then of course setData method is called when I edit a cell because user is editing the model, however I do not want user to edit value in the table directly, it has to be edited by the underlying logic imposed by the code. Specifically, only Model::edit method should be editing the model.

          I feel like what I am trying to do should have been straightforward, what am I still missing here?

          JonBJ 1 Reply Last reply
          0
          • ozcanayO ozcanay

            What I changed with respect to your feedback is as follows:

            bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
            {
                qDebug() << __PRETTY_FUNCTION__ << ", row: " << index.row() << ", column:" << index.column();
            
                if (role == Qt::EditRole) {
                    if (!checkIndex(index))
                        return false;
            
                    my_objects_[index.row()] = qvariant_cast<MyObject>(value);
                    emit dataChanged(index, index);
            
                    return true;
                }
            
                return false;
            }
            

            myobject.h

            #pragma once
            
            #include <QString>
            #include <QMetaType>
            
            struct MyObject {
                int id;
                int a;
                double b;
                QString c;
            };
            
            Q_DECLARE_METATYPE(MyObject)
            

            However, still no improvement on the behavior. Btw, you are right that I meant to call ui->tableView->update(), but that does not work as well.

            setData method is not even called when I programmatically edit the model. However, when I make cells editable via:

            Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
            {
                return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
            }
            

            then of course setData method is called when I edit a cell because user is editing the model, however I do not want user to edit value in the table directly, it has to be edited by the underlying logic imposed by the code. Specifically, only Model::edit method should be editing the model.

            I feel like what I am trying to do should have been straightforward, what am I still missing here?

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #4

            @ozcanay
            You have written a setData(), but as you say you are not calling that from your MyModel::edit() method. That seems to look up some row via my_object_.id == id and changes it. But the model does not tell the view this has happened. Where you wrote

            /// should I use dataChanged signal here? If so, how?

            I said, yes, you must do that, passing the correct index to dataChanged() for whichever row item you alter.

            ozcanayO 1 Reply Last reply
            1
            • JonBJ JonB

              @ozcanay
              You have written a setData(), but as you say you are not calling that from your MyModel::edit() method. That seems to look up some row via my_object_.id == id and changes it. But the model does not tell the view this has happened. Where you wrote

              /// should I use dataChanged signal here? If so, how?

              I said, yes, you must do that, passing the correct index to dataChanged() for whichever row item you alter.

              ozcanayO Offline
              ozcanayO Offline
              ozcanay
              wrote on last edited by
              #5

              @JonB

              Now, it seems to be working:

              
              void MyModel::edit(const MyObject& my_object) {
                  const auto id = my_object.id;
                  for(auto i = 0u; i < my_objects_.size(); ++i) {
                      if(my_objects_[i].id == id) {
                          const auto var = QVariant::fromValue(my_object);
                          setData(index(i, 1), var);
                          setData(index(i, 2), var);
                          setData(index(i, 3), var);
                          break;
                      }
                  }
              }
              
              bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
              {
                  qDebug() << __PRETTY_FUNCTION__ << ", row: " << index.row() << ", column:" << index.column();
              
                  if (role == Qt::EditRole) {
                      if (!checkIndex(index))
                          return false;
              
                      my_objects_[index.row()] = qvariant_cast<MyObject>(value);
                      emit dataChanged(index, index);
              
                      return true;
                  }
              
                  return false;
              }
              

              The thing I did not know was how to create a QModelIndex in edit() method. QAbtractItemModel::index() was what I was looking for. dataChanged() signal is emitted in setData() method, so I do not have to explicitly emit that signal in edit() method. Thanks for your help. One thing bothering me is 3 separate calls to setData in my code, since I want to update 3 columns. Is there a way to say for example "update all of the values in this row" given the input QVariant.

              JonBJ 1 Reply Last reply
              0
              • ozcanayO ozcanay

                @JonB

                Now, it seems to be working:

                
                void MyModel::edit(const MyObject& my_object) {
                    const auto id = my_object.id;
                    for(auto i = 0u; i < my_objects_.size(); ++i) {
                        if(my_objects_[i].id == id) {
                            const auto var = QVariant::fromValue(my_object);
                            setData(index(i, 1), var);
                            setData(index(i, 2), var);
                            setData(index(i, 3), var);
                            break;
                        }
                    }
                }
                
                bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
                {
                    qDebug() << __PRETTY_FUNCTION__ << ", row: " << index.row() << ", column:" << index.column();
                
                    if (role == Qt::EditRole) {
                        if (!checkIndex(index))
                            return false;
                
                        my_objects_[index.row()] = qvariant_cast<MyObject>(value);
                        emit dataChanged(index, index);
                
                        return true;
                    }
                
                    return false;
                }
                

                The thing I did not know was how to create a QModelIndex in edit() method. QAbtractItemModel::index() was what I was looking for. dataChanged() signal is emitted in setData() method, so I do not have to explicitly emit that signal in edit() method. Thanks for your help. One thing bothering me is 3 separate calls to setData in my code, since I want to update 3 columns. Is there a way to say for example "update all of the values in this row" given the input QVariant.

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by
                #6

                @ozcanay
                Good that it works, shows the issue was the right dataChanged() emission(s) so that the view knew what was changed in the model.

                setData() can only alter a single (row, column) element at a time, and hence emit separate dataChanged() signals. In your case you do not need to go via setData() in your own edit() method. Now that you have identified the row number via a for (auto i = 0; ++i) loop you can revert to your first approach, something like:

                    for(auto i = 0u; i < my_objects_.size(); ++i) {
                        if(my_objects_[i].id == id) {
                            my_object_.a = my_object.a;
                            my_object_.b = my_object.b;
                            my_object_.c = my_object.c;
                            /// should I use dataChanged signal here? If so, how?
                            emit dataChanged(index(i, 1), index(i, 3));    // not sure about your column numbers here
                            break;
                        }
                    }
                
                
                ozcanayO 1 Reply Last reply
                1
                • JonBJ JonB

                  @ozcanay
                  Good that it works, shows the issue was the right dataChanged() emission(s) so that the view knew what was changed in the model.

                  setData() can only alter a single (row, column) element at a time, and hence emit separate dataChanged() signals. In your case you do not need to go via setData() in your own edit() method. Now that you have identified the row number via a for (auto i = 0; ++i) loop you can revert to your first approach, something like:

                      for(auto i = 0u; i < my_objects_.size(); ++i) {
                          if(my_objects_[i].id == id) {
                              my_object_.a = my_object.a;
                              my_object_.b = my_object.b;
                              my_object_.c = my_object.c;
                              /// should I use dataChanged signal here? If so, how?
                              emit dataChanged(index(i, 1), index(i, 3));    // not sure about your column numbers here
                              break;
                          }
                      }
                  
                  
                  ozcanayO Offline
                  ozcanayO Offline
                  ozcanay
                  wrote on last edited by ozcanay
                  #7

                  @JonB

                  void MyModel::edit(const MyObject& my_object) {
                      const auto id = my_object.id;
                      for(auto i = 0u; i < my_objects_.size(); ++i) {
                          if(my_objects_[i].id == id) {
                              my_objects_[i].a = my_object.a;
                              my_objects_[i].b = my_object.b;
                              my_objects_[i].c = my_object.c;
                  
                              emit dataChanged(index(i, 1), index(i, 3));    // not sure about your column numbers here
                              break;
                          }
                      }
                  }
                  

                  works like a charm. Then, should I conclude that setData is not needed? I have removed setData method and it still works.

                  JonBJ 1 Reply Last reply
                  0
                  • ozcanayO ozcanay

                    @JonB

                    void MyModel::edit(const MyObject& my_object) {
                        const auto id = my_object.id;
                        for(auto i = 0u; i < my_objects_.size(); ++i) {
                            if(my_objects_[i].id == id) {
                                my_objects_[i].a = my_object.a;
                                my_objects_[i].b = my_object.b;
                                my_objects_[i].c = my_object.c;
                    
                                emit dataChanged(index(i, 1), index(i, 3));    // not sure about your column numbers here
                                break;
                            }
                        }
                    }
                    

                    works like a charm. Then, should I conclude that setData is not needed? I have removed setData method and it still works.

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #8

                    @ozcanay
                    As I think you said earlier, setData() will for one thing be used if you make your view editable. If you only ever do updates programmatically via your edit() method there is nothing that needs to call it, no point mapping your changes over to setData() if you have internal code to do it directly. The vital thing is the emit dataChanged() covering the rows/columns you have changed; from that the view knows which cells to refresh.

                    ozcanayO 1 Reply Last reply
                    1
                    • JonBJ JonB

                      @ozcanay
                      As I think you said earlier, setData() will for one thing be used if you make your view editable. If you only ever do updates programmatically via your edit() method there is nothing that needs to call it, no point mapping your changes over to setData() if you have internal code to do it directly. The vital thing is the emit dataChanged() covering the rows/columns you have changed; from that the view knows which cells to refresh.

                      ozcanayO Offline
                      ozcanayO Offline
                      ozcanay
                      wrote on last edited by ozcanay
                      #9

                      @JonB
                      Yes, the current version of edit() method is basically doing what setData() method is supposed to do in my scenario. As you said, given that users will not be able to edit cells in the table, i.e. the model will not be editable by users, setData() method seems to be redundant. Thanks for elaborating.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved