【问题标题】:How to force refresh of changed cells with Qt Custom TableModel (deriving from QAbstractTableModel) ?如何使用 Qt Custom TableModel(源自 QAbstractTableModel)强制刷新更改的单元格?
【发布时间】:2019-02-06 18:06:58
【问题描述】:

我是 Qt 的新手。我正在尝试实现自定义表模型:

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

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

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

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

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

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

tableModel->update();

定义如下:

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

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

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

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

【问题讨论】:

  • 为什么要手动更新视图?
  • @scopchanov 好吧,这不是我想要的,如果您知道只要我的模型更新,我就可以重新绘制视图,那就更好了...但是目前还不清楚哪种方法我应该打电话让它工作..
  • 模型/视图背后的想法是,一旦模型中的数据发生变化,视图应该自动更新。因此,无需手动执行此操作。为了能够进一步帮助您,请准备一个Minimal, Complete, and Verifiable example,它会重现该问题(包括您的自定义模型)。

标签: c++ qt qtableview


【解决方案1】:

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


  1. 演示自定义表格。


  1. ma​​in.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 使用设计模式和框架。享受吧!


【讨论】:

  • 虽然我没有详细阅读您的长回复(我已经找到了解决方案),但我接受它作为答案,因为这对其他人有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多