使用 QTimer、QThread 和进度条
Work with QTimer, QThread and Progress Bar
我想在与 Ui 线程分开使用计算。计算方法 — 主窗口的私有插槽 Метод.在计算过程中,计算数据在对象输出数据 — 进度条窗口的字段中逐渐传输。执行计算的线程也是主窗口的一个字段。
主窗口构造函数,其中创建线程并将计算开始连接到线程的开头:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
calculation = new QThread(this);
settings.setToDefault();
outputData.setToDefault();
calculationDone = false;
connect(calculation, SIGNAL(started()), this, SLOT(calculate()));
ui->results_display->setText("Загрузите <b>параметры</b> и начинайте расчёты!");
}
按钮方法,启动带有进度条的线程和模式窗口:
void MainWindow::on_calculate_button_clicked() {
ui->results_display->clear();
ui->results_display2->clear();
calculation->start();
///TODO QProgressDialog
ProgressDialog progressDialog(&outputData, this);
progressDialog.setModal(true);
progressDialog.exec();
if (progressDialog.result() == QDialog::Rejected) {
calculation->terminate();
QMessageBox::critical(this, "Результат", "Расчёт был остановлен!");
} else {
if (progressDialog.result() == QDialog::Accepted) {
calculation->quit();
QMessageBox::about(this, "Результат", "Готово!");
}
}
}
模态窗口构造函数设置进度条的参数,创建计时器并设置计时器和更新方法之间的连接:
ProgressDialog::ProgressDialog(OutputData *outputData, QWidget *parent) :
QDialog(parent),
ui(new Ui::ProgressDialog) {
ui->setupUi(this);
data = outputData;
ui->quantity_label->setText("0");
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(static_cast<int>(data->outputSettings.aircraftQuantity));
timer = new QTimer(this);
timer->setSingleShot(false);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(50);
}
进度条更新方法:
void ProgressDialog::update() {
unsigned long aircraftsDone = data->results.size() + data->unprocessedAircrafts.size();
ui->progressBar->setValue(static_cast<int>(aircraftsDone));
ui->aircraftQunatityDone_label->setText(QString::number(aircraftsDone));
ui->progressBar->repaint();
if (aircraftsDone == data->outputSettings.aircraftQuantity) {
accept();
}
}
计算当前运行良好,但不会绘制或更新进度信息。
这是因为您的calculate
方法未在新线程中调用。
要使用QThread
在新线程中运行任何方法,您必须使用QObject::moveToThread
将此方法的公共插槽对象(此对象必须继承自QObject
类(移动到QThread
实例中,然后将您的方法与QThread::started
信号连接。 在您的情况下,您只需在MainWindow
中的插槽calculate
QThread::started
信号之间设置连接calculation
,因此当您调用calculation->start()
时,您将在调用calculation->start()
的同一线程中触发calculate
方法。
若要解决此问题,应为calculate
方法创建一个单独的类,然后将此类的对象移动到构造函数MainWindow
QThread
中。
我建议您查看参考中的示例
这些是Rhathin的答案和参考的示例代码。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include "calculationworker.h"
#include "progressdialog.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QThread *calculationThread;
CalculationWorker *calculationWorker;
signals:
void startCalculation();
void stopCalculation();
private slots:
void on_calculate_button_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
calculationThread = new QThread( this );
calculationWorker = new CalculationWorker();
calculationWorker->moveToThread( calculationThread );
connect( this, SIGNAL(startCalculation()), calculationWorker, SLOT(calculate()) );
connect( this, SIGNAL(stopCalculation()), calculationWorker, SLOT(stopCalculation()), Qt::DirectConnection );
calculationThread->start();
}
MainWindow::~MainWindow()
{
calculationThread->quit();
calculationThread->wait();
delete calculationWorker;
delete ui;
}
void MainWindow::on_calculate_button_clicked()
{
ProgressDialog progressDialog( this );
connect( calculationWorker, SIGNAL(progress(int, int)), &progressDialog, SLOT(progress(int, int)) );
connect( calculationWorker, SIGNAL(completed()), &progressDialog, SLOT(completed()) );
emit startCalculation();
progressDialog.setModal( true );
progressDialog.exec();
if ( progressDialog.result() == QDialog::Rejected ) {
emit stopCalculation();
} else if ( progressDialog.result() == QDialog::Accepted ) {
// Accepted
}
}
计算工作者.h
#ifndef CALCULATIONWORKER_H
#define CALCULATIONWORKER_H
#include <QObject>
#include <QThread> // msleep()
class CalculationWorker : public QObject
{
Q_OBJECT
public:
explicit CalculationWorker(QObject *parent = nullptr);
signals:
void progress( int processed, int quantity );
void stopped();
void completed();
public slots:
void calculate();
void stopCalculation();
private:
bool m_stopFlag;
};
#endif // CALCULATIONWORKER_H
计算工作者.cpp
#include "calculationworker.h"
CalculationWorker::CalculationWorker(QObject *parent) : QObject(parent)
{
}
void CalculationWorker::calculate()
{
m_stopFlag = false;
int quantity = 1000;
for ( int i = 0; i < quantity; i++ ) {
if ( m_stopFlag ) {
emit stopped();
return;
}
// Do calculation here
QThread::msleep( 10 );
emit progress( i, quantity );
}
emit completed();
}
void CalculationWorker::stopCalculation()
{
m_stopFlag = true;
}
进度对话框.h
#ifndef PROGRESSDIALOG_H
#define PROGRESSDIALOG_H
#include <QDialog>
namespace Ui {
class ProgressDialog;
}
class ProgressDialog : public QDialog
{
Q_OBJECT
public:
explicit ProgressDialog(QWidget *parent = nullptr);
~ProgressDialog();
private:
Ui::ProgressDialog *ui;
public slots:
void progress( int processed, int quantity );
void completed();
};
#endif // PROGRESSDIALOG_H
进度对话框.cpp
#include "progressdialog.h"
#include "ui_progressdialog.h"
ProgressDialog::ProgressDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ProgressDialog)
{
ui->setupUi(this);
}
ProgressDialog::~ProgressDialog()
{
delete ui;
}
void ProgressDialog::progress(int processed, int quantity)
{
ui->progressBar->setMaximum( quantity );
ui->progressBar->setValue( processed );
}
void ProgressDialog::completed()
{
accept();
}
这些是另一个版本示例代码,在计算时不会发出进度。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include "calculationworker.h"
#include "progressdialog.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QThread *calculationThread;
CalculationWorker *calculationWorker;
signals:
void startCalculation();
private slots:
void on_calculate_button_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
calculationThread = new QThread( this );
calculationWorker = new CalculationWorker();
calculationWorker->moveToThread( calculationThread );
connect( this, SIGNAL(startCalculation()), calculationWorker, SLOT(calculate()) );
calculationThread->start();
}
MainWindow::~MainWindow()
{
calculationThread->quit();
calculationThread->wait();
delete calculationWorker;
delete ui;
}
void MainWindow::on_calculate_button_clicked()
{
ProgressDialog progressDialog( calculationWorker ,this );
emit startCalculation();
progressDialog.exec();
if ( progressDialog.result() == QDialog::Rejected ) {
// Rejected(Stopped)
} else if ( progressDialog.result() == QDialog::Accepted ) {
// Accepted
}
}
计算工作者.h
#ifndef CALCULATIONWORKER_H
#define CALCULATIONWORKER_H
#include <QObject>
#include <QThread> // msleep()
class CalculationWorker : public QObject
{
Q_OBJECT
public:
explicit CalculationWorker(QObject *parent = nullptr);
int getQuantity();
int getProcessed();
signals:
void started();
void stopped();
void completed();
public slots:
void calculate();
void stopCalculation();
private:
bool m_stopFlag;
bool m_stopped;
int m_processed;
int m_quantity;
};
#endif // CALCULATIONWORKER_H
计算工作者.cpp
#include "calculationworker.h"
CalculationWorker::CalculationWorker(QObject *parent) : QObject(parent)
{
m_quantity = 1000;
}
int CalculationWorker::getQuantity()
{
// Return quantity (data->outputSettings.aircraftQuantity)))
return m_quantity;
}
int CalculationWorker::getProcessed()
{
// Return processed (data->results.size() + data->unprocessedAircrafts.size())
return m_processed;
}
void CalculationWorker::calculate()
{
m_stopFlag = false;
emit started();
// Calculation
for ( m_processed = 0; m_processed < m_quantity; m_processed++ ) {
if ( m_stopFlag ) {
// emit stopped();
return;
}
// Do calculation here
QThread::msleep( 10 );
}
emit completed();
}
void CalculationWorker::stopCalculation()
{
m_stopFlag = true;
}
进度对话框.h
#ifndef PROGRESSDIALOG_H
#define PROGRESSDIALOG_H
#include <QDialog>
#include <QTimer>
#include "calculationworker.h"
namespace Ui {
class ProgressDialog;
}
class ProgressDialog : public QDialog
{
Q_OBJECT
public:
explicit ProgressDialog( CalculationWorker *worker, QWidget *parent = nullptr);
~ProgressDialog();
private:
Ui::ProgressDialog *ui;
CalculationWorker *m_worker;
QTimer *m_progressTimer;
public slots:
void started();
void update();
};
#endif // PROGRESSDIALOG_H
进度对话框.cpp
#include "progressdialog.h"
#include "ui_progressdialog.h"
ProgressDialog::ProgressDialog( CalculationWorker *worker, QWidget *parent) :
QDialog(parent),
ui(new Ui::ProgressDialog),
m_worker(worker),
m_progressTimer(new QTimer(parent))
{
ui->setupUi(this);
// Connect started signal from worker
connect( m_worker, SIGNAL(started()), this, SLOT(started()) );
// Stop calculation by clicking reject button
connect( this, SIGNAL(rejected()), m_worker, SLOT(stopCalculation()), Qt::DirectConnection );
// Connect timeout signal to update
connect( m_progressTimer, SIGNAL(timeout()), this, SLOT(update()) );
// Stop the timer when the dialog is closed
connect( this, SIGNAL(finished(int)), m_progressTimer, SLOT(stop()) );
}
ProgressDialog::~ProgressDialog()
{
delete ui;
}
void ProgressDialog::started()
{
ui->progressBar->setMaximum( m_worker->getQuantity() );
m_progressTimer->start( 50 );
}
void ProgressDialog::update()
{
ui->progressBar->setValue( m_worker->getProcessed() );
if ( m_worker->getProcessed() == m_worker->getQuantity() ) {
accept();
}
}
- 如何监控QThread
- 以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)
- 如何从其他类n个Qt C++调用QTimer?
- 如何完全停止QTimer
- 等待 qthread 终止的正确方法是什么?
- 如何正确停止运行非循环QThread?
- QTimer 超时不会在单元测试中触发
- Valgrind 在 QThread::start() 上报告内存泄漏
- 如何在 QThread 中传递引用
- 使用 QTimer、QThread 和进度条
- 为什么我不能调用 QMetaObject::invokeMethod(&threadObj, &QThread::start, Qt::QueuedConnection)?
- QThread::create 似乎没有将参数传递给函数
- 在C++中使用std或boost库的Qtimer等价物是什么
- qthread带有qtimer和qserial-育儿
- 如何在 QThread 中使用 QTimer
- 在 QThread 中启动 QTimer
- 使用QThread和QTimer来运行方法
- QThread and QTimer
- 自定义类,具有自己的QTimer和QThread
- 我可以用QTimer来代替QThread吗?