QAbstractTableModel 并为单行发出 dataChanged

Sou*_*rXP 6 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(5, 3));
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试仅使用一次发出来实现相同的目的,则视图中所有行的所有列都会更新。

emit dataChanged(index(5, 0), index(5, 3));
Run Code Online (Sandbox Code Playgroud)

我在这里做错了什么?

最小示例(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
  }
};
Run Code Online (Sandbox Code Playgroud)

主程序

#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();
}
Run Code Online (Sandbox Code Playgroud)

G.M*_*.M. 6

我认为问题在于您对信号发出QTreeView时的行为所做的某些假设。QAbstractItemModel::dataChanged

具体来说,您假设视图将仅调用QAbstractItemModel::data信号中指定的那些索引。情况不一定如此。

查看QAbstractItemView::dataChanged( Qt5.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();
}
Run Code Online (Sandbox Code Playgroud)

重要的一点是,这段代码的行为会有所不同,具体取决于信号是否指定单个QModelIndex- 例如,topLeft与 相同bottomRight。如果它们相同,则视图会尝试确保仅更新该模型索引。但是,如果指定了多个模型索引,那么它将调用...

d->viewport->update();
Run Code Online (Sandbox Code Playgroud)

这可能会导致查询所有可见模型索引的数据。

由于您的实现Demo::data始终根据当前时间返回新数据,因此您将看到视图更新的整个可见部分,给人的印象是dataChanged所有行和列都发出了信号。

因此,解决方案实际上是让您的数据模型更加“有状态”——它需要跟踪值,而不是简单地根据需要生成它们。