前言

欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/

项目开源地址:https://AtomGit.com/lqjmac/qt_yunbiantianqi

这篇文章记录第 5 个 Qt 适配鸿蒙小项目:云边天气
云边天气是一个静态天气卡片应用,用预设数据展示城市、温度、天气状态和基础指标。
项目定位是 天气卡片,核心功能是 展示城市、温度、天气状态
本文会从工程结构、Qt SDK 配置、QML 界面、入口函数、窗口缩放验证、安装运行和常见问题几个角度完整拆解。
为了便于继续扩展,文章中的命令、路径和 Bundle Name 都按实际项目填写。

提示:本文不是只展示界面截图,而是把一个可构建、可安装、可启动的 Qt for OpenHarmony 项目拆开讲清楚。

一、项目定位与学习目标

1.1 为什么选择这个项目

天气卡片适合演示信息卡片、城市切换、指标网格和响应式视觉层级,也能检验 QML 动态绑定在窗口变化时的表现。
从学习路径看,云边天气 的价值不在于功能复杂,而在于它足够小,适合集中验证 Qt Quick 在鸿蒙窗口里的运行方式。
做这种小项目时,我更关心三件事:能不能构建、能不能显示、窗口变化时能不能立即刷新。
这三件事都跑通以后,再继续堆业务功能才比较踏实。

1.2 本文会覆盖哪些知识点

主要学习点如下:

  • 展示城市
  • 展示温度
  • 展示天气状态
  • 切换预设城市
  • 展示湿度风力等指标
  • Qt SDK 使用相对路径配置
  • mainqtmain 双入口保留
  • HAP 构建、安装、启动命令
  • 最大化和还原时的窗口刷新检查

1.3 最终效果截图

在这里插入图片描述

图 1:云边天气 在 OpenHarmony 桌面环境中的运行效果。

1.4 推荐阅读顺序

如果你是第一次做 Qt 适配鸿蒙,可以按下面顺序读:

  1. 先看项目基本信息,确认目录和包名。
  2. 再看 Qt SDK 规则,避免 QT_PREFIX 写错。
  3. 然后看入口函数,理解为什么保留 mainqtmain
  4. 最后看构建、安装和窗口验证命令。

二、项目基本信息

2.1 信息总览

项目 内容
应用展示名 云边天气
项目目录名 QtWeatherCardDemo
Bundle Name com.nutpi.yunbiantianqi
项目类型 天气卡片
核心功能 展示城市、温度、天气状态
核心实现 天气 ListModel + 卡片式布局
界面风格 浅色天气卡片

这个表格建议直接写进 README 或项目清单中。
当项目数量逐渐增加时,目录名、中文名和 Bundle Name 的对应关系非常重要。

2.2 目录结构

qt_for_harmony/
├── qtforharmony/
├── QtWeatherCardDemo/
│   ├── AppScope/
│   ├── entry/
│   │   ├── build-profile.json5
│   │   └── src/main/
│   │       ├── cpp/
│   │       │   ├── CMakeLists.txt
│   │       │   ├── main.cpp
│   │       │   ├── main.qml
│   │       │   └── qml.qrc
│   │       └── ets/
│   └── README.md
└── qt-docs/

目录看起来不复杂,但每一层都有明确职责。
我建议不要把 Qt SDK 复制进每个项目,否则后续更新 SDK 会非常痛苦。

2.3 模块职责表

模块 职责 检查重点
AppScope 声明应用包名、图标、展示名 Bundle Name 是否唯一
entry/build-profile.json5 声明 native 构建入口和 Qt SDK 参数 QT_PREFIX 是否为相对路径
entry/src/main/cpp Qt C++ 与 QML 代码 main/qtmain 是否都存在
entry/src/main/ets Stage Ability 与 XComponent 容器 XComponent 是否占满窗口
README.md 项目说明和运行命令 SDK 仓库说明是否写明

三、环境准备

3.1 必要工具

建议准备下面这些工具:

  • DevEco Studio 或 OpenHarmony 命令行构建环境
  • hvigorw 构建工具
  • hdc 设备调试工具
  • Qt for OpenHarmony SDK
  • 一个 arm64-v8a 设备或模拟器

可以参考文末保留的 OpenHarmony 文档入口。

  • OpenHarmony 文档中心:https://docs.openharmony.cn/

3.2 Qt SDK 相对路径规则

本系列项目统一使用相对路径引用 Qt SDK。
也就是说,项目目录和 qtforharmony SDK 目录保持同级。

{
  "externalNativeOptions": {
    "path": "./src/main/cpp/CMakeLists.txt",
    "arguments": "-DQT_PREFIX=../qtforharmony",
    "abiFilters": ["arm64-v8a"]
  }
}

关键规则:项目需要 "arguments": "-DQT_PREFIX=../qtforharmony",SDK 文件请到文末保留的 SDK 仓库自取,并放到项目同级目录 qtforharmony 中。

3.3 为什么不用绝对路径

使用绝对路径时,项目只能在你自己的机器上构建。
换一台电脑、换一个用户目录、换一个 CI 环境,路径就会失效。
相对路径的好处是清晰、稳定、可迁移。

可以按下面步骤检查:

  1. 确认项目位于 qt_for_harmony 目录下。
  2. 确认 SDK 位于 qt_for_harmony/qtforharmony
  3. 确认 entry/build-profile.json5 里写的是 ../qtforharmony

四、工程配置拆解

4.1 CMakeLists.txt 的关键点

cmake_minimum_required(VERSION 3.5.0)
project(qtweathercarddemo)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../.." ABSOLUTE)
if (NOT IS_ABSOLUTE "${QT_PREFIX}")
    get_filename_component(QT_PREFIX "${PROJECT_ROOT}/${QT_PREFIX}" ABSOLUTE)
endif()

list(PREPEND CMAKE_PREFIX_PATH ${QT_PREFIX})
find_package(Qt5 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2)
add_library(entry SHARED main.cpp qml.qrc)
target_link_libraries(entry PRIVATE Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick)

这段 CMake 重点做了三件事。
第一,把相对的 QT_PREFIX 转成绝对路径。
第二,把 Qt SDK 加入 CMAKE_PREFIX_PATH
第三,把 C++ 入口和 qml.qrc 打进 libentry.so

4.2 构建参数检查表

配置项 推荐值 说明
path ./src/main/cpp/CMakeLists.txt native 构建入口
arguments -DQT_PREFIX=../qtforharmony Qt SDK 相对路径
abiFilters arm64-v8a 当前项目验证 ABI
copyCodeResource false 避免资源重复处理

4.3 qml.qrc 的作用

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
    </qresource>
</RCC>

QML 文件被打入 Qt 资源系统后,C++ 侧可以通过 qrc:/main.qml 加载。
这能减少路径依赖,也能避免运行时找不到 QML 文件。

五、入口函数设计

5.1 为什么保留 main 和 qtmain

在当前适配方案里,libentry.so 需要兼容不同 QPA 启动行为。
如果只导出 qtmain,某些版本可能会报 Symbol not found: main
如果只保留 main,又可能不符合已有 QPA 调用链。
所以当前项目统一保留两个入口。

extern "C" void qtmain()
{
    static ResponsiveQuickView *view = nullptr;
    if (view != nullptr) {
        return;
    }

    configureSurfaceFormat();
    view = new ResponsiveQuickView();
    loadQml(view);
}

int main(int argc, char *argv[])
{
    configureSurfaceFormat();
    QGuiApplication app(argc, argv);
    ResponsiveQuickView view;
    if (!loadQml(&view)) {
        return -1;
    }
    return app.exec();
}

5.2 启动流程

启动链路可以理解为:

  1. HarmonyOS Stage Ability 创建 XComponent 容器。
  2. QPA 插件加载 libentry.so
  3. mainqtmain 初始化 Qt 应用环境。
  4. C++ 侧加载 qrc:/main.qml
  5. QML 渲染到 XComponent 对应的原生节点上。

5.3 XComponent 容器示意

import { NodeController, FrameNode } from '@kit.ArkUI';

class QtNodeController extends NodeController {
  makeNode(uiContext: UIContext): FrameNode | null {
    return this.node;
  }
}

@Entry
@Component
struct Index {
  build() {
    XComponent({ id: 'qt_xcomponent', type: XComponentType.NODE })
      .width('100%')
      .height('100%')
  }
}

这段 ArkTS 只是示意。
真正项目里还需要配合 Qt QPA 插件完成 native node 绑定。

六、QML 界面实现

6.1 根节点尺寸策略

Rectangle {
    id: root
    width: 880
    height: 1100
    color: "#F4F6FB"

    property real pageMargin: Math.max(24, Math.min(56, width * 0.045))
    property real bodySize: Math.max(18, Math.min(24, width * 0.025))

    Column {
        anchors.fill: parent
        anchors.margins: root.pageMargin
        spacing: 16
    }
}

这里的关键是不要把布局写死成一个固定手机尺寸。
宽度、间距和字号都可以用根节点尺寸推导。
这样最大化、还原和不同分辨率设备上都更稳。

6.2 当前项目的核心 QML

ListModel {
    id: weatherModel
    ListElement {
        city: "深圳"
        state: "多云"
        temp: "27"
        humidity: "68%"
        wind: "东南风 3 级"
    }
}

这段代码体现了 云边天气 的核心功能:展示城市、温度、天气状态。
真实项目里可以继续拆成多个 QML 组件,但演示项目保持单文件更利于阅读。

6.3 UI 层级表

层级 作用 设计建议
根 Rectangle 承载背景和全局尺寸 颜色和尺寸统一从这里派生
页面 Column/Row 控制整体排版 避免控件互相覆盖
功能卡片 展示核心信息 半径不超过 8px,保持工具感
按钮区域 触发业务动作 用明确文案和稳定尺寸
列表或指标区 承载动态数据 设置 clip,避免越界

七、核心逻辑拆解

7.1 数据模型

function weather() {
    return weatherModel.get(selectedCity)
}

Text { text: weather().city }
Text { text: weather().temp + "°" }
Text { text: weather().state }

这个模型只做最小必要的数据组织。
演示项目不建议一上来引入数据库、网络层或复杂状态管理。
先把交互链路跑通,再逐步增强。

7.2 业务动作

Repeater {
    model: weatherModel
    Rectangle {
        color: index === selectedCity ? "#214F5C" : "#FFFFFF"
        MouseArea { anchors.fill: parent; onClicked: selectedCity = index }
    }
}

业务动作保持短小有一个好处:问题发生时更容易定位。
比如按钮点击无响应时,可以直接确认是 MouseArea、函数调用还是模型更新的问题。

7.3 交互验证步骤

建议每次改完 QML 后都做下面这组验证:

  1. 启动应用,确认首屏不白屏。
  2. 点击主要按钮,确认状态变化。
  3. 最大化窗口,确认没有黑边。
  4. 还原窗口,确认内容立刻刷新。
  5. 查看日志,确认没有 QML 加载错误。

八、窗口缩放适配

8.1 曾经遇到的问题

在 Qt 适配鸿蒙时,窗口最大化或还原后,XComponent 尺寸可能已经变化,但 Qt 场景没有立即重算。
表现出来就是右侧出现黑边,或者必须点击一次界面才刷新。

注意:不要用 XComponent 的尺寸反向调用 QWindow::resize() 做闭环同步,这很容易造成窗口不断缩小,极端情况下会缩成一个点。

8.2 推荐的刷新思路

class ResponsiveQuickView : public QQuickView {
public:
    explicit ResponsiveQuickView(QWindow *parent = nullptr)
        : QQuickView(parent)
    {
        setResizeMode(QQuickView::SizeRootObjectToView);
        connect(this, &QWindow::widthChanged, this, [this](int) { refreshRootLayout(); });
        connect(this, &QWindow::heightChanged, this, [this](int) { refreshRootLayout(); });
    }

private:
    void refreshRootLayout()
    {
        if (rootObject() != nullptr) {
            rootObject()->setSize(QSizeF(size()));
            rootObject()->polish();
            rootObject()->update();
        }
        requestUpdate();
    }
};

这个思路的核心是只刷新 QML 根对象和 scene graph。
它不会拿原生节点尺寸反向 resize Qt 窗口,因此不会形成尺寸反馈环。

8.3 验证命令

hdc shell aa start -a EntryAbility -b com.nutpi.yunbiantianqi -m entry
sleep 2
hdc shell snapshot_display -f /data/local/tmp/yunbian_screen.jpeg
hdc file recv /data/local/tmp/yunbian_screen.jpeg /tmp/yunbian_screen.jpeg
hdc shell uitest dumpLayout -p /data/local/tmp/layout_yunbian.json -d 0

如果截图正常、布局树里的 XComponent bounds 与窗口内部区域一致,基本可以确认窗口刷新链路是通的。

九、构建、安装与运行

9.1 构建 HAP

cd /new/ugc/qt_for_harmony/QtWeatherCardDemo
/Users/luqingjiedemac/ohos/command-line-tools/bin/hvigorw assembleHap --stacktrace

构建成功后会看到 BUILD SUCCESSFUL
HAP 默认输出路径如下。

entry/build/default/outputs/default/entry-default-unsigned.hap

9.2 安装并启动

hdc install -r entry/build/default/outputs/default/entry-default-unsigned.hap
hdc shell aa start -a EntryAbility -b com.nutpi.yunbiantianqi -m entry
hdc shell pidof com.nutpi.yunbiantianqi

如果 pidof 能返回进程号,就说明应用已经启动。
接着再看截图,确认 QML 是否渲染正常。

9.3 日志观察

hdc shell hilog -r
hdc shell aa start -a EntryAbility -b com.example.demo -m entry
sleep 2
hdc shell hilog -x | tail -n 120

常见非阻塞日志包括 NAPI 验证提示、签名配置提示、图形栈内部符号提示。
真正需要优先处理的是 QML 加载失败、入口符号缺失、动态库找不到。

十、常见问题与排查

10.1 问题表

问题 可能原因 排查方式
白屏 QML 未加载或入口函数不匹配 检查 hilog 和 qrc 路径
找不到 main 只导出了 qtmain 同时保留 main 和 qtmain
构建失败 QT_PREFIX 不正确 检查 …/qtforharmony 是否存在
窗口黑边 根对象未随窗口刷新 检查 QQuickView resize 策略
按钮无响应 MouseArea 层级或 enabled 状态错误 用最小按钮复现
文字溢出 字号和容器不匹配 使用 elide 或 wrapMode

10.2 推荐排查顺序

遇到问题时不要同时改很多地方。
建议按下面顺序处理:

  1. 先确认 HAP 能构建成功。
  2. 再确认应用能被 aa start 拉起。
  3. 然后检查 hilog 是否有入口符号问题。
  4. 最后看 QML 布局、交互和窗口缩放。

经验:构建问题、启动问题、渲染问题要分开排查。混在一起改,最后很难判断是哪一步修好的。

十一、质量自检

11.1 运行验证矩阵

验证项 预期结果 当前项目建议
普通启动 显示首屏 看到 云边天气 主界面
主要交互 功能状态变化 展示城市、温度、天气状态
最大化 界面铺满窗口 无黑边,无延迟刷新
还原 尺寸恢复正常 不缩成点,不等待点击刷新
日志 无入口错误 不出现 Symbol not found: main

11.2 发布前检查清单

发布项目或文章前,我建议至少检查这些内容:

  • README 是否写明 SDK 仓库和 QT_PREFIX 规则
  • Bundle Name 是否和其他项目冲突
  • 应用展示名是否已经产品化
  • HAP 是否能重新构建
  • 截图是否来自实际运行效果
  • 最大化和还原是否已经验证

十二、可以继续扩展什么

12.1 功能增强方向

围绕 云边天气,后续可以做这些增强:

  • 把“展示城市”做成更完整的产品级体验
  • 把“展示温度”做成更完整的产品级体验
  • 把“展示天气状态”做成更完整的产品级体验
  • 把“切换预设城市”做成更完整的产品级体验
  • 把“展示湿度风力等指标”做成更完整的产品级体验
  • 增加本地持久化能力
  • 增加主题切换
  • 增加更多输入校验和异常提示

12.2 工程增强方向

工程层面可以按下面路线演进:

  1. 把通用的 ResponsiveQuickView 抽成公共模板。
  2. 把 README 的 SDK 规则变成统一脚本检查。
  3. 把截图验证命令纳入日常验收流程。
  4. 把项目列表和 Bundle Name 放进表格统一维护。

十三、完整命令速查

13.1 从构建到启动

cd /new/ugc/qt_for_harmony/QtWeatherCardDemo
/Users/luqingjiedemac/ohos/command-line-tools/bin/hvigorw assembleHap --stacktrace
hdc install -r entry/build/default/outputs/default/entry-default-unsigned.hap
hdc shell aa start -a EntryAbility -b com.nutpi.yunbiantianqi -m entry

13.2 截图和布局树

hdc shell snapshot_display -f /data/local/tmp/yunbian_screen.jpeg
hdc file recv /data/local/tmp/yunbian_screen.jpeg /tmp/yunbian_screen.jpeg
hdc shell uitest dumpLayout -p /data/local/tmp/layout_yunbian.json -d 0
hdc file recv /data/local/tmp/layout_yunbian.json /tmp/layout_yunbian.json

13.3 清理和重建

rm -rf entry/build entry/.cxx .hvigor
/Users/luqingjiedemac/ohos/command-line-tools/bin/hvigorw assembleHap --stacktrace

十四、保留资源入口

14.1 OpenHarmony 文档

OpenHarmony 文档中心:docs.openharmony.cn

这个入口用于继续查询 Ability、XComponent、设备调试和系统能力相关资料。

14.2 Qt for Harmony SDK

Qt for Harmony SDK 仓库:https://AtomGit.com/nutpi/qtforharmony_sdk

这个入口用于获取本文项目依赖的 Qt 适配鸿蒙 SDK 文件。

14.3 为什么只保留这两个正文资源

技术文章不一定需要堆很多链接。
这五篇文章保留社区入口、OpenHarmony 文档和 SDK 仓库三个关键链接,读者路径更短,也更适合复制到 CSDN 发布。

总结

这篇文章从工程、配置、入口、QML、构建、安装和验证几个角度拆解了 云边天气
它不是一个复杂应用,但已经覆盖 Qt 适配鸿蒙项目最关键的闭环:能构建、能启动、能显示、能随窗口尺寸刷新
下一篇可以继续扩展新的业务类型,把同一套 Qt for OpenHarmony 工程规则复制到更多产品化小应用里。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐