QAbstractTableModel,并为单行发出dataChanged
QAbstractTableModel and emit dataChanged for a single row
我从QAbstractTableModel派生了一个模型,现在我想通知,整行的数据已经更改。例如,如果索引为5的行的数据发生了更改(4列(,则使用以下代码可以正常工作。
emit dataChanged(index(5,0), index(5, 0));
emit dataChanged(index(5,1), index(5, 1));
emit dataChanged(index(5,2), index(5, 2));
emit dataChanged(index(5,3), index(5, 3));
但是,如果我尝试只使用一个emit来实现相同的,则视图中所有行的所有列都会更新。
emit dataChanged(index(5, 0), index(5, 3));
我在这里做错了什么?
最小示例(C++11、QTCreator 4.7.1、Windows 10(1803(、64位(
演示.h
#pragma once
#include <QAbstractTableModel>
#include <QTime>
#include <QTimer>
class Demo : public QAbstractTableModel
{
Q_OBJECT
QTimer * t;
public:
Demo()
{
t = new QTimer(this);
t->setInterval(1000);
connect(t, SIGNAL(timeout()) , this, SLOT(timerHit()));
t->start();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
int c = index.column();
if (role == Qt::DisplayRole)
{
QString strTime = QTime::currentTime().toString();
if (c == 0) return "A" + strTime;
if (c == 1) return "B" + strTime;
if (c == 2) return "C" + strTime;
if (c == 3) return "D" + strTime;
}
return QVariant();
}
int rowCount(const QModelIndex &) const override { return 10; }
int columnCount(const QModelIndex &) const override { return 4; }
private slots:
void timerHit()
{
//Works
emit dataChanged(index(5,0), index(5, 0));
emit dataChanged(index(5,1), index(5, 1));
emit dataChanged(index(5,2), index(5, 2));
emit dataChanged(index(5,3), index(5, 3));
//emit dataChanged(index(5,0), index(5, 3)); // <-- Doesn't work
}
};
main.cpp
#include "demo.h"
#include <QApplication>
#include <QTreeView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView dataView;
Demo dataModel{};
dataView.setModel( &dataModel );
dataView.show();
return a.exec();
}
我认为问题在于您对QAbstractItemModel::dataChanged
信号发出时QTreeView
的行为所做的某些假设。
具体来说,您假设视图将仅对信号中指定的那些索引调用QAbstractItemModel::data
。事实并非如此。
查看QAbstractItemView::dataChanged
(Qt
5.11.2(的源代码,您将看到。。。
void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
Q_UNUSED(roles);
// Single item changed
Q_D(QAbstractItemView);
if (topLeft == bottomRight && topLeft.isValid()) {
const QEditorInfo &editorInfo = d->editorForIndex(topLeft);
//we don't update the edit data if it is static
if (!editorInfo.isStatic && editorInfo.widget) {
QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft);
if (delegate) {
delegate->setEditorData(editorInfo.widget.data(), topLeft);
}
}
if (isVisible() && !d->delayedPendingLayout) {
// otherwise the items will be update later anyway
update(topLeft);
}
} else {
d->updateEditorData(topLeft, bottomRight);
if (isVisible() && !d->delayedPendingLayout)
d->viewport->update();
}
#ifndef QT_NO_ACCESSIBILITY
if (QAccessible::isActive()) {
QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged);
accessibleEvent.setFirstRow(topLeft.row());
accessibleEvent.setFirstColumn(topLeft.column());
accessibleEvent.setLastRow(bottomRight.row());
accessibleEvent.setLastColumn(bottomRight.column());
QAccessible::updateAccessibility(&accessibleEvent);
}
#endif
d->updateGeometry();
}
重要的一点是,该代码的行为不同,这取决于信号是否指定单个QModelIndex
——例如topLeft
与bottomRight
相同。如果它们相同,则视图会尝试确保仅更新该模型索引。但是,如果指定了多个模型索引,则它将调用。。。
d->viewport->update();
据推测,这将导致查询所有可见模型索引的数据。
由于Demo::data
的实现总是根据当前时间返回新数据,因此您将看到视图更新的整个可见部分,这会给人一种dataChanged
信号已针对所有行和列发出的印象。
因此,解决方法实际上是让数据模型更"有状态"——它需要跟踪值,而不是简单地按需生成值。
这是Qt中一个真正的效率错误。
Qt项目不再接受对Qt5的更改,所以我进行了更改,并将其推送到GitHub上的fork。你可以在这里看到你遇到的问题的修复程序。
如果你想构建自己的5.15.2副本,你可能对我的其他修复程序感兴趣。
不确定这是否是你想要的,但我无论如何都会把它放上去。
即使使用emit dataChanged(...)
,您仍然会看到行上的单击/选择会导致它们自我更新(在Mac上执行此操作,因此可能有所不同(。
我将使用QAbstractItemModel::setData()
函数,而不是使用QAbstractItemModel::dataChanged
信号。
这是我对demo.h
的实现
#pragma once
#include <QAbstractTableModel>
#include <QTime>
#include <QTimer>
class Demo : public QAbstractTableModel
{
Q_OBJECT
public:
Demo()
{
int cCount = columnCount(index(0, 0));
int rCount = rowCount(index(0, 0));
// populate model data with *static* values
QString strTime = QTime::currentTime().toString();
QStringList temp;
for (int j = 0; j < cCount; j++)
temp.append(strTime);
for (int i = 0; i < rCount; i++)
demoModelData.append(temp);
// nothing new here
t = new QTimer(this);
t->setInterval(1000);
connect(t, SIGNAL(timeout()) , this, SLOT(timerHit()));
t->start();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
// tells the *view* what to display
// if this was dynamic (e.g. like your original strTime implementation)
// then the view (QTreeView in main.cpp) will constantly update
if (role == Qt::DisplayRole)
return demoModelData.at(index.row()).at(index.column()); // retrieve data from model
return QVariant();
}
// reimplemented from QAbstractTableModel
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override
{
if (role == Qt::DisplayRole)
{
demoModelData[index.row()][index.column()] = value.toString(); // set the new data
emit dataChanged(index, index); // explicitly emit dataChanged signal, notifies TreeView to update by
// calling this->data(index, Qt::DisplayRole)
}
return true;
}
int rowCount(const QModelIndex &) const override { return 10; }
int columnCount(const QModelIndex &) const override { return 4; }
private slots:
void timerHit()
{
QString strTime = QTime::currentTime().toString();
setData(index(5, 0), QVariant(strTime), Qt::DisplayRole); // only changes index at (row = 5, col = 0)
}
private:
QTimer *t;
QList<QStringList> demoModelData; // stores the table model's data
};
由于类是一个"模型",因此应该有一些存储/检索数据以供显示的方法。在这里,我使用了QList<QStringList>
,但您也可以用其他适合您的方式存储数据(例如树、QVector、QMap(。
- QAbstractTableModel,并为单行发出dataChanged
- Visual Studio 2019:插入多个C++风格的单行注释
- 套接字连接"Operation not permitted"错误,甚至使用升压/平发器根.cpp
- 即发即弃 std::线程对象清理自身
- 将 hh:mm:ss.zzz 时间 QString 转换为毫秒的单行函数?
- 在 Qml 中从 QSqlTableModel 中删除单行时视图不一致
- 单行函数模板 c++ 的内联性保证
- 当输入在带有空格的单行中给出时,cin 如何在 while 循环中工作?
- C++模板是否有助于定义单行'property'成员?
- 如何在单行语句中C++递减多个变量?
- 单行初始化映射中的C++对象
- 叮当格式捕获,单行上有空语句
- 实例化对象并调用方法,使用单行语法在 C# 或 C++ 中返回值?
- 使用"multiple"命名空间单行
- 我应该将哪种有效负载类型发送给webrtc::P ayloadRouter的构造函数?
- 单行类声明和实例化
- 格式化宏以使用单行if语句
- 这个代码片段中会发生死锁吗?为什么
- C++查找用于构建单行计算器
- 谷歌测试发现在macOSX上添加并运行了我的所有测试两次