Qt 流畅的运行大循环

        在我早期开发的时候,处理循环问题就喜欢用for直接运行到底, 但如果要同时更新界面的输出信息、进度信息,不幸遇到几万次甚至几十万次循环的时候,随便动一下界面就白屏了,界面上其他的操作就完全没法响应。

举个小例子:

void MainWindow::on_btn_print_clicked()
{
    //清理输出信息
    ui->textBrowser->clear();

    //获取打印行数
    int count = ui->spinBox->value();
    
    //设置进度值范围
    ui->progressBar->setRange(0, count);
    ui->progressBar->setValue(0);

    //循环打印
    for(int i = 0; i < count; i++) {
        ui->textBrowser->append("最近多次持续40℃以上高温天气,未来一周左右还会继续...");

        //将数据输出到文件中或是做一些其他操作...

        //更新进度值
        ui->progressBar->setValue(i+1);
    }

    ui->textBrowser->append("输出完毕...");
}

 我们来运行一下demo,看一下效果,这里是GIF截图:

        如图所示,随便点一下就卡住了。后来想到用线程处理数据上的操作, 但是UI的打印输出只能在主线程中进行 ,循环太久,主线程还是会卡顿,毕竟CPU不会给一个APP太多的占用时间。后来在别人的开源代码中学到了一个方法,只需要一句话就解决了这个问题。

    //循环打印
    for(int i = 0; i < count; i++) {
        ui->textBrowser->append("最近多次持续40℃以上高温天气,未来一周左右还会继续...");

        //将数据输出到文件中或是做一些其他操作...

        //更新进度值
        ui->progressBar->setValue(i+1);

        //优先响应UI事件,防止卡死, 提升用户体验
        qApp->processEvents();
    }

就是这个 qApp->processEvents(), 以下是Qt帮助文档中的描述:

[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents)

Processes all pending events for the calling thread according to the specified flags until there are no more events to process.

You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).

In the event that you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop.

Calling this function processes events only for the calling thread.

Note: This function is thread-safe.

        根据指定的标志处理调用线程的所有挂起事件,直到没有更多事件要处理。当您的程序忙于执行长时间操作(例如复制文件)时,您可以偶尔调用此函数。如果您正在运行一个连续调用此函数的本地循环,而没有事件循环,则不会处理 DeferredDelete 事件。这会影响widgets的行为,例如QToolTip,依赖 DeferredDelete 事件才能正常运行。另一种方法是从该本地循环中调用 sendPostedEvents()。调用此函数仅为调用线程处理事件。

优化后, 我们再来看一下运行结果:

打印50W行数据的同时,执行其他操作不会卡住了。 

以下是完整的代码:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_btn_print_clicked();

    void on_btn_stop_clicked();

private:
    Ui::MainWindow *ui;

    bool print_lock;
    bool stop_flag;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    print_lock = false;
    stop_flag = false;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_btn_print_clicked()
{
    if (print_lock){
        return;
    }

    //防止重复执行
    print_lock = true;

    //获取打印行数
    int count = ui->spinBox->value();
    //清理输出信息
    ui->textBrowser->clear();

    //设置进度值范围
    ui->progressBar->setRange(0, count);
    ui->progressBar->setValue(0);

    //循环打印
    for(int i = 0; i < count; i++) {
        ui->textBrowser->append("最近多次持续40℃以上高温天气,未来一周左右还会继续...");

        //将数据输出到文件中或是做一些其他操作...

        //更新进度值
        ui->progressBar->setValue(i+1);

        //停止
        if (stop_flag) {
            break;
        }

        //优先响应UI事件,防止卡死, 提升用户体验
        qApp->processEvents();
    }

    if (stop_flag){
        ui->textBrowser->append("输出中断...");
    } else {
        ui->textBrowser->append("输出完毕...");
    }

    print_lock = false;
    stop_flag = false;
}

void MainWindow::on_btn_stop_clicked()
{
    //正在打印中
    if (print_lock)
        stop_flag = true;
    else
        stop_flag = false;
}

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值