基于 Qt 上的模型更新更新视图

Updating view based on model update on Qt

当视图使用的模型包装的数据发生变化时,我无法更新我的 qtableview。我这里不是说 appending/adding 数据,我是专门说修改现有数据。视图仅在我滚动或 select 个单元格时更新。换句话说,它仅在触发重绘时更新(这实际上是我的猜测)。所以,这让我觉得当数据被修改时调用 widget 的 update() 就足够了,但事实并非如此。

我正在以编程方式向模型添加数据,用户无法在模型中修改或添加数据。

这是最小的、可重现的例子:

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

那么,为什么当我调用 update() 时视图没有更新?我的实现有问题吗?

在这里,我点击了编辑按钮,但是当我点击一个单元格时,视图只更新了它的新值:

我觉得 Qt MVC 示例并没有很好地映射到我在这里的操作方式。如果可能的话,我不知道如何以更像 Qt 的方式实现这个逻辑。

这是最小项目的 github 存储库:

https://github.com/ozcanay/qt_model_view_example

只要现有项目中的数据发生变化,您就应该发出信号 dataChanged。 QTableView 应该会自动更新。

参见:

我得到了 Qt forum 的帮助并解决了它。