我很难理解必须向大屏幕上显示大量数据的应用程序的最佳方法,该屏幕正在以高速更新。我正在为Qt编写这个应用程序。我不会介绍实际应用的细节,但我已经在下面编写了一个示例应用程序来演示此问题。Qt中很多小部件的快速更新GUI
在这我有一个线程是计算值。在这种情况下,这是一个简单的计数器。在真正的应用程序中有很多值。这每毫秒更新一次。该比率是数据所需的计算速率,而不是GUI所需的更新速率。如上所述,这是在它自己的线程中完成的。这个线程的想法是它只是关于数据的计算,并不关心它的显示。
现在要更新此示例中的数据显示,我使用QLabels网格多次显示该值(模拟许多不同值的显示)。我从Qt文档中了解到,Widgets的更新必须在主GUI线程中完成。所以我在这里做的是我得到的线程计算值发出一个信号与计算值每次重新计算(1毫秒)。然后连接到主GUI,然后依次更新每个小部件以显示值。
从这样得出的结论是:
- GUI线程是由从数据线1ms的更新淹没,所以显示变得更新很慢。在我的机器上,有100个小部件更新,我估计更新大约为4fps。
- 图形用户界面的所有其他方面,如移动,调整大小和按下按钮都在努力获得处理器时间。
- 看来只是用文本更新一个简单的QLabel似乎很慢。这是正常的吗?
- 我已经为此添加了时间代码,并且GUI线程中的1ms更新大约1.8ms运行于avergae上,最大增量时间为40-75ms。所以即使它落后,其运行速度也非常快。但大概在幕后,其他事件正在GUI线程事件队列中,以实际将更新画到屏幕上,而这些事情真的很挣扎。
- 我真的不明白什么是确定实际的屏幕更新率? Qt如何决定何时更新屏幕?
最重要的是我不确定更新要显示的数据的正确方法是什么。很明显,屏幕不需要在1毫秒内更新,即使显示器无法快速刷新。另一方面,我不希望我的数据线程与屏幕更新速度有关。有没有更好的方式从数据线程获取数据到GUI而不需要淹没GUI事件队列?
任何深入了解这个问题的Qt方法将不胜感激。
下面是在自己的线程运行在数据发生器:
class Generator : public QObject
{
Q_OBJECT
public:
explicit Generator(QObject *parent = 0);
signals:
void dataAvailable(int val);
public slots:
void run(bool run);
void update(void);
private:
QTimer *timer;
int value;
};
void Generator::run(bool run)
{
if(run)
{
value = 0;
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1);
} else
{
timer->stop();
delete timer;
}
}
void Generator::update()
{
value++;
emit dataAvailable(value);
}
而这里的主界面类更新dislay:已为我在过去的工作
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private:
QLabel *labels[ROW_MAX][COL_MAX];
Generator *dataGenerator;
public slots:
void dataAvailable(int val);
signals:
void runGenerator(bool run);
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QGridLayout *layout = new QGridLayout;
for(int iCol=0; iCol<COL_MAX; iCol++)
{
for(int iRow=0; iRow<ROW_MAX; iRow++)
{
QLabel *label = new QLabel("Hello");
labels[iRow][iCol] = label;
layout->addWidget(labels[iRow][iCol], iRow, iCol);
}
}
centralWidget->setLayout(layout);
dataGenerator = new Generator;
QThread *dgThread = new QThread;
dataGenerator->moveToThread(dgThread);
dgThread->start(QThread::HighestPriority);
connect(this, SIGNAL(runGenerator(bool)), dataGenerator, SLOT(run(bool)));
connect(dataGenerator, SIGNAL(dataAvailable(int)), this, SLOT(dataAvailable(int)));
emit runGenerator(true);
}
void MainWindow::dataAvailable(int val)
{
for(int iCol=0; iCol< COL_MAX; iCol++)
{
for(int iRow=0; iRow<ROW_MAX; iRow++)
{
labels[iRow][iCol]->setText(QString::number(val));
}
}
}
感谢您的建议亚历克斯。是的这是有道理的。我其实只是尝试了类似的地方,我原来的(1ms)插槽只是保存了值,但不更新显示。然后我有第二个计时器,就像您建议以更合理的速率更新显示器一样。我必须承认,我非常喜欢使用信号/插槽机制来处理线程交叉问题,而不是互斥锁,这些互斥锁在发生锁定时通常会隐藏情况。不是我可以想到任何可能与你的建议有关的错误。 – SteveC
最后一个问题,没有人知道当你尝试和更新小部件的速度比他们可以绘制,或者如何知道实际的屏幕更新率是什么?或者换句话说,除了视觉效果之外,你怎么知道你已经超越了性能限制? – SteveC
如果更多的事情发生得比视频缓冲区允许的更快,Qt会将多个'update'调用合并到一个'repaint'中。 – phyatt