4. QThread子类化与信号槽通信
任务目标
创建一个继承自 QThread 的工作线程子类,在 run() 中执行耗时计算任务,通过自定义信号向主线程发送进度百分比和最终计算结果,并在主线程中更新界面显示。
环境依赖
Qt 5.12+(含 Widgets 模块),支持 C++11。使用 qmake 或 CMake 构建均可,本案例给出 .pro 文件。
关键完整代码
// workerthread.h
#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
#include <QThread>
class WorkerThread : public QThread
{
Q_OBJECT
public:
explicit WorkerThread(QObject *parent = nullptr);
signals:
void progressUpdated(int percent);
void resultReady(int result);
protected:
void run() override;
};
#endif // WORKERTHREAD_H
// workerthread.cpp
#include "workerthread.h"
#include <QThread>
WorkerThread::WorkerThread(QObject *parent)
: QThread(parent)
{
}
void WorkerThread::run()
{
int total = 100;
int sum = 0;
for (int i = 1; i <= total; ++i) {
// 模拟耗时计算:累加 1 到 100
sum += i;
QThread::msleep(50); // 模拟耗时,便于观察进度变化
emit progressUpdated(i); // 发送进度
}
emit resultReady(sum); // 发送最终结果
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QPushButton>
#include <QProgressBar>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
private slots:
void startTask();
void updateProgress(int percent);
void showResult(int result);
private:
QPushButton *startButton;
QProgressBar *progressBar;
QLabel *resultLabel;
WorkerThread *worker;
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include "workerthread.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *central = new QWidget(this);
setCentralWidget(central);
startButton = new QPushButton("开始任务", this);
progressBar = new QProgressBar(this);
progressBar->setRange(0, 100);
resultLabel = new QLabel("结果:未计算", this);
QVBoxLayout *layout = new QVBoxLayout(central);
layout->addWidget(startButton);
layout->addWidget(progressBar);
layout->addWidget(resultLabel);
worker = new WorkerThread(this);
connect(startButton, &QPushButton::clicked, this, &MainWindow::startTask);
connect(worker, &WorkerThread::progressUpdated, this, &MainWindow::updateProgress);
connect(worker, &WorkerThread::resultReady, this, &MainWindow::showResult);
}
void MainWindow::startTask()
{
if (worker->isRunning())
return;
progressBar->setValue(0);
resultLabel->setText("结果:计算中...");
worker->start();
}
void MainWindow::updateProgress(int percent)
{
progressBar->setValue(percent);
}
void MainWindow::showResult(int result)
{
resultLabel->setText(QString("结果:%1").arg(result));
}
// main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
// thread_demo.pro
QT += widgets
CONFIG += c++11
SOURCES += main.cpp mainwindow.cpp workerthread.cpp
HEADERS += mainwindow.h workerthread.h
运行与验证
- 将上述文件放入同一目录,在终端执行
qmake && make(Linux/macOS)或使用 Qt Creator 打开.pro文件构建运行。 - 点击“开始任务”按钮,观察进度条从 0 平滑增长到 100,耗时约 5 秒(100 × 50ms)。
- 进度条到达 100 后,标签显示“结果:5050”。
通过条件:界面无卡顿(窗口可正常拖动、响应),进度条持续更新,最终结果正确显示 5050。若在任务执行期间窗口无响应,说明信号槽连接或线程启动方式有误。
关键说明
- 信号
progressUpdated和resultReady在子线程中发射,由于使用QueuedConnection(默认跨线程连接方式),槽函数会在主线程事件循环中执行,因此可直接安全地更新 UI。 run()是线程入口,任务完成后线程自动结束。若需中途停止,可设置标志位并在线程循环中检查,但本案例未涉及。- 禁止在主线程中直接调用
worker->run(),必须通过start()启动,否则任务会在主线程执行,界面将卡死。