我使用synchronously块QThread::sleep()
函数进行计时,按时间顺序显示数字。
预期运行的进程是同步阻塞当前线程2秒,然后运行以下代码改变显示数量,并同步阻塞另外2秒,依此类推,这种思想在非GUI程序中运行良好。
但在GUI模式下,标签只显示9,这是要显示的最后一个数字。
是什么导致图形用户界面和非图形用户界面程序中同步阻塞函数sleep
的不同结果?
#include <windows.h>
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QThread>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
//slot of start timing button
void Widget::on_pushButton_2_clicked()
{
for(int i=0;i<10;i++){
QThread.sleep(2);
ui->label->setText(QString::number(i));
}
}
发布于 2018-06-26 23:31:18
GUI需要不断地验证鼠标、键盘等事件,并在满足特定条件时执行操作,这称为eventloop。在sleep()
的情况下,它是一个阻塞任务,不允许eventloop运行,生成冻结的图形用户界面(如果你想验证它,试着改变窗口的大小),所以在GUI线程中你应该避免使用那种函数,阻塞任务你必须把它们变成异步的或在另一个线程中执行它。
但是sleep()
的任务可以由QTimer替代,而不会阻塞图形用户界面:
*.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTimer>
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_2_clicked();
void onTimeout();
private:
Ui::Widget *ui;
int counter;
QTimer timer;
};
#endif // WIDGET_H
*.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
counter = 0;
connect(&timer, &QTimer::timeout, this, &Widget::onTimeout);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_2_clicked()
{
timer.start(2000);
}
void Widget::onTimeout()
{
ui->label->setText(QString::number(counter));
counter++;
if(counter > 10){
counter = 0;
timer.stop();
}
}
另一种选择是在QTimer
中使用QEventLoop
void Widget::on_pushButton_2_clicked()
{
for(int i=0;i<10;i++){
QEventLoop loop;
QTimer::singleShot(2000, &loop, &QEventLoop::quit);
loop.exec();
ui->label->setText(QString::number(i));
}
}
更新:
图形用户界面和非图形用户界面程序中同步阻塞函数休眠的不同结果是什么?
如果您使用Qt创建非GUI应用程序,您也会遇到问题,尽管效果可能不太明显。
在图形用户界面的情况下,正如我所说的,有一个事件循环来处理事件,其中包括重绘事件,我的意思是当你在QLabel
中设置新文本时,这不会自动绘制,但Qt会决定正确的时机。这就是为什么当您使用QThread::sleep()
时,您没有时间更新绘画。
显然,在非GUI应用程序中,eventloop不会验证许多事件,因为它所绘制的事件看不到效果,事实上,在只打印数字的脚本中,它不会验证任何事件。
为了注意这个问题,让我们使用以下示例:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [](){
qDebug()<< "hello world";
});
timer.start(1000);
qDebug()<< "start blocking";
QThread::sleep(10);
qDebug()<< "end blocking";
return a.exec();
}
我们将看到,在sleep()
结束之前,不会打印任何内容,即阻塞允许QTimer
执行其工作的eventloop。
回答你的评论:
但仍然让我困惑的是,为什么在执行完睡眠函数后,在停止阻塞当前线程之后,以下代码不能像normal
ui->label->setText(QString::number(i));
语句一样运行,紧跟在睡眠函数之后异步任务(如绘画)的优先级低于同步任务,也就是说,第一个Qt将执行for循环,然后只执行异步任务,因此在for中,存储QLabel
文本的变量被更新,也就是说,0,1,...,9,之后,任务被移交给eventloop,因此它只绘制最后一个值,即9。
注意:
您可以强制使用QXXXApplication::processEvents()
在同步执行中更新often循环,但这通常被认为是一种糟糕的做法,我展示它只是为了让您知道它,但避免使用它:
for(int i=0;i<10;i++){
QThread.sleep(2);
ui->label->setText(QString::number(i));
QApplication::processEvents();
}
https://stackoverflow.com/questions/51044697
复制相似问题