Ste*_*veC 5 performance user-interface qt multithreading
我很难理解必须向屏幕显示大量数据的应用程序的最佳方法,该方法正以高速率更新.我正在Qt for windows中编写这个应用程序.我不会详细介绍实际的应用程序,但我在下面编写了一个示例应用程序来演示这个问题.
在这里我有一个计算值的线程.在这种情况下,它只是一个计数器的值.在实际应用中它有很多价值.这是每毫秒更新一次.此速率是数据所需的计算速率,而不是GUI所需的更新速率.如上所述,这是在自己的线程中完成的.这个线程的想法是它只是计算数据,而不关心它的显示.
现在,为了更新此示例中的数据显示,我使用QLabel网格多次显示该值(模拟许多不同值的显示).我从Qt文档中了解到,Widgets的更新必须在主GUI线程中完成.所以我在这里做的是我得到线程计算值,每次重新计算它时发出一个带有计算值的信号(1ms).然后将其连接到主GUI,然后依次更新每个小部件以显示值.
这样做的结论是:
最重要的是,我不确定更新要显示的数据的正确方法是什么.很明显,屏幕不需要在1ms更新,即使它可能无论如何都不能快速刷新.另一方面,我不希望我的数据线程与屏幕更新率有关.有没有更好的方法将数据从数据线程获取到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);
}
Run Code Online (Sandbox Code Playgroud)
这是更新dislay的主要GUI类:
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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
过去对我有用的一种方法是将访问器方法构建到工作对象中,然后让视图根据其自己的更新周期“拉取”数据。
要使用示例代码,请将如下方法添加到Generator:
// TODO For a more complex class, you probably want one "get all the stats"
// accessor rather than lots of little methods -- that way all the data is
// synced up
int Generator::getCurrentValue()
{
QMutexLocker(mutex); // (also add a QMutex member to the class)
return value;
}
Run Code Online (Sandbox Code Playgroud)
然后为主窗口提供自己的更新计时器,这样不会对系统造成很大的影响:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// ...
// Replace dataAvailable code with this:
updateTimer = new QTimer;
connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateDisplayedValues());
updateTimer->start(MY_MAIN_WINDOW_UPDATE_RATE); // some constant; try 200 ms?
// ...
}
void MainWindow::updateDisplayedValues()
{
int val = dataGenerator->getCurrentValue();
// TODO You might this more efficient by checking whether you *need to*
// repaint first here; generally Qt is pretty good about not wasting cycles
// on currently-hidden widgets anyway
for(int iCol=0; iCol< COL_MAX; iCol++)
{
for(int iRow=0; iRow<ROW_MAX; iRow++)
{
labels[iRow][iCol]->setText(QString::number(val));
}
}
}
Run Code Online (Sandbox Code Playgroud)