Qt for HarmonyOS 入门指南 - 从零开始的第一个应用
·
Qt for HarmonyOS 入门指南 - 从零开始的第一个应用
📌 前言
本文将带你从零开始,在HarmonyOS上开发第一个Qt应用。通过实战一个简单的时钟应用,你将学会Qt for HarmonyOS的核心概念和开发流程。
🎯 学习目标
- ✅ 理解Qt for HarmonyOS与桌面Qt的区别
- ✅ 掌握鸿蒙专用的入口函数写法
- ✅ 学会C++与QML的数据绑定
- ✅ 了解QML在鸿蒙中的特殊要求
- ✅ 完成一个可运行的时钟应用
🔍 Qt for HarmonyOS vs 桌面Qt
核心差异对比
| 特性 | 桌面Qt | HarmonyOS Qt |
|---|---|---|
| 应用类型 | 独立进程 | 嵌入式库 |
| 入口函数 | int main() |
extern "C" void qtmain() |
| 应用对象 | QGuiApplication |
不需要 |
| 事件循环 | app.exec() |
鸿蒙管理 |
| QML根元素 | Window |
Rectangle/Item |
| 窗口管理 | Qt Window | XComponent |
| 生命周期 | Qt管理 | 鸿蒙管理 |
架构对比图
桌面Qt架构:
┌──────────────────────┐
│ Qt Application │
│ ┌────────────────┐ │
│ │ QGuiApp │ │
│ │ ↓ │ │
│ │ QML Engine │ │
│ │ ↓ │ │
│ │ Window │ │
│ └────────────────┘ │
│ (独立进程) │
└──────────────────────┘
HarmonyOS Qt架构:
┌──────────────────────────────┐
│ HarmonyOS Application │
│ ┌────────────────────────┐ │
│ │ ArkUI (鸿蒙UI框架) │ │
│ │ ┌──────────────────┐ │ │
│ │ │ XComponent │ │ │
│ │ │ ┌──────────┐ │ │ │
│ │ │ │ Qt QML │ │ │ │
│ │ │ │ (嵌入) │ │ │ │
│ │ │ └──────────┘ │ │ │
│ │ └──────────────────┘ │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
🚀 实战:创建第一个时钟应用
步骤1:创建项目结构
qtdemo/
├── entry/
│ └── src/
│ └── main/
│ └── cpp/
│ ├── main.cpp # 入口文件
│ ├── main.qml # UI界面
│ ├── ClockModel.h # C++时钟模型
│ ├── ClockModel.cpp
│ ├── qml.qrc # 资源文件
│ └── CMakeLists.txt # 构建配置
步骤2:编写C++时钟模型
ClockModel.h - 模型定义
#ifndef CLOCKMODEL_H
#define CLOCKMODEL_H
#include <QObject>
#include <QTimer>
#include <QDateTime>
#include <QString>
class ClockModel : public QObject
{
Q_OBJECT
// 定义QML可访问的属性
Q_PROPERTY(QString currentTime READ currentTime NOTIFY timeChanged)
Q_PROPERTY(QString currentDate READ currentDate NOTIFY dateChanged)
Q_PROPERTY(QString currentWeekDay READ currentWeekDay NOTIFY weekDayChanged)
Q_PROPERTY(bool is24HourFormat READ is24HourFormat WRITE setIs24HourFormat NOTIFY formatChanged)
public:
explicit ClockModel(QObject *parent = nullptr);
~ClockModel();
// 属性读取方法
QString currentTime() const { return m_currentTime; }
QString currentDate() const { return m_currentDate; }
QString currentWeekDay() const { return m_currentWeekDay; }
bool is24HourFormat() const { return m_is24HourFormat; }
// 属性设置方法
void setIs24HourFormat(bool format);
// QML可调用的方法
Q_INVOKABLE void toggleTimeFormat();
signals:
void timeChanged();
void dateChanged();
void weekDayChanged();
void formatChanged();
private slots:
void updateTime();
private:
void formatTime();
void formatDate();
void formatWeekDay();
QTimer *m_timer;
QString m_currentTime;
QString m_currentDate;
QString m_currentWeekDay;
bool m_is24HourFormat;
QDateTime m_dateTime;
};
#endif // CLOCKMODEL_H
关键知识点:
-
Q_OBJECT 宏
- 启用Qt元对象系统
- 支持信号槽机制
- 必须放在类定义的第一行
-
Q_PROPERTY 宏
- 将C++属性暴露给QML
READ- 读取方法WRITE- 写入方法(可选)NOTIFY- 属性变化信号
-
Q_INVOKABLE 宏
- 使C++方法可在QML中调用
- 支持参数传递和返回值
ClockModel.cpp - 模型实现
#include "ClockModel.h"
ClockModel::ClockModel(QObject *parent)
: QObject(parent)
, m_timer(new QTimer(this))
, m_is24HourFormat(true)
{
// 初始化时间
updateTime();
// 连接定时器信号
connect(m_timer, &QTimer::timeout, this, &ClockModel::updateTime);
// 启动定时器:每秒触发一次
m_timer->start(1000);
}
ClockModel::~ClockModel()
{
if (m_timer->isActive()) {
m_timer->stop();
}
}
void ClockModel::setIs24HourFormat(bool format)
{
if (m_is24HourFormat != format) {
m_is24HourFormat = format;
formatTime();
emit formatChanged();
}
}
void ClockModel::toggleTimeFormat()
{
setIs24HourFormat(!m_is24HourFormat);
}
void ClockModel::updateTime()
{
m_dateTime = QDateTime::currentDateTime();
formatTime();
formatDate();
formatWeekDay();
}
void ClockModel::formatTime()
{
QString newTime;
if (m_is24HourFormat) {
newTime = m_dateTime.toString("HH:mm:ss");
} else {
newTime = m_dateTime.toString("h:mm:ss AP");
}
if (m_currentTime != newTime) {
m_currentTime = newTime;
emit timeChanged(); // 通知QML更新
}
}
void ClockModel::formatDate()
{
QString newDate = m_dateTime.toString("yyyy年MM月dd日");
if (m_currentDate != newDate) {
m_currentDate = newDate;
emit dateChanged();
}
}
void ClockModel::formatWeekDay()
{
int dayOfWeek = m_dateTime.date().dayOfWeek();
static const QStringList weekDays = {
"星期一", "星期二", "星期三", "星期四",
"星期五", "星期六", "星期日"
};
QString newWeekDay = weekDays[dayOfWeek - 1];
if (m_currentWeekDay != newWeekDay) {
m_currentWeekDay = newWeekDay;
emit weekDayChanged();
}
}
关键知识点:
-
QTimer 使用
m_timer->start(1000); // 每1000ms触发一次 connect(m_timer, &QTimer::timeout, this, &ClockModel::updateTime); -
信号发送时机
if (m_currentTime != newTime) { // 只在值改变时发送 m_currentTime = newTime; emit timeChanged(); } -
QDateTime 格式化
"HH:mm:ss" // 24小时制:14:30:25 "h:mm:ss AP" // 12小时制:2:30:25 PM "yyyy年MM月dd日" // 日期:2025年11月08日
步骤3:编写鸿蒙专用入口
main.cpp - 鸿蒙版本
#include <QQmlApplicationEngine>
#include <QSurfaceFormat>
#include <QQmlContext>
#include "ClockModel.h"
// ⚠️ 关键:使用鸿蒙专用入口函数
extern "C" void qtmain()
{
// 配置 OpenGL ES 表面格式
QSurfaceFormat format;
format.setAlphaBufferSize(8);
format.setBlueBufferSize(8);
format.setGreenBufferSize(8);
format.setRedBufferSize(8);
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setRenderableType(QSurfaceFormat::OpenGLES);
format.setVersion(3, 0);
QSurfaceFormat::setDefaultFormat(format);
// ⚠️ 关键:使用static确保对象不被销毁
static QQmlApplicationEngine *engine = new QQmlApplicationEngine();
// 创建模型并注册到QML
static ClockModel clockModel;
engine->rootContext()->setContextProperty("clockModel", &clockModel);
// 加载QML文件
engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
}
对比:桌面Qt版本
// ❌ 桌面Qt - 不适用于鸿蒙
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
ClockModel clockModel; // 局部变量
engine.rootContext()->setContextProperty("clockModel", &clockModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
关键差异:
| 项目 | 桌面Qt | HarmonyOS Qt | 原因 |
|---|---|---|---|
| 入口函数 | int main(argc, argv) |
extern "C" void qtmain() |
鸿蒙动态库入口 |
| QGuiApplication | 必须创建 | 不需要 | 鸿蒙管理应用 |
| app.exec() | 必须调用 | 不需要 | 鸿蒙管理事件循环 |
| 对象生命周期 | 局部变量 | static |
防止函数返回后销毁 |
步骤4:编写QML界面
main.qml - 鸿蒙版本
// ⚠️ 关键:不导入 QtQuick.Window
import QtQuick 2.15
// ⚠️ 关键:使用 Rectangle 而不是 Window
Rectangle {
anchors.fill: parent // 填充XComponent
// 渐变背景
gradient: Gradient {
GradientStop { position: 0.0; color: "#0F2027" }
GradientStop { position: 0.5; color: "#203A43" }
GradientStop { position: 1.0; color: "#2C5364" }
}
Column {
anchors.centerIn: parent
spacing: 40
// 时钟显示 - 绑定到C++属性
Text {
text: clockModel.currentTime // 自动更新
font.pixelSize: 120
font.bold: true
color: "#FFFFFF"
anchors.horizontalCenter: parent.horizontalCenter
}
// 日期显示
Text {
text: clockModel.currentDate
font.pixelSize: 36
color: "#B0E0E6"
anchors.horizontalCenter: parent.horizontalCenter
}
// 星期显示
Text {
text: clockModel.currentWeekDay
font.pixelSize: 28
color: "#87CEEB"
anchors.horizontalCenter: parent.horizontalCenter
}
// 格式提示
Text {
text: clockModel.is24HourFormat ? "24小时制" : "12小时制"
font.pixelSize: 18
color: "#87CEEB"
anchors.horizontalCenter: parent.horizontalCenter
}
}
// 点击切换时间格式
MouseArea {
anchors.fill: parent
onClicked: {
clockModel.toggleTimeFormat() // 调用C++方法
}
}
}
对比:桌面Qt版本
// ❌ 桌面Qt - 不适用于鸿蒙
import QtQuick 2.15
import QtQuick.Window 2.15 // ❌ 鸿蒙不需要
Window { // ❌ 鸿蒙使用Rectangle
width: 800
height: 1200
visible: true
title: "时钟应用"
// ... 其他内容相同
}
关键差异:
| QML元素 | 桌面Qt | HarmonyOS Qt | 原因 |
|---|---|---|---|
| 根元素 | Window |
Rectangle |
Qt嵌入XComponent |
| 尺寸设置 | width/height |
anchors.fill: parent |
自适应容器 |
| visible | visible: true |
不需要 | 默认可见 |
| title | title: "xxx" |
不需要 | 鸿蒙管理标题 |
步骤5:配置构建文件
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(qtdemo VERSION 1.0 LANGUAGES C CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找Qt库
find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS
Concurrent
Gui
Network
Qml
Quick
)
# 添加库
add_library(entry SHARED
main.cpp
ClockModel.cpp
ClockModel.h
qml.qrc
)
# 链接Qt库
target_link_libraries(entry PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Qml
Qt${QT_VERSION_MAJOR}::Quick
)
qml.qrc - 资源文件
<?xml version="1.0" encoding="UTF-8"?>
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
🎓 核心概念详解
1. 为什么使用 extern "C" void qtmain()?
// C++ 名称修饰(Name Mangling)
void qtmain() // 编译后:_Z6qtmainv (C++修饰)
// extern "C" 禁用名称修饰
extern "C" void qtmain() // 编译后:qtmain (C风格)
原因: 鸿蒙通过dlsym查找函数,需要C风格的函数名。
2. 为什么对象要用 static?
void qtmain()
{
// ❌ 错误:局部变量
QQmlApplicationEngine engine; // 函数返回后销毁!
ClockModel model; // 函数返回后销毁!
// ✅ 正确:静态变量
static QQmlApplicationEngine *engine = new QQmlApplicationEngine();
static ClockModel model; // 永久存在
}
原因: qtmain()返回后,局部变量被销毁,但Qt引擎需要一直运行。
3. 属性绑定如何工作?
C++ 端 QML 端
──────── ──────
QString currentTime Text {
↓ text: clockModel.currentTime
emit timeChanged() → → }
(信号发送) (自动更新)
流程:
- C++属性值改变
emit timeChanged()发送信号- QML监听到信号
- QML调用
currentTime()读取新值 - UI自动更新
4. Q_PROPERTY 完整语法
Q_PROPERTY(Type name
READ getter // 读取方法(必须)
WRITE setter // 写入方法(可选)
NOTIFY signal // 变化信号(推荐)
RESET resetFunction // 重置方法(可选)
CONSTANT // 常量属性(可选)
STORED true/false // 是否存储(可选)
)
🐛 常见问题与解决
问题1:编译错误 “undefined reference to vtable”
原因: 类中有 Q_OBJECT 但没有运行 moc(Meta-Object Compiler)。
解决:
# CMakeLists.txt 中添加
set(CMAKE_AUTOMOC ON) # 自动运行 moc
问题2:应用启动后闪退
原因: 使用了 main() 而不是 qtmain()。
解决:
// ❌ 错误
int main(int argc, char *argv[]) { }
// ✅ 正确
extern "C" void qtmain() { }
问题3:QML显示白屏/黑屏
原因: 使用了 Window 作为根元素。
解决:
// ❌ 错误
import QtQuick.Window 2.15
Window { }
// ✅ 正确
import QtQuick 2.15
Rectangle {
anchors.fill: parent
}
问题4:QML中显示 “clockModel is not defined”
原因: 忘记在C++中注册对象。
解决:
engine->rootContext()->setContextProperty("clockModel", &clockModel);
✅ 完整开发流程总结
1. 创建C++模型类
├─ 继承 QObject
├─ 添加 Q_OBJECT 宏
├─ 使用 Q_PROPERTY 定义属性
└─ 使用 Q_INVOKABLE 定义方法
2. 在 qtmain() 中注册模型
├─ 创建 static 对象
├─ 使用 setContextProperty 注册
└─ 加载 QML 文件
3. 编写 QML 界面
├─ 使用 Rectangle 作为根元素
├─ 绑定 C++ 属性
└─ 调用 C++ 方法
4. 配置构建文件
├─ CMakeLists.txt
├─ qml.qrc
└─ 确保 AUTOMOC ON
5. 构建和运行
├─ Clean Project
├─ Rebuild Project
└─ Run
🚀 进阶学习路径
初级 → 中级
- ✅ 完成本文的时钟应用
- ⬜ 学习更多QML组件(ListView, GridView)
- ⬜ 学习Qt Quick Controls 2
- ⬜ 掌握QML动画
中级 → 高级
- ⬜ 自定义QML组件
- ⬜ 使用 QAbstractListModel
- ⬜ 多线程与异步操作
- ⬜ 网络请求与数据持久化
📚 参考资源
🎯 总结
通过本文,你应该掌握了:
- ✅ Qt for HarmonyOS的核心概念
- ✅
extern "C" void qtmain()的使用 - ✅ C++与QML的数据绑定
- ✅ QML在鸿蒙中的特殊要求
- ✅ 完整的开发流程
下一步:
- 运行示例代码,确保应用正常工作
- 尝试修改UI,熟悉QML语法
- 添加新功能(如秒表、倒计时)
祝你在Qt for HarmonyOS的开发旅程中顺利前行!💪
作者: 坚果派
日期: 2025-11-08
系列: Qt for HarmonyOS 实战教程
更多推荐



所有评论(0)