QTableView 和 QStandardItemModel 问题
Issue with QTableView and QStandardItemModel
我在向我的 QStandardItemModel 动态添加项目并使用 QTableView 显示它们时遇到了奇怪的事情。如果事件之间的时间足够长(例如 50 毫秒),那么视图是可滚动的,但如果事件更快(10 毫秒),视图总是向下滚动并且不允许我做任何事情。
下面是简单的代码片段:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#include <QMainWindow>
#include <QTimer>
#include <QTableView>
#include <QStandardItemModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QTimer dataTimer;
QStandardItemModel *model;
QTableView *view;
public slots:
void insertData();
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
view = new QTableView(this);
this->setCentralWidget(view);
model = new QStandardItemModel(this);
model->setColumnCount(1);
view->setModel(model);
QObject::connect(&dataTimer, &QTimer::timeout, this, &MainWindow::insertData);
dataTimer.start(10); // 50 here gives correct behaviour while 10 does not
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::insertData()
{
QList<QStandardItem *> items;
items.append(new QStandardItem(QTime::currentTime().toString("hh:mm:ss.zzz")));
model->insertRow(0, items);
if (model->rowCount() > 20){
model->removeRow(model->rowCount()-1);
}
}
dataTimer.start(50) 工作正常,而 dataTimer.start(10) 刹车行为。这是我的笔记本电脑,也许其他人的数字会有所不同,但我认为逻辑很清楚。
我认为它有点太快了,像 beginInsertRows() 这样的一些方法效果不佳。但这通常是针对定制模型的,这里只是标准模型。
谁能告诉我为什么会这样?
谢谢
你好。好的,当计时器设置为 10 毫秒时,它会在每次屏幕刷新时添加和删除一行。
Qtimer与屏幕刷新率挂钩。这意味着如果计时器在 16 毫秒内关闭,它会在每次屏幕刷新时关闭。
当您将项目添加到列表时,Qt 会在列表上运行额外的 UI 事件以用于布局和视图边界。因为您在每次屏幕刷新时添加和删除,所以您的列表陷入 运行 这些事件的持续循环中。更不用说您的列表基本上每 20 帧就被完全替换一次。
我不确定您为什么需要如此频繁地向列表中添加项目,但也许您应该将添加和删除分批添加到列表中,然后每隔 100 毫秒就可以批量添加和删除?那将解决问题。
在 GUI 线程中使用 10 毫秒计时器来影响 GUI 元素本身就是个坏主意。例如,在 Windows 中,标准方法无法达到 10 毫秒超时。您的用户真的需要每秒查看超过 1 次 table 数据的更新吗?他真正需要什么?基于此,您应该确定一种优化方法。
那你可以选择:
- 减少计时器的超时时间并影响每个事件的多行。 看来您无论如何都必须减少 GUI 计时器的超时时间。
- 提前定期保留所需的行数,例如
model->setRowCount(n)
;
暂时禁用 table 通过 setUpdatesEnabled() 方法刷新:
setUpdatesEnabled(假);
bigVisualChanges();
setUpdatesEnabled(true);
继承 Qt 模型之一 类 并重新实现 insertRows() 或更多。
我在向我的 QStandardItemModel 动态添加项目并使用 QTableView 显示它们时遇到了奇怪的事情。如果事件之间的时间足够长(例如 50 毫秒),那么视图是可滚动的,但如果事件更快(10 毫秒),视图总是向下滚动并且不允许我做任何事情。
下面是简单的代码片段:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#include <QMainWindow>
#include <QTimer>
#include <QTableView>
#include <QStandardItemModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QTimer dataTimer;
QStandardItemModel *model;
QTableView *view;
public slots:
void insertData();
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
view = new QTableView(this);
this->setCentralWidget(view);
model = new QStandardItemModel(this);
model->setColumnCount(1);
view->setModel(model);
QObject::connect(&dataTimer, &QTimer::timeout, this, &MainWindow::insertData);
dataTimer.start(10); // 50 here gives correct behaviour while 10 does not
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::insertData()
{
QList<QStandardItem *> items;
items.append(new QStandardItem(QTime::currentTime().toString("hh:mm:ss.zzz")));
model->insertRow(0, items);
if (model->rowCount() > 20){
model->removeRow(model->rowCount()-1);
}
}
dataTimer.start(50) 工作正常,而 dataTimer.start(10) 刹车行为。这是我的笔记本电脑,也许其他人的数字会有所不同,但我认为逻辑很清楚。
我认为它有点太快了,像 beginInsertRows() 这样的一些方法效果不佳。但这通常是针对定制模型的,这里只是标准模型。
谁能告诉我为什么会这样? 谢谢
你好。好的,当计时器设置为 10 毫秒时,它会在每次屏幕刷新时添加和删除一行。
Qtimer与屏幕刷新率挂钩。这意味着如果计时器在 16 毫秒内关闭,它会在每次屏幕刷新时关闭。
当您将项目添加到列表时,Qt 会在列表上运行额外的 UI 事件以用于布局和视图边界。因为您在每次屏幕刷新时添加和删除,所以您的列表陷入 运行 这些事件的持续循环中。更不用说您的列表基本上每 20 帧就被完全替换一次。
我不确定您为什么需要如此频繁地向列表中添加项目,但也许您应该将添加和删除分批添加到列表中,然后每隔 100 毫秒就可以批量添加和删除?那将解决问题。
在 GUI 线程中使用 10 毫秒计时器来影响 GUI 元素本身就是个坏主意。例如,在 Windows 中,标准方法无法达到 10 毫秒超时。您的用户真的需要每秒查看超过 1 次 table 数据的更新吗?他真正需要什么?基于此,您应该确定一种优化方法。
那你可以选择:
- 减少计时器的超时时间并影响每个事件的多行。 看来您无论如何都必须减少 GUI 计时器的超时时间。
- 提前定期保留所需的行数,例如
model->setRowCount(n)
; 暂时禁用 table 通过 setUpdatesEnabled() 方法刷新:
setUpdatesEnabled(假);
bigVisualChanges();
setUpdatesEnabled(true);继承 Qt 模型之一 类 并重新实现 insertRows() 或更多。