QAbstractTableModel & QTableView:频繁的数据更新

问题描述 投票:0回答:1

背景:

我有一个应用程序,它从 CAN 总线接收帧,在单独的线程中处理它们,然后通知主线程(使用

Qt::QueuedConnection
每块消息一个信号)。主线程更新表模型后,会触发
dataChanged()
信号,从而导致视图的更新。

目前,我正在每 500 毫秒使用 20-25 条消息来测试此设置。它工作得很好,除了当表视图可见并且应用程序正在接收消息时,UI 由于表视图重新绘制/更新而变得滞后。

示例:

为了最大限度地减少影响结果、计时和主线程事件循环的变量数量,我创建了一个简单的表模型,由间隔为 500 毫秒的计时器触发,以模拟消息的周期性接收。

简单表格模型:

#include <QAbstractTableModel>

class SimpleTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit SimpleTableModel(QObject *parent = nullptr)
        : QAbstractTableModel{parent}
    {

    }

    void setRowCount(int rows)
    {
        beginResetModel();
        m_rows = std::max(rows, 0);
        endResetModel();
    }
    void setColumnCount(int columns)
    {
        beginResetModel();
        m_columns = std::max(columns, 0);
        endResetModel();
    }

    void triggerDataChanged()
    {
        emit dataChanged(index(0, 0), index(m_rows - 1, m_columns - 1));
    }

    int rowCount(const QModelIndex &parent = QModelIndex()) const override
    {
        return m_rows;
    }

    int columnCount(const QModelIndex &parent = QModelIndex()) const override
    {
        return m_columns;
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
    {
        if (!index.isValid())
            return {};

        if (Qt::DisplayRole == role)
            return QStringLiteral("(%1:%2)").arg(index.row() + 1).arg(index.column() + 1);

        return {};
    }
private:
    int m_rows{};
    int m_columns{};
};

然后,在我们的

MainWindow
中使用默认的
QTableView

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    auto model = new SimpleTableModel(this);
    auto timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [model](){ model->triggerDataChanged(); });

    ui->tableView->setModel(model);

    m_staticModel.setColumnCount(5);
    m_staticModel.setRowCount(100);
}

我还使用以下方式添加了登录

QApplication::notify

class MyApplication : public QApplication
{
    QElapsedTimer t;
public:
    MyApplication(int& argc, char ** argv) : QApplication(argc, argv) { }
    virtual ~MyApplication() { }

    virtual bool notify(QObject* receiver, QEvent* event)
    {
        t.start();
        bool ret = QApplication::notify(receiver, event);

        if(t.elapsed() > 1)
            qDebug("Processing event type %d for object %s took %dms",
                   (int)event->type(), receiver->objectName().toLocal8Bit().data(),
                   (int)t.elapsed());
        return ret;
    }
};

int main(int argc, char *argv[])
{
    MyApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

我收到了以下日志:

...
Processing event type 12 for object qt_scrollarea_viewport took 98ms
Processing event type 77 for object MainWindow took 99ms

Processing event type 12 for object qt_scrollarea_viewport took 97ms
Processing event type 77 for object MainWindow took 98ms

Processing event type 12 for object qt_scrollarea_viewport took 93ms
Processing event type 77 for object MainWindow took 94ms
...

问题:

  1. 在 Qt 应用程序中处理表视图的频繁更新时,有人遇到过类似的性能问题吗?
  2. 如果是这样,采用了哪些策略或优化来解决与实时更新 UI 相关的挑战?
c++ qt model
1个回答
0
投票

您应该在每种情况下使用正确(最有效)的变更通知方法:

  • 仅当某些数据实际更改时,才调用

    dataChanged
    ,仅传递实际更改的范围和角色。通常,对模型的所有更改都是使用
    setData
    成员完成的。 (在您的示例中,永远不应该调用它)

  • 尽可能避免使用

    begin/endResetModel
    。仅当几乎所有数据都更改时,f.ex。当使用一些完全不同的数据重新初始化表时,重置模型是合适的。 (在您的示例中,永远不应该调用它)

  • 添加/插入行时使用

    begin/endInsertRows
    ,而不是重置模型。 尝试使用此方法实现
    setRowCount

  • 添加/插入列时使用

    begin/endInsertColumns
    ,而不是重置模型。 尝试使用此方法实现
    setColumnCount

使用带有正确参数的特定函数,允许视图最小化所需的更新。

© www.soinside.com 2019 - 2024. All rights reserved.