C++ QAbstractTableModel和emit dataChanged用于单行
我从QAbstractTableModel派生了一个模型,现在我想通知您,整行的数据已经更改。例如,如果索引为5的行的数据发生了更改(4列),则使用以下代码的效果与预期一样C++ QAbstractTableModel和emit dataChanged用于单行,c++,qt,row,onchange,qabstracttablemodel,C++,Qt,Row,Onchange,Qabstracttablemodel,我从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
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位)
demo.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
}
};
#pragma一次
#包括
#包括
#包括
类演示:公共QAbstractTableModel
{
Q_对象
QTimer*t;
公众:
演示()
{
t=新的QTimer(本);
t->setInterval(1000);
连接(t,信号(timeout()),此,插槽(timerHit());
t->start();
}
QVariant数据(常量QModelIndex&index,int-role=Qt::DisplayRole)常量覆盖
{
int c=index.column();
如果(角色==Qt::DisplayRole)
{
QString strTime=QTime::currentTime().toString();
如果(c==0)返回“A”+strTime;
如果(c==1)返回“B”+strTime;
如果(c==2)返回“c”+strTime;
如果(c==3)返回“D”+strTime;
}
返回QVariant();
}
int rowCount(constqmodelindex&)constoverride{return 10;}
int columnCount(constqmodelindex&)constoverride{return 4;}
专用插槽:
void timerHit()
{
//工作
发射数据改变(索引(5,0),索引(5,0));
发射数据改变(索引(5,1),索引(5,1));
发射数据改变(索引(5,2),索引(5,2));
发射数据改变(索引(5,3),索引(5,3));
//emit dataChanged(索引(5,0),索引(5,3));//不确定这是否是您要查找的内容,但无论如何我都会把它放出来
即使使用emit-dataChanged(…)
,您仍然会看到对行的单击/选择将导致它们自我更新(在Mac上执行此操作,因此可能会有所不同)
我将使用函数,而不是使用qabstractemmodel::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
};
#pragma一次
#包括
#包括
#包括
类演示:公共QAbstractTableModel
{
Q_对象
公众:
演示()
{
int-cCount=columnCount(索引(0,0));
int rCount=rowCount(索引(0,0));
//用*静态*值填充模型数据
QString strTime=QTime::currentTime().toString();
晶体温度;
对于(int j=0;jsetInterval(1000);
连接(t,信号(timeout()),此,插槽(timerHit());
t->start();
}
QVariant数据(常量QModelIndex&index,int-role=Qt::DisplayRole)常量覆盖
{
//告诉*视图*要显示的内容
//如果这是动态的(例如,与原始strTime实现类似)
//然后视图(main.cpp中的QTreeView)将不断更新
如果(角色==Qt::DisplayRole)
返回demoModelData.at(index.row()).at(index.column());//从模型中检索数据
返回QVariant();
}
//从QAbstractTableModel重新实现
虚拟布尔setData(常量QModelIndex和index,常量QVariant和value,int role=Qt::DisplayRole)重写
{
如果(角色==Qt::DisplayRole)
{
demoModelData[index.row()][index.column()]=value.toString();//设置新数据
发出dataChanged(index,index);//显式发出dataChanged信号,通知TreeView通过
//调用此->数据(索引,Qt::DisplayRole)
}
返回true;
}
int rowCount(constqmodelindex&)constoverride{return 10;}
int columnCount(constqmodelindex&)constoverride{return 4;}
专用插槽:
void timerHit()
{
QString strTime=QTime::currentTime().toString();
setData(索引(5,0),QVariant(strTime),Qt::DisplayRole);//仅在(行=5,列=0)处更改索引
}
私人:
QTimer*t;
QList demoModelData;//存储表模型的数据
};
因为类是一个“模型”,所以应该有一些存储/检索数据以供显示的方法。在这里,我使用了QList
,但您也可以用其他适合您的方式存储数据(例如树、QVector、QMap).我认为问题在于您对发出信号时QTreeView
的行为所做的某些假设
具体地说,您假设视图将只对信号中指定的索引进行调用,但情况并非如此
查看qabstractemview::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();
}
这可能会导致查询所有可见模型索引的数据
由于Demo::data
的实现总是基于当前时间返回新数据,因此您将看到视图更新的整个可见部分,给人的印象是所有行和列都发出了dataChanged
信号
因此,解决方法实际上是使您的数据模型更“有状态”——它需要跟踪值,而不是简单地按需生成值。对于emit-dataChanged(索引(5,0),索引(5,3));
?更改所有列?只更改索引(5,0)
和索引(5,3)
?(从你的问题来看,我不是特别清楚。)根据,包含在该范围内的所有单元格都将更改。我在问题中对其进行了重新表述。我希望该范围内的所有列都将在视图中更改。因此,这应该是第5行的第0-3列,而不是整个模型所有行的所有列。为什么所有行的所有列都在视图中更改?Thanx
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();
}
d->viewport->update();