Multithreading Qt-GUI中许多小部件的快速更新
对于必须在屏幕上显示大量数据的应用程序,我很难理解最佳方法,因为屏幕正在以高速更新。我正在用Qt for windows编写这个应用程序。我不会详细介绍实际应用程序,但我已经在下面编写了一个示例应用程序来演示这个问题 在这里,我有一个线程,它正在计算值。在这种情况下,它是一个值,它只是一个计数器。在实际应用中,它有很多价值。这是每毫秒更新一次。此速率是所需的数据计算速率,而不是GUI所需的更新速率。如前所述,这是在它自己的线程中完成的。这个线程的想法是,它只是关于数据的计算,而不关心数据的显示 现在,为了更新本例中数据的显示,我使用QLabel网格多次显示该值(模拟许多不同值的显示)。我从Qt文档中了解到,小部件的更新必须在主GUI线程中完成。所以我在这里做的是让线程计算这些值,在每次重新计算时(1ms)发出一个带有计算值的信号。然后连接到主GUI thred,然后依次更新每个小部件以显示值 这样做的结论是:Multithreading Qt-GUI中许多小部件的快速更新,multithreading,performance,qt,user-interface,Multithreading,Performance,Qt,User Interface,对于必须在屏幕上显示大量数据的应用程序,我很难理解最佳方法,因为屏幕正在以高速更新。我正在用Qt for windows编写这个应用程序。我不会详细介绍实际应用程序,但我已经在下面编写了一个示例应用程序来演示这个问题 在这里,我有一个线程,它正在计算值。在这种情况下,它是一个值,它只是一个计数器。在实际应用中,它有很多价值。这是每毫秒更新一次。此速率是所需的数据计算速率,而不是GUI所需的更新速率。如前所述,这是在它自己的线程中完成的。这个线程的想法是,它只是关于数据的计算,而不关心数据的显示
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的主要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));
}
}
}
class主窗口:公共QMainWindow
{
Q_对象
公众:
显式主窗口(QWidget*parent=0);
私人:
QLabel*标签[行最大值][列最大值];
生成器*数据生成器;
公众时段:
无效数据可用(int val);
信号:
无效发电机(bool运行);
};
主窗口::主窗口(QWidget*父窗口):
QMainWindow(父窗口)
{
QGridLayout*layout=新的QGridLayout;
用于(int iCol=0;iColsetLayout(布局);
数据生成器=新的生成器;
QThread*dgThread=新的QThread;
dataGenerator->moveToThread(dgThread);
dgThread->start(QThread::HighestPriority);
连接(此,信号(runGenerator(bool)),数据生成器,插槽(run(bool)));
连接(dataGenerator,信号(dataAvailable(int)),此,插槽(dataAvailable(int));
生成程序(真);
}
void主窗口::数据可用(int val)
{
对于(int iCol=0;iCol
过去对我有效的一种方法是将访问器方法构建到worker对象中,然后让视图根据自己的更新周期“拉”数据
要使用示例代码,请向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;
}
然后,为主窗口提供自己的更新计时器,该计时器不会对系统造成太大影响:
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));
}
}
}
MainWindow::MainWindow(QWidget*父项):
QMainWindow(父窗口)
{
// ...
//将dataAvailable代码替换为以下代码:
updateTimer=新的QTimer;
连接(updateTimer,SIGNAL(timeout()),此,插槽(updateDisplayValues());
UpdateTime->start(我的主窗口更新速率);//某个常数;尝试200毫秒?
// ...
}
void main window::updateDisplayedValues()
{
int val=dataGenerator->getCurrentValue();
//要做到这一点,您可以通过检查是否需要来提高效率*
//首先在这里重新绘制;一般来说,Qt在不浪费周期方面非常好
//在当前隐藏的小部件上
对于(int iCol=0;iCol
如果您只是将计算放入一个在计时器上运行的生成器中……它的事件循环与GUI在同一线程上
如果将生成器移动到它自己的线程,然后设置一个计时器以每秒最多30次检查生成器,您应该会看到大多数问题都消失了。告诉它以比人眼所能感觉到的或监视器刷新率更快的速度直观地更新显示通常是过分的。NTSC每秒30次,PAL每秒2次5 fps,就普通视频速率而言。对于数据的文本标签,我通常需要在一秒钟内进行采样/平均,并在视觉上每秒更新一次,以保持可读性,而不仅仅是模糊的数字
当您开始使用线程时,您应该看到您的计算机使用不同的内核来管理应用程序中的不同负载