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

运行与验证

  1. 将上述文件放入同一目录,在终端执行 qmake && make(Linux/macOS)或使用 Qt Creator 打开 .pro 文件构建运行。
  2. 点击“开始任务”按钮,观察进度条从 0 平滑增长到 100,耗时约 5 秒(100 × 50ms)。
  3. 进度条到达 100 后,标签显示“结果:5050”。

通过条件:界面无卡顿(窗口可正常拖动、响应),进度条持续更新,最终结果正确显示 5050。若在任务执行期间窗口无响应,说明信号槽连接或线程启动方式有误。

关键说明

  • 信号 progressUpdated 和 resultReady 在子线程中发射,由于使用 QueuedConnection(默认跨线程连接方式),槽函数会在主线程事件循环中执行,因此可直接安全地更新 UI。
  • run() 是线程入口,任务完成后线程自动结束。若需中途停止,可设置标志位并在线程循环中检查,但本案例未涉及。
  • 禁止在主线程中直接调用 worker->run(),必须通过 start() 启动,否则任务会在主线程执行,界面将卡死。