Qt for 鸿蒙PC 多线程绘制开源鸿蒙开发实践
📋 项目概述

项目地址:https://gitcode.com/szkygc/HarmonyOs_PC-PGC/tree/main/Multithreading
本文档基于一个完整的 Multithreading 项目,详细介绍了如何在 HarmonyOS 平台上使用 Qt 实现多线程绘制功能。项目实现了两个独立的工作线程同时绘制圆形和方形,展示了 Qt 多线程编程、QML 与 C++ 集成、Canvas 绘制等技术在 HarmonyOS 平台上的实际应用。
✨ 主要功能
- ✅ 多线程绘制:两个独立线程同时绘制圆形和方形
- ✅ Worker 模式:使用 QThread + Worker 模式,符合 Qt 最佳实践
- ✅ C++/QML 集成:C++ 自定义类型注册到 QML,实现无缝集成
- ✅ Canvas 实时绘制:使用 Canvas 2D API 实时绘制线程生成的点
- ✅ 线程安全通信 :使用信号槽机制和
Qt::QueuedConnection确保线程安全 - ✅ 线程生命周期管理:完善的线程启动、停止和清理机制
- ✅ 防止重复调用 :使用标志位防止
doWork被重复调用 - ✅ 自适应窗口调整:窗口尺寸变化时自动调整绘制参数
- ✅ 优雅停止机制:支持线程中断和资源清理
🎯 技术亮点
多线程架构:
- 使用
QThread+Worker模式,符合 Qt 最佳实践 - 工作对象(Worker)在独立线程中运行
- 主对象(Thread)在主线程中供 QML 使用
- 使用
Qt::QueuedConnection确保线程安全
线程管理:
- 防止重复调用
doWork的机制 - 工作完成后自动停止线程
- 完善的线程清理和资源释放
HarmonyOS 适配:
- 使用
qtmain()作为入口函数 - OpenGL ES 配置
- QML 与 C++ 类型注册
🛠️ 技术栈
- 开发框架: Qt 5.15+ for HarmonyOS
- 编程语言: C++ / QML / JavaScript
- 多线程框架: QThread + Worker 模式
- 图形渲染: Canvas 2D API
- 界面框架: Qt Quick Controls 2
- 构建工具: CMake
- 目标平台: HarmonyOS (OpenHarmony) / PC
🏗️ 项目架构
目录结构
Multithreading/
├── entry/src/main/
│ ├── cpp/
│ │ ├── main.cpp # 应用入口(HarmonyOS适配)
│ │ ├── main.qml # 主界面(Canvas绘制)
│ │ ├── CircleThread.h # 圆形绘制线程头文件
│ │ ├── CircleThread.cpp # 圆形绘制线程实现
│ │ ├── SquareThread.h # 方形绘制线程头文件
│ │ ├── SquareThread.cpp # 方形绘制线程实现
│ │ ├── CMakeLists.txt # 构建配置
│ │ └── qml.qrc # QML资源文件
│ ├── module.json5 # 模块配置
│ └── resources/ # 资源文件
└── image/
└── 演示示例.gif # 演示动图
组件层次结构
ApplicationWindow (main.qml)
├── ColumnLayout
│ ├── Text (标题)
│ ├── Rectangle (画布容器)
│ │ └── Canvas (绘制组件)
│ │ ├── onPaint (绘制函数)
│ │ └── 绘制圆形点(蓝色)
│ │ └── 绘制方形点(红色)
│ ├── RowLayout (控制按钮)
│ │ ├── Button (开始绘制)
│ │ ├── Button (停止绘制)
│ │ └── Button (清空画布)
│ └── Text (状态显示)
├── CircleThread (C++ 线程对象)
│ ├── CircleWorker (工作对象)
│ │ └── doWork() (在工作线程中执行)
│ └── 信号: circlePoint, finished
└── SquareThread (C++ 线程对象)
├── SquareWorker (工作对象)
│ └── doWork() (在工作线程中执行)
└── 信号: squarePoint, finished
多线程架构设计
主线程 (Main Thread)
├── ApplicationWindow (QML)
├── CircleThread (QObject)
│ ├── 管理 QThread
│ ├── 管理 CircleWorker
│ └── 信号槽连接 (Qt::QueuedConnection)
└── SquareThread (QObject)
├── 管理 QThread
├── 管理 SquareWorker
└── 信号槽连接 (Qt::QueuedConnection)
工作线程 1 (Worker Thread 1)
└── CircleWorker
└── doWork() - 计算圆形点
工作线程 2 (Worker Thread 2)
└── SquareWorker
└── doWork() - 计算方形点
📝 核心功能实现
1. HarmonyOS 入口函数:qtmain()
⚠️ 关键要点 :HarmonyOS 真机上必须使用 qtmain() 而不是 main()!
cpp
// ✅ 正确写法
extern "C" int qtmain(int argc, char **argv)
{
// Qt 应用作为共享库加载,生命周期由 HarmonyOS 管理
QGuiApplication app(argc, argv);
// 注册 C++ 类型到 QML
qmlRegisterType<CircleThread>("Multithreading", 1, 0, "CircleThread");
qmlRegisterType<SquareThread>("Multithreading", 1, 0, "SquareThread");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec(); // ⚠️ 重要:必须调用 exec() 启动事件循环
}
// ❌ 错误写法(桌面应用方式)
int main(int argc, char *argv[])
{
// 这种方式在 HarmonyOS 上会导致应用无法正常启动
}
原因说明:
- HarmonyOS 将 Qt 应用作为共享库(.so)加载
- 应用生命周期由 HarmonyOS 的 Ability 管理
qtmain()是 HarmonyOS Qt 插件的标准入口点
2. OpenGL ES 表面格式配置
⚠️ 关键要点 :必须在创建 QGuiApplication 之前 配置 QSurfaceFormat!
cpp
// Step 1: 配置 OpenGL ES 表面格式(必须在创建应用之前!)
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QSurfaceFormat format;
// 设置 Alpha 通道(透明度)
format.setAlphaBufferSize(8); // 8 位 Alpha 通道
// 设置颜色通道(RGBA 32 位真彩色)
format.setRedBufferSize(8); // 8 位红色通道
format.setGreenBufferSize(8); // 8 位绿色通道
format.setBlueBufferSize(8); // 8 位蓝色通道
// 设置深度和模板缓冲区
format.setDepthBufferSize(24); // 24 位深度缓冲
format.setStencilBufferSize(8); // 8 位模板缓冲
// 指定渲染类型为 OpenGL ES(HarmonyOS要求)
format.setRenderableType(QSurfaceFormat::OpenGLES);
// 指定 OpenGL ES 版本为 2.0(推荐,兼容性更好)
format.setVersion(2, 0);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
format.setSamples(0);
// ⚠️ 关键:必须在创建 QGuiApplication 之前设置默认格式!
QSurfaceFormat::setDefaultFormat(format);
// Step 2: 创建 Qt 应用实例(必须在设置格式之后)
QGuiApplication app(argc, argv);
3. Qt 多线程架构:Worker 模式
核心思想:将工作对象(Worker)移动到工作线程,主对象(Thread)在主线程中供 QML 使用。
3.1 Worker 类设计
cpp
// 工作对象:实际在工作线程中运行
class CircleWorker : public QObject
{
Q_OBJECT
public:
explicit CircleWorker(QObject *parent = nullptr);
void requestStop() { m_shouldStop = true; }
public slots:
void doWork(const QPoint ¢er, int radius);
signals:
void pointGenerated(const QPoint &pt);
void workFinished();
private:
bool m_shouldStop;
};
关键点:
CircleWorker继承自QObject,可以使用信号槽doWork()是槽函数,在工作线程中执行- 通过信号
pointGenerated和workFinished与主线程通信 m_shouldStop标志用于优雅停止
3.2 Thread 类设计
cpp
// 主对象:始终在主线程,供QML使用
class CircleThread : public QObject
{
Q_OBJECT
Q_PROPERTY(QPoint center READ center WRITE setCenter NOTIFY centerChanged)
Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
public:
explicit CircleThread(QObject *parent = nullptr);
~CircleThread();
QPoint center() const { return m_center; }
void setCenter(const QPoint ¢er);
int radius() const { return m_radius; }
void setRadius(int radius);
bool running() const { return m_running; }
public slots:
void start();
void stop();
signals:
void circlePoint(const QPoint &pt);
void centerChanged();
void radiusChanged();
void runningChanged();
void finished();
private slots:
void onWorkerFinished();
void onPointGenerated(const QPoint &pt);
void onThreadStarted();
private:
QPoint m_center;
int m_radius;
QThread *m_thread;
CircleWorker *m_worker;
bool m_running;
bool m_workStarted; // 防止重复调用 doWork
};
关键点:
CircleThread继承自QObject,使用Q_PROPERTY暴露属性给 QML- 管理
QThread和CircleWorker的生命周期 - 使用
Qt::QueuedConnection连接信号槽,确保线程安全
4. 线程初始化和 Worker 移动
⚠️ 关键要点:Worker 对象必须移动到工作线程!
cpp
CircleThread::CircleThread(QObject *parent)
: QObject(parent)
, m_center(0, 0)
, m_radius(0)
, m_thread(nullptr)
, m_worker(nullptr)
, m_running(false)
, m_workStarted(false)
{
// 创建工作线程和工作对象
m_thread = new QThread(this);
m_worker = new CircleWorker();
// ⚠️ 关键:将工作对象移动到工作线程
// 这一步非常重要!只有移动到线程后,Worker 的槽函数才会在工作线程中执行
m_worker->moveToThread(m_thread);
// 连接信号槽:工作对象 -> 主对象
// ⚠️ 使用 Qt::QueuedConnection 确保线程安全
connect(m_worker, &CircleWorker::pointGenerated,
this, &CircleThread::onPointGenerated,
Qt::QueuedConnection);
connect(m_worker, &CircleWorker::workFinished,
this, &CircleThread::onWorkerFinished,
Qt::QueuedConnection);
// 连接线程信号:线程启动后开始工作
connect(m_thread, &QThread::started,
this, &CircleThread::onThreadStarted);
// 线程结束时清理工作对象
connect(m_thread, &QThread::finished,
m_worker, &QObject::deleteLater);
}
为什么使用 Qt::QueuedConnection:
Qt::QueuedConnection确保信号在接收对象的线程中执行- Worker 在工作线程中发送信号,主对象在主线程中接收
- 这样可以安全地更新 UI(QML 必须在主线程中更新)
5. 线程启动和防止重复调用
问题 :QThread::started 信号和备用机制可能同时触发,导致 doWork 被调用两次。
解决方案 :使用 m_workStarted 标志防止重复调用。
cpp
void CircleThread::start()
{
// 检查是否已经在运行
if (m_running || !m_thread) {
return;
}
// 如果线程已经在运行,先停止并等待完成
if (m_thread->isRunning()) {
stop();
if (!m_thread->wait(2000)) {
qWarning() << "CircleThread: 线程未在2秒内停止";
return;
}
}
// 重新创建工作对象(如果已被删除)
if (!m_worker) {
m_worker = new CircleWorker();
m_worker->moveToThread(m_thread);
// 重新连接信号槽...
}
// 重置标志
m_running = true;
m_workStarted = false; // ⚠️ 重置工作开始标志
emit runningChanged();
// 启动线程
m_thread->start();
// 备用机制:如果信号没有触发,延迟调用 onThreadStarted
QTimer::singleShot(50, this, [this]() {
// ⚠️ 检查 m_workStarted,防止重复调用
if (m_running && !m_workStarted && m_worker && m_thread->isRunning()) {
qDebug() << "CircleThread: 备用机制 - 直接调用 onThreadStarted";
onThreadStarted();
}
});
}
void CircleThread::onThreadStarted()
{
// ⚠️ 防止重复调用
if (m_workStarted) {
qDebug() << "CircleThread: onThreadStarted 已被调用过,跳过重复调用";
return;
}
// 检查 worker 是否存在
if (!m_worker) {
qWarning() << "CircleThread: m_worker 为 nullptr,无法调用 doWork";
return;
}
// ⚠️ 标记工作已开始,防止重复调用
m_workStarted = true;
// 使用 QMetaObject::invokeMethod 在工作线程中调用 doWork
bool success = QMetaObject::invokeMethod(m_worker, "doWork",
Qt::QueuedConnection,
Q_ARG(QPoint, m_center),
Q_ARG(int, m_radius));
if (!success) {
qWarning() << "CircleThread: invokeMethod 失败";
m_workStarted = false; // 如果调用失败,重置标志
}
}
关键点:
m_workStarted标志确保doWork只被调用一次- 备用机制中也检查该标志,避免重复调用
- 如果
invokeMethod失败,重置标志以便重试
6. Worker 工作实现
圆形绘制:使用参数方程计算圆周上的点。
cpp
void CircleWorker::doWork(const QPoint ¢er, int radius)
{
qDebug() << "CircleWorker::doWork 开始 - center:" << center << "radius:" << radius;
m_shouldStop = false;
// 检查参数有效性
if (radius <= 0) {
qWarning() << "CircleWorker: radius 无效:" << radius;
emit workFinished();
return;
}
// 使用固定点数绘制圆形(360个点,每个点1度)
const int totalPoints = 360;
double radianIncrement = 2.0 * M_PI / totalPoints;
int pointCount = 0;
// 遍历所有点
for (int i = 0; i < totalPoints && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++i) {
// 计算当前角度
double circleAngle = i * radianIncrement;
// 计算新点的位置(参数方程)
int centerX = center.x();
int centerY = center.y();
int x = centerX + static_cast<int>(radius * cos(circleAngle));
int y = centerY + static_cast<int>(radius * sin(circleAngle));
// 发送新点的位置(通过信号发送到主线程)
emit pointGenerated(QPoint(x, y));
pointCount++;
QThread::msleep(62); // 控制绘制速度
}
qDebug() << "CircleWorker: 画圆完成,共生成" << pointCount << "个点";
emit workFinished();
}
方形绘制:沿着四条边依次绘制点。
cpp
void SquareWorker::doWork(const QPoint &topLeft, int sideLength)
{
qDebug() << "SquareWorker::doWork 开始 - topLeft:" << topLeft << "sideLength:" << sideLength;
m_shouldStop = false;
// 检查参数有效性
if (sideLength <= 0) {
qWarning() << "SquareWorker: sideLength 无效:" << sideLength;
emit workFinished();
return;
}
// 方形的四个顶点
QPoint points[4] = {
topLeft,
QPoint(topLeft.x() + sideLength, topLeft.y()),
QPoint(topLeft.x() + sideLength, topLeft.y() + sideLength),
QPoint(topLeft.x(), topLeft.y() + sideLength)
};
int pointCount = 0;
// 沿着每条边绘制点
for (int i = 0; i < 4 && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++i) {
QPoint start = points[i];
QPoint end = points[(i + 1) % 4];
// 使用浮点数计算避免整数除法精度问题
double dx = static_cast<double>(end.x() - start.x()) / sideLength;
double dy = static_cast<double>(end.y() - start.y()) / sideLength;
for (int j = 0; j <= sideLength && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++j) {
int x = start.x() + static_cast<int>(dx * j);
int y = start.y() + static_cast<int>(dy * j);
emit pointGenerated(QPoint(x, y));
pointCount++;
QThread::msleep(5); // 控制绘制速度
}
}
qDebug() << "SquareWorker: 画方完成,共生成" << pointCount << "个点";
emit workFinished();
}
关键点:
- 检查
m_shouldStop和QThread::currentThread()->isInterruptionRequested()以支持优雅停止 - 使用
emit pointGenerated()将点发送到主线程 - 工作完成后发送
workFinished()信号
7. 线程停止和清理
⚠️ 关键要点:工作完成后必须停止线程!
cpp
void CircleThread::onWorkerFinished()
{
qDebug() << "CircleThread: onWorkerFinished 被调用 - 当前 m_running:" << m_running;
// ⚠️ 停止线程
if (m_thread && m_thread->isRunning()) {
qDebug() << "CircleThread: 工作完成,停止线程";
m_thread->quit();
if (!m_thread->wait(1000)) {
qWarning() << "CircleThread: 线程未在1秒内停止,强制终止";
m_thread->terminate();
m_thread->wait(500);
}
}
// 更新状态
m_running = false;
m_workStarted = false; // 重置工作开始标志
emit runningChanged();
emit finished();
// 标记worker为null,因为会被deleteLater删除
m_worker = nullptr;
qDebug() << "CircleThread: 工作完成";
}
void CircleThread::stop()
{
if (!m_thread) {
return;
}
// 无论 m_running 是什么,只要线程在运行就停止它
if (m_thread->isRunning()) {
qDebug() << "CircleThread: 停止线程 - m_running:" << m_running;
// 通知工作对象停止
if (m_worker) {
QMetaObject::invokeMethod(m_worker, "requestStop", Qt::QueuedConnection);
}
m_thread->requestInterruption();
m_thread->quit();
if (!m_thread->wait(1000)) {
qWarning() << "CircleThread: 线程未在1秒内停止,强制终止";
m_thread->terminate();
m_thread->wait(500);
}
}
// 更新运行状态
if (m_running) {
m_running = false;
m_workStarted = false; // 重置工作开始标志
emit runningChanged();
}
qDebug() << "CircleThread: 停止画圆线程完成 - isRunning:" << (m_thread ? m_thread->isRunning() : false);
}
关键点:
- 在
onWorkerFinished()中自动停止线程 - 使用
quit()优雅停止,如果超时则使用terminate()强制终止 - 重置所有状态标志
- Worker 对象通过
deleteLater自动清理
8. QML 中使用 C++ 类型
注册类型:
cpp
// 在 main.cpp 中注册
qmlRegisterType<CircleThread>("Multithreading", 1, 0, "CircleThread");
qmlRegisterType<SquareThread>("Multithreading", 1, 0, "SquareThread");
QML 中使用:
qml
import Multithreading 1.0
ApplicationWindow {
id: root
// 圆形点集合
property var circlePoints: []
// 方形点集合
property var squarePoints: []
// 画圆线程
CircleThread {
id: circleThread
onCirclePoint: function(pt) {
root.addCirclePoint(pt);
}
onRunningChanged: {
console.log("Multithreading: 圆形线程 running 状态变化 - running:", running);
}
onFinished: {
console.log("Multithreading: 画圆线程完成 - running:", running);
}
}
// 画方线程
SquareThread {
id: squareThread
onSquarePoint: function(pt) {
root.addSquarePoint(pt);
}
onRunningChanged: {
console.log("Multithreading: 方形线程 running 状态变化 - running:", running);
}
onFinished: {
console.log("Multithreading: 画方线程完成 - running:", running);
}
}
// 添加圆形点
function addCirclePoint(pt) {
if (!pt) {
return;
}
var newPoints = circlePoints.slice();
newPoints.push({"x": pt.x, "y": pt.y});
circlePoints = newPoints;
// 触发 Canvas 重绘
if (canvas.width > 0 && canvas.height > 0) {
canvas.requestPaint();
}
}
// 添加方形点
function addSquarePoint(pt) {
if (!pt) {
return;
}
var newPoints = squarePoints.slice();
newPoints.push({"x": pt.x, "y": pt.y});
squarePoints = newPoints;
// 触发 Canvas 重绘
if (canvas.width > 0 && canvas.height > 0) {
canvas.requestPaint();
}
}
// 启动绘制
Button {
text: "开始绘制"
enabled: !circleThread.running && !squareThread.running
onClicked: {
// 清空之前的点
circlePoints = [];
squarePoints = [];
canvas.requestPaint();
// 更新绘制参数
updateDrawingParameters();
// 启动线程
Qt.callLater(function() {
circleThread.start();
squareThread.start();
});
}
}
}
关键点:
- 使用
import Multithreading 1.0导入自定义类型 - 通过
onCirclePoint等信号处理器接收数据 - 使用
running属性控制按钮状态 - 在 QML 中直接调用 C++ 的
start()和stop()方法
9. Canvas 绘制实现
Canvas 绘制函数:
qml
Canvas {
id: canvas
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
if (!ctx) {
return;
}
// 填充白色背景
ctx.fillStyle = "white";
ctx.fillRect(0, 0, width, height);
// 绘制圆形点(蓝色)
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
if (circlePoints.length > 0) {
ctx.beginPath();
var pathStarted = false;
for (var i = 0; i < circlePoints.length; i++) {
var pt = circlePoints[i];
// 检查坐标是否在 Canvas 范围内
if (pt.x >= 0 && pt.x <= width && pt.y >= 0 && pt.y <= height) {
if (!pathStarted) {
ctx.moveTo(pt.x, pt.y);
pathStarted = true;
} else {
ctx.lineTo(pt.x, pt.y);
}
}
}
if (pathStarted) {
ctx.stroke();
}
}
// 绘制方形点(红色)
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
if (squarePoints.length > 0) {
ctx.beginPath();
var squarePathStarted = false;
for (var j = 0; j < squarePoints.length; j++) {
var sqPt = squarePoints[j];
if (sqPt.x >= 0 && sqPt.x <= width && sqPt.y >= 0 && sqPt.y <= height) {
if (!squarePathStarted) {
ctx.moveTo(sqPt.x, sqPt.y);
squarePathStarted = true;
} else {
ctx.lineTo(sqPt.x, sqPt.y);
}
}
}
if (squarePathStarted) {
ctx.stroke();
}
}
}
}
关键点:
- 使用
onPaint处理绘制逻辑 - 检查坐标是否在 Canvas 范围内
- 使用
beginPath()、moveTo()、lineTo()、stroke()绘制路径 - 每次收到新点时调用
canvas.requestPaint()触发重绘
⚠️ 常见陷阱和最佳实践
避免的做法
-
在主线程中执行耗时操作
cpp// ❌ 错误:在主线程中执行耗时操作 void CircleThread::start() { // 这会阻塞主线程,导致 UI 卡顿 for (int i = 0; i < 360; ++i) { // 计算点... } } -
直接访问 Worker 对象
cpp// ❌ 错误:直接调用 Worker 的方法 m_worker->doWork(center, radius); // 这会在主线程中执行! -
忘记移动 Worker 到线程
cpp// ❌ 错误:忘记 moveToThread m_worker = new CircleWorker(); // 没有 moveToThread,Worker 仍在主线程中 -
使用 AutoConnection 连接信号槽
cpp// ❌ 错误:可能使用 DirectConnection(如果对象在同一线程) connect(m_worker, &CircleWorker::pointGenerated, this, &CircleThread::onPointGenerated); // 默认 AutoConnection -
不处理线程停止
cpp// ❌ 错误:工作完成后不停止线程 void CircleThread::onWorkerFinished() { m_running = false; // 忘记停止线程,导致资源浪费 }
推荐的做法
-
使用 Worker 模式
cpp// ✅ 正确:Worker 在工作线程中执行 m_worker->moveToThread(m_thread); QMetaObject::invokeMethod(m_worker, "doWork", Qt::QueuedConnection, ...); -
使用 QueuedConnection
cpp// ✅ 正确:确保线程安全 connect(m_worker, &CircleWorker::pointGenerated, this, &CircleThread::onPointGenerated, Qt::QueuedConnection); -
防止重复调用
cpp// ✅ 正确:使用标志防止重复调用 bool m_workStarted; void CircleThread::onThreadStarted() { if (m_workStarted) { return; // 已调用过,跳过 } m_workStarted = true; // 调用 doWork... } -
工作完成后停止线程
cpp// ✅ 正确:工作完成后停止线程 void CircleThread::onWorkerFinished() { if (m_thread && m_thread->isRunning()) { m_thread->quit(); m_thread->wait(1000); } m_running = false; m_workStarted = false; } -
优雅停止工作
cpp// ✅ 正确:检查停止标志 void CircleWorker::doWork(...) { for (int i = 0; i < totalPoints && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++i) { // 工作... } }
💡 完整代码示例
CircleThread.h
cpp
#ifndef CIRCLETHREAD_H
#define CIRCLETHREAD_H
#include <QObject>
#include <QThread>
#include <QPoint>
#include <QDebug>
#include <cmath>
// 工作对象:实际在工作线程中运行
class CircleWorker : public QObject
{
Q_OBJECT
public:
explicit CircleWorker(QObject *parent = nullptr);
void requestStop() { m_shouldStop = true; }
public slots:
void doWork(const QPoint ¢er, int radius);
signals:
void pointGenerated(const QPoint &pt);
void workFinished();
private:
bool m_shouldStop;
};
// 主对象:始终在主线程,供QML使用
class CircleThread : public QObject
{
Q_OBJECT
Q_PROPERTY(QPoint center READ center WRITE setCenter NOTIFY centerChanged)
Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
public:
explicit CircleThread(QObject *parent = nullptr);
~CircleThread();
QPoint center() const { return m_center; }
void setCenter(const QPoint ¢er);
int radius() const { return m_radius; }
void setRadius(int radius);
bool running() const { return m_running; }
public slots:
void start();
void stop();
signals:
void circlePoint(const QPoint &pt);
void centerChanged();
void radiusChanged();
void runningChanged();
void finished();
private slots:
void onWorkerFinished();
void onPointGenerated(const QPoint &pt);
void onThreadStarted();
private:
QPoint m_center;
int m_radius;
QThread *m_thread;
CircleWorker *m_worker;
bool m_running;
bool m_workStarted; // 防止重复调用 doWork
};
#endif // CIRCLETHREAD_H
CircleThread.cpp(关键部分)
cpp
#include "CircleThread.h"
#include <QTimer>
CircleWorker::CircleWorker(QObject *parent)
: QObject(parent)
, m_shouldStop(false)
{
}
void CircleWorker::doWork(const QPoint ¢er, int radius)
{
m_shouldStop = false;
if (radius <= 0) {
emit workFinished();
return;
}
const int totalPoints = 360;
double radianIncrement = 2.0 * M_PI / totalPoints;
for (int i = 0; i < totalPoints && !m_shouldStop &&
!QThread::currentThread()->isInterruptionRequested(); ++i) {
double circleAngle = i * radianIncrement;
int x = center.x() + static_cast<int>(radius * cos(circleAngle));
int y = center.y() + static_cast<int>(radius * sin(circleAngle));
emit pointGenerated(QPoint(x, y));
QThread::msleep(62);
}
emit workFinished();
}
CircleThread::CircleThread(QObject *parent)
: QObject(parent)
, m_center(0, 0)
, m_radius(0)
, m_thread(nullptr)
, m_worker(nullptr)
, m_running(false)
, m_workStarted(false)
{
m_thread = new QThread(this);
m_worker = new CircleWorker();
m_worker->moveToThread(m_thread);
connect(m_worker, &CircleWorker::pointGenerated,
this, &CircleThread::onPointGenerated, Qt::QueuedConnection);
connect(m_worker, &CircleWorker::workFinished,
this, &CircleThread::onWorkerFinished, Qt::QueuedConnection);
connect(m_thread, &QThread::started,
this, &CircleThread::onThreadStarted);
connect(m_thread, &QThread::finished,
m_worker, &QObject::deleteLater);
}
void CircleThread::onThreadStarted()
{
if (m_workStarted) {
return; // 防止重复调用
}
if (!m_worker) {
return;
}
m_workStarted = true;
QMetaObject::invokeMethod(m_worker, "doWork", Qt::QueuedConnection,
Q_ARG(QPoint, m_center),
Q_ARG(int, m_radius));
}
void CircleThread::onWorkerFinished()
{
if (m_thread && m_thread->isRunning()) {
m_thread->quit();
m_thread->wait(1000);
}
m_running = false;
m_workStarted = false;
emit runningChanged();
emit finished();
m_worker = nullptr;
}
void CircleThread::stop()
{
if (!m_thread) {
return;
}
if (m_thread->isRunning()) {
if (m_worker) {
QMetaObject::invokeMethod(m_worker, "requestStop", Qt::QueuedConnection);
}
m_thread->requestInterruption();
m_thread->quit();
m_thread->wait(1000);
}
if (m_running) {
m_running = false;
m_workStarted = false;
emit runningChanged();
}
}
🔗 相关资源
📝 总结
本文详细介绍了在 HarmonyOS 平台上使用 Qt 实现多线程绘制的完整方案,包括:
- 多线程架构:使用 Worker 模式,将工作对象移动到独立线程
- 线程安全 :使用
Qt::QueuedConnection确保线程间通信安全 - 防止重复调用 :使用标志位防止
doWork被重复调用 - 线程管理:工作完成后自动停止线程,完善的清理机制
- QML 集成:C++ 类型注册到 QML,实现无缝集成
- Canvas 绘制:实时绘制线程生成的点,实现动态效果
通过这个项目,我们可以学习到 Qt 多线程编程的最佳实践,以及如何在 HarmonyOS 平台上正确使用 Qt 进行应用开发。