前言

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

项目开源地址:https://atomgit.com/lqjmac/qtmqd

如果你正在把 Qt Quick 放到鸿蒙窗口里跑,最容易踩坑的往往不是控件,而是构建参数、入口符号和窗口刷新。
本文记录第 24 个 Qt 适配鸿蒙小项目:买清单
它的定位是 购物清单,核心功能是 添加商品、分类筛选、勾选已购买、清理已购买
购物清单强调“快”:能看见还缺什么,能一键勾掉,能按分类缩小范围。
买清单 的工程价值不是功能复杂,而是把 Qt Quick、OpenHarmony HAP、QML 资源和窗口刷新串成一条可复现路径。

提示:本文按真实工程路径和真实 Bundle Name 编写,命令可以直接对照项目目录执行。

推荐阅读顺序:

  1. 确认项目目录、中文展示名和 Bundle Name。
  2. 检查 Qt SDK 相对路径,避免构建机路径绑定。
  3. 运行构建、安装、启动和日志过滤命令。

本文包含的重点元素:

  • 项目截图
  • 配置表格
  • 构建命令
  • QML 片段
  • 常见问题表

一、应用目标

1.1 项目解决什么问题

买清单 不是为了把 购物清单 做到复杂,而是为了验证 Qt Quick 在鸿蒙桌面窗口里的完整链路。
这个链路包含资源配置、Native CMake、QML 首屏、双入口导出、窗口自适应、HAP 构建和设备启动。

主要功能如下:

  • 展示本地购物清单
  • 按分类筛选
  • 切换待买和已买状态
  • 添加预设商品
  • 清理已购买商品
  • 统计数量

1.2 最终运行截图

在这里插入图片描述

图 1:买清单 在 OpenHarmony 桌面环境里的运行效果。

1.3 关注点速览

关注点 为什么重要 在本项目中的体现
应用标识 避免 25 个 Demo 安装冲突 com.nutpi.maiqingdan
QML 状态 决定界面是否能即时反馈 ListModel 保存商品,done 字段驱动统计和列表样式
构建参数 决定换机器后能否继续构建 -DQT_PREFIX=../qtforharmony
启动入口 避免白屏和符号缺失 同时保留 mainqtmain

二、目录和包名

2.1 信息总览

项目 内容
应用展示名 买清单
项目目录名 QtShoppingListDemo
Bundle Name com.nutpi.maiqingdan
CMake project maiqingdan_shoppinglist
项目类型 购物清单
核心功能 添加商品、分类筛选、勾选已购买、清理已购买

这张表建议和 README 中的信息保持一致。项目多了以后,最怕的是展示名、包名和目录名互相错位。

2.2 目录结构

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

这里有一个朴素但重要的规则:Qt SDK 放在项目同级目录,不塞进每个 Demo 里面。

2.3 模块职责

模块 作用 检查点
AppScope 应用包名和展示名 com.nutpi.maiqingdan 是否唯一
entry/build-profile.json5 native 构建参数 Qt SDK 相对路径
entry/src/main/cpp C++ 入口和 QML main.qml 是否进入 qrc
README.md 项目说明 构建和启动命令是否完整

三、Qt SDK 相对路径

3.1 build-profile.json5

项目需要使用相对路径指定 Qt SDK。当前工程使用的关键片段如下:

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

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

3.2 CMakeLists.txt

cmake_minimum_required(VERSION 3.5.0)
project(maiqingdan_shoppinglist)

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)

这段配置先把相对的 QT_PREFIX 转成绝对路径,再交给 CMake 查找 Qt。

3.3 qml.qrc

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

把 QML 打进 Qt 资源系统后,运行时可以统一通过 qrc:/main.qml 加载。

四、QML 首屏组织

4.1 首屏布局

顶部剩余数量和分类切换,列表居中,右侧放快捷操作和采购概览。
这种布局不是为了炫技,而是为了让 Demo 的功能一眼就能被看懂。

4.2 根节点尺寸策略

Rectangle {
    id: root
    width: 960
    height: 1080
    color: "#F5F6FA"

    property real margin: Math.max(20, Math.min(48, width * 0.042))
    property real gap: Math.max(12, Math.min(24, width * 0.022))
    property real titleSize: Math.max(34, Math.min(58, width * 0.054))
}

根节点给出默认尺寸,真正运行时由 QQuickView::SizeRootObjectToView 接管。

4.3 本项目的数据模型

ListModel {
    id: demoModel
    ListElement { name: "示例项一"; state: "ready" }
    ListElement { name: "示例项二"; state: "active" }
    ListElement { name: "示例项三"; state: "done" }
}

实际工程中,买清单 使用的是 itemModel / categoryModel。这些数据都在 QML 内部,适合单机演示。

五、交互逻辑拆解

5.1 函数职责表

函数 职责
itemVisible(category) 封装业务逻辑
boughtCount() 计算界面统计值
uncheckedCount() 计算界面统计值
addPreset() 恢复或清理状态
clearBought() 恢复或清理状态

这些函数没有故意抽得很复杂。Demo 项目最重要的是让读者能从 QML 文件里直接看懂状态从哪里来、又流向哪里。

5.2 典型交互片段

MouseArea {
    anchors.fill: parent
    onClicked: {
        // 点击后修改模型状态,界面绑定会自动刷新
        demoModel.setProperty(index, "state", "done")
    }
}

点击事件只负责改变状态,展示层通过绑定更新,这比手动到处刷新文本更稳。

5.3 业务函数示意

function demoFlow(row) {
    var current = row
    var before = demoModel.count
    demoModel.setProperty(row, "state", "done")
    return before
}

这段不是要照搬,而是说明当前项目的函数组织方式:

  • 先从模型拿当前数据
  • 再执行用户动作
  • 最后让绑定表达式刷新界面

六、C++ 入口保持稳定

6.1 保留双入口

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();
}

之前遇到过只导出 qtmain 时运行时报 Symbol not found: main 的情况,所以这一批 Qt Demo 都统一保留两个入口。

6.2 标题和 QML 加载

bool loadQml(ResponsiveQuickView *view)
{
    view->setTitle(QStringLiteral("买清单"));
    view->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
    if (view->status() == QQuickView::Error) {
        qWarning() << "Failed to load qrc:/main.qml";
        return false;
    }
    view->showResponsive();
    return true;
}

这里标题要和应用展示名保持一致。运行时如果日志出现 QML 加载失败,优先检查 qml.qrc

七、命令行跑通

7.1 构建 HAP

cd QtShoppingListDemo
/Users/luqingjiedemac/ohos/command-line-tools/bin/hvigorw assembleHap --stacktrace

构建完成后,HAP 默认输出在下面这个路径:

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

7.2 安装和启动

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

启动命令里的 Bundle Name 必须和 AppScope/app.json5 一致。

7.3 构建失败先看哪里

  1. QT_PREFIX 是否指向同级 qtforharmony
  2. CMakeLists.txt 的 project 名称是否已经替换。
  3. main.qml 是否存在于 qml.qrc 中。

八、窗口缩放验证

8.1 过滤关键日志

hdc shell hilog -x -z 500 | rg -n "QtForOpenHarmony|QPAForOpenHarmony|maiqingdan|Failed|failed|Error|error|qrc|main function|Symbol not found|QML|ReferenceError|TypeError"

我重点看这些信号:

  • 是否出现 Symbol not found: main
  • 是否出现 Failed to load qrc:/main.qml
  • 是否出现 QML 的 ReferenceErrorTypeError
  • 是否能看到应用进程还在运行

8.2 截图命令

hdc shell snapshot_display -f /data/local/tmp/maiqingdan_screen.jpeg
hdc file recv /data/local/tmp/maiqingdan_screen.jpeg ./qt-docs/assets/maiqingdan-screen.jpeg

截图不是装饰,它是确认首屏真的渲染出来的证据。

8.3 当前项目验证点

  1. 勾选后统计变化
  2. 分类筛选隐藏其他项
  3. 清理已购买会删除对应项

经验:这类小项目最适合把功能做窄,把构建、启动、截图、日志验证做完整。

九、问题表

9.1 常见问题表

现象 可能原因 处理方式
白屏 QML 没加载成功或窗口未刷新 检查 qrc:/main.qml 和日志
找不到 main 只保留了 qtmain 同时导出 mainqtmain
构建找不到 Qt QT_PREFIX 路径错误 使用 -DQT_PREFIX=../qtforharmony
启动失败 Bundle Name 写错 对照 com.nutpi.maiqingdan
窗口缩放不刷新 根对象没有跟随窗口尺寸 使用响应式 QQuickView 刷新策略

9.2 排查顺序

  1. 先看 HAP 是否重新构建。
  2. 再看安装的包名是不是当前项目。
  3. 然后看日志里有没有 QML 或 main 符号错误。
  4. 最后才去调整具体控件布局。

十、下一步迭代

10.1 可以继续做的功能

  • 增加手动输入商品
  • 保存常买清单
  • 加入排序和搜索

这些扩展不建议一口气全做。Qt 适配鸿蒙的早期样例,优先目标应该是结构稳定、运行稳定、验证路径稳定。

10.2 工程增强方向

QtObject {
    id: appState
    property int currentIndex: 0
    property bool dirty: false
    property string message: "ready"
}

当页面状态继续变多,可以把零散属性收进一个 QtObject,这样顶层不会越来越乱。

10.3 清理和重建

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

遇到缓存比较顽固的问题时,可以清掉构建产物后重建。正常开发时不需要每次都清理。

十一、相关入口

11.1 OpenHarmony 文档入口

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

11.2 Qt for Harmony SDK 仓库

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

11.3 为什么只保留三个链接

这批文章用于项目发布和读者复现。链接过多会分散注意力,所以正文只保留社区入口、OpenHarmony 文档和 Qt SDK 仓库三个入口。

总结

买清单 这个 Demo 的重点是 添加商品、分类筛选、勾选已购买、清理已购买
从工程角度看,它已经覆盖了 Qt for OpenHarmony 项目里最关键的几步:配置 Qt SDK、打包 QML、保留双入口、构建 HAP、安装启动和截图验证。
如果你也在做 Qt 适配鸿蒙,可以先用这种小项目把链路跑稳,再逐步接入更真实的数据和业务。

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

Logo

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

更多推荐