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

关键知识点:

  1. Q_OBJECT 宏

    • 启用Qt元对象系统
    • 支持信号槽机制
    • 必须放在类定义的第一行
  2. Q_PROPERTY 宏

    • 将C++属性暴露给QML
    • READ - 读取方法
    • WRITE - 写入方法(可选)
    • NOTIFY - 属性变化信号
  3. 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();
    }
}

关键知识点:

  1. QTimer 使用

    m_timer->start(1000);  // 每1000ms触发一次
    connect(m_timer, &QTimer::timeout, this, &ClockModel::updateTime);
    
  2. 信号发送时机

    if (m_currentTime != newTime) {  // 只在值改变时发送
        m_currentTime = newTime;
        emit timeChanged();
    }
    
  3. 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()    →    →   }
(信号发送)              (自动更新)

流程:

  1. C++属性值改变
  2. emit timeChanged() 发送信号
  3. QML监听到信号
  4. QML调用 currentTime() 读取新值
  5. 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
  • ⬜ 多线程与异步操作
  • ⬜ 网络请求与数据持久化

📚 参考资源


🎯 总结

通过本文,你应该掌握了:

  1. ✅ Qt for HarmonyOS的核心概念
  2. extern "C" void qtmain() 的使用
  3. ✅ C++与QML的数据绑定
  4. ✅ QML在鸿蒙中的特殊要求
  5. ✅ 完整的开发流程

下一步:

  • 运行示例代码,确保应用正常工作
  • 尝试修改UI,熟悉QML语法
  • 添加新功能(如秒表、倒计时)

祝你在Qt for HarmonyOS的开发旅程中顺利前行!💪


作者: 坚果派
日期: 2025-11-08
系列: Qt for HarmonyOS 实战教程

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐