如何使用 Qt Custom TableModel(派生自 QAbstractTableModel)强制刷新已更改的单元格?

How to force refresh of changed cells with Qt Custom TableModel (deriving from QAbstractTableModel) ?

我是 Qt 新手。我正在尝试实施自定义 table 模型:

我有一个 class 数据源,它包含我的所有数据并在行更改时或在一行中仅更改某些值时触发事件(传统事件,而不是 Qt signa/slots)。这是一个简化版本:

class DataSource{
  public:
  int size();
  int nbColumns();
  const std::string& data(int row, int col);
  bool hasChanges();
};

我实现了一个 TableModel 作为我的数据源的包装器:

class MyTableModel : public QAbstractTableModel {
  public:

  int rowCount(const QModelIndex& parent) const {
      return datasource->size();
  }

  int columnCount(const QModelIndex& parent) const {
    return datasource->nbColumns();
  }

  QVariant data(const QModelIndex& index, int role) const {
    return QVariant(datasource.data(index.row(), index.column());
  }
};

我在计时器上刷新 table(我的应用中更新太多):

void timerRefresh(){
  if(datasource->hasChanges()){
    ui->tableView->repaint();
    ui->tableView->update();
  }
}

这不起作用(我原以为视图会在重绘时查询模型,但也许我应该调用其他方法?)

所以我在调用重绘之前添加了这个:

tableModel->update();

定义如下:

void MyTableModel::update()
{
  beginResetModel();
  endResetModel();
  //doesn't work: emit dataChanged(createIndex(0,0), createIndex(DataSource->size(), DataSource->nbColumns()));
}

它有效,但如果只更改一个单元格,我想应该有比重置整个模型更有效的方法?为什么 emit DataChanged 不起作用? 文档不清楚 Qt 框架调用了哪个函数以及我应该显式调用哪个函数...

编辑:感谢下面的评论,我明白了它是如何工作的。我不需要在 QTableView 本身上调用任何东西,我只需更新我的模型并调用
开始重置模型(); ... endResetModel();

或者只是当只有单元格发生变化时: 发出 dataChanged(...);

Qt 版本一直在变化。从一个版本更改为另一个版本是微不足道的。规划您的项目非常重要。您为用户界面 (ui) 选择的 Qt 小部件是导入的。 Qt Widgets 传输信号,您可以使用插槽连接到这些信号。取决于你想对你的项目做什么。在实现某些功能时,重要的是调用适当的功能,以便所有连接的视图都知道任何更改。 QAbstractTableModel 可能对想要实现自己的 table 模型或自定义委托的开发人员有用。 MVC(模型、视图和控制器)背后有一个理论。实施 MVC 将解决问题。您可以使用多种策略来实现它。起点是计划。 您可以使用 class DataSource 和 class MyTableModel 作为模型。缺少的是视图和控制器的定义。您的“#include”在 Qt 中也很重要。 QAbstractTableModel提供了标准的MVAPI来访问数据。我们可以使用 QtableView 来使用不同的视图来查看数据。必须实现的基本函数有:rowCount()、columnCount()、data()、headerData()。要使 Table模型成为 editable,它必须提供 insertRows()、removeRows()、setData() 和 flags() 函数的实现。


  1. 演示自定义 Table.


  1. main.cpp

//
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();

return a.exec();
}
//

  1. widget.h

//
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTableView>
#include <QListView>
#include <QLabel>
#include <QLineEdit>
#include <QTextBrowser>
#include <QDateEdit>
#include <QString>
//namespace Ui {
//class Widget;
//}
class MyTableModel;
class Widget : public QWidget
{
Q_OBJECT
public:
//explicit Widget(QObject t, QWidget *parent = 0);
Widget(QWidget *parent = 0);
~Widget();
public slots:
void updateView();
private:
//Ui::Widget *ui;
QTableView *tableView;
MyTableModel *tableModel;
QLineEdit *ledMessage;
QLabel *lblMessage;
QTextBrowser *logs;
QDateEdit *dateEdit;
};
#endif // WIDGET_H
//

  1. widget.cpp

//
#include <QtGui>
#include "mytablemodel.h"
#include "widget.h"
//#include "ui_widget.h"
#include <QGridLayout>

Widget::Widget(QWidget *parent) :
QWidget(parent)/*,
ui(new Ui::Widget*)*/
{
//ui->setupUi(this);
tableModel = new MyTableModel(this);
tableView = new QTableView;
tableView->setModel(tableModel);
lblMessage = new QLabel(tr("&Message:"));
ledMessage = new QLineEdit;
lblMessage->setBuddy(ledMessage);
dateEdit = new QDateEdit(QDate::currentDate());
logs = new QTextBrowser;
logs->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));

QGridLayout *layout = new QGridLayout;
layout->addWidget(lblMessage, 0, 0);
layout->addWidget(ledMessage, 0, 1);
layout->addWidget(dateEdit, 0, 2);
layout->addWidget(tableView, 1, 0, 1, 2);
layout->addWidget(logs, 2, 0, 1, 2);

setLayout(layout);
setWindowTitle(tr("Demo Custom Table."));
//connect(ledMessage, SIGNAL(textChanged(const QString &)),
//             tableModel, SLOT(newMessage(const QString &)));
//connect(tableModel, SIGNAL(updateMode()),
//             this, SLOT(updateView()));
connect(ledMessage, SIGNAL(editingFinished()), this, SLOT(updateView()));
}

Widget::~Widget()
{
//delete ui;
}

void Widget::updateView()
{
QModelIndex p = tableView->currentIndex();
tableModel->update(p,ledMessage->text(), dateEdit->text());
logs->append(tr("%1 : ").arg(p.row()));
logs->append(ledMessage->text());
}
//

  1. mytablemodel.h

//
#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H

#include <QAbstractTableModel>
#include <QLabel>
//#include <QDateEdit>
#include <QDateTime>
#include "datasource.h"

class MyTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
//explicit MyTableModel(QObject t, QObject t2,QObject *parent = 0);
MyTableModel(QObject *parent = 0);
MyTableModel(QList< QPair<QString,QString> > t, QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int part, Qt::Orientation orient, int role) const;
void update(const QModelIndex &parent,const QString &mes, const QString &date);
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex &index) const;
bool insertRows(int position, int rows, const QModelIndex &index=QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index=QModelIndex());

signals:
void updateMod();

public slots:
void newMessage(const QString &mes);

protected:
void update(const QModelIndex &parent);

private:
DataSource *datasource;
QPair<QString,QString> message;
QList< QPair<QString,QString> > messageList;
QList<QString> dList;
int listSize;
int col;
QLabel *lblNumber;
QDateTime *dTime;
//QDateEdit *dateEdit;
};

#endif // MYTABLEMODEL_H
//

  1. mytablemodel.cpp

//    
#include "mytablemodel.h"
#include <QApplication>

MyTableModel::MyTableModel(QObject *parent) :
QAbstractTableModel(parent)
{
datasource = new DataSource();
lblNumber = new QLabel;
dTime = new QDateTime();
//dateEdit = new QDateEdit(QDate::currentDate());
col = datasource->nbColumns();
int l = datasource->size();
for(int i = 0; i < l; i++)
{
    message.first = datasource->data(i,0);
    message.second = datasource->data(i,1);
    messageList.append(message);
    dList.append(dTime->currentDateTime().toString());
}
listSize = messageList.size();

}

MyTableModel::MyTableModel(QList< QPair<QString,QString> > t, QObject *parent) :
QAbstractTableModel(parent)
{
messageList = t;
datasource = new DataSource();
listSize = t.length();
col = datasource->nbColumns();
}

void MyTableModel::update(const QModelIndex &,const QString &mes, const QString &d)
{
int first = messageList.count();
int last = first + 1;
first = last;
lblNumber->setText(tr("%1").arg(first));
beginInsertRows(QModelIndex(),first,last);
message.first = lblNumber->text();
message.second = mes;
messageList.append(message);
dList.append(d);
listSize++;
endInsertRows();
datasource->hasChanges();
}

int MyTableModel::rowCount(const QModelIndex &p) const
{
Q_UNUSED(p);
return listSize;
}

int MyTableModel::columnCount(const QModelIndex &p) const
{
Q_UNUSED(p);
return col;
}

QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
int c = 0;
if (!index.isValid())
{
   return QVariant();
}
if(index.row() < 0)
{
   return QVariant();
}
if (role == Qt::DisplayRole)
{
    c = index.column();
    if(c == 0)
    {
       return messageList.at(index.row()).first;
    }
    else if (c == 1)
    {
       return messageList.at(index.row()).second;
    }

    return dList.at(index.row());
}

return QVariant();
}

void MyTableModel::update(const QModelIndex &p)
{
Q_UNUSED(p);
emit updateMod();
}

void MyTableModel::newMessage(const QString &mes)
{
listSize++;
message.first = lblNumber->text();
message.second = mes;
messageList.append(message);
dList.append(dTime->currentDateTime().toString());
}

bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
QString d = dTime->currentDateTime().toString();
if (index.isValid() && role == Qt::EditRole)
{
    int row = index.row();

    message = messageList.value(row);
    if (index.column() == 0)
    message.first = value.toString();
    else if (index.column() == 1)
    message.second = value.toString();
    else
    return false;

    messageList.replace(row, message);
    dList.replace(row,d);
    emit(dataChanged(index, index));

     return true;
 }

 return false;
}

bool MyTableModel::insertRows(int post, int rows, const QModelIndex &p)
{
 Q_UNUSED(p);
 beginInsertRows(QModelIndex(), post, post+rows-1);
 for (int row=0; row < rows; row++)
 {
     message.first = " ";
     message.second = " ";
     messageList.insert(post, message);
     dList.insert(post, " ");
 }
 endInsertRows();
 return true;
}

bool MyTableModel::removeRows(int post, int rows, const QModelIndex &p)
{
 Q_UNUSED(p);
 beginRemoveRows(QModelIndex(), post, post+rows-1);
 for (int row=0; row < rows; ++row)
 {
     messageList.removeAt(post);
     dList.removeAt(post);
 }
 endRemoveRows();
 return true;
}

Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
 if (!index.isValid())
     return Qt::ItemIsEnabled;

 return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}

QVariant MyTableModel::headerData(int part, Qt::Orientation orient, int role) const
{
 if (role != Qt::DisplayRole)
     return QVariant();

 if (orient == Qt::Horizontal)
 {
     switch (part)
     {
         case 0:
             return tr("Number");

         case 1:
             return tr("Message");

         case 2:
             return tr("Date");

         default:
             return QVariant();
     }
 }
 return QVariant();
}
//

  1. datasource.h

//
#include <QString>
#include <QVariant>
#include <QList>
#include <QPair>

class DataSource
{
public:
DataSource();
int size();
int nbColumns();
const QString& data(int row, int col);
bool hasChanges();

private:
int beginRow;
int columns;
QPair<QString,QString> message;
QList< QPair<QString,QString> > messages;
};

#endif // DATASOURCE_H
//

  1. datasource.cpp

//
#include "datasource.h"

DataSource::DataSource()
{
beginRow = 1;
columns = 3;
message.first = "One";
message.second = "Testing123";
messages.push_back(message);
message.first = "Two";
message.second = "QObject";
messages.push_back(message);
message.first = "Three";
message.second = "QWidget";
messages.push_back(message);
}

int DataSource::size()
{
return messages.length();
}

int DataSource::nbColumns()
{
return columns;
}

const QString& DataSource::data(int r, int c)
{
int l = messages.length();
if(r < l)
{
    message = messages.at(r);
    if (c == 0)
    {
        return message.first;
    }
    else if (c == 1)
    {
       return message.second;
    }
}
return "Testing123";
}


bool DataSource::hasChanges()
{
beginRow++;
return true;
}
//

  1. customtab.pro

//
#-------------------------------------------------
#
# Project created by QtCreator 2018-09-10T15:39:01
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = customtab
TEMPLATE = app


SOURCES += main.cpp\
    widget.cpp \
datasource.cpp \
mytablemodel.cpp

HEADERS  += widget.h \
datasource.h \
mytablemodel.h

#//FORMS    += widget.ui
//

  1. 尽情享受吧。

如果您是 Qt 新手。 Qt 使用 C++。它实现了 GUI。大多数 C++ 应用程序使用控制台应用程序。 Qt 使用设计模式和框架。享受吧!