前言欢迎加入鸿蒙PC开发者社区共同打造开发者工具生态鸿蒙PC开发者社区 https://harmonypc.csdn.net/项目开源地址https://atomgit.com/lqjmac/qtld这篇不是从概念开始而是从一个已经能安装、能启动、能截图的 Qt for OpenHarmony 小项目开始复盘。本文记录第 18 个 Qt 适配鸿蒙小项目乐单。它的定位是音乐清单核心功能是展示歌曲列表和播放状态 UI。音乐类示例先做 UI 状态就够了当前曲目、播放暂停、进度条和歌曲列表。乐单 的工程价值不是功能复杂而是把 Qt Quick、OpenHarmony HAP、QML 资源和窗口刷新串成一条可复现路径。提示本文按真实工程路径和真实 Bundle Name 编写命令可以直接对照项目目录执行。推荐阅读顺序确认项目目录、中文展示名和 Bundle Name。检查 Qt SDK 相对路径避免构建机路径绑定。运行构建、安装、启动和日志过滤命令。本文包含的重点元素项目截图配置表格构建命令QML 片段常见问题表一、先看成品定位1.1 项目解决什么问题乐单不是为了把 音乐清单 做到复杂而是为了验证 Qt Quick 在鸿蒙桌面窗口里的完整链路。这个链路包含资源配置、Native CMake、QML 首屏、双入口导出、窗口自适应、HAP 构建和设备启动。主要功能如下展示歌曲清单点击歌曲切换当前播放项播放暂停状态切换上一首下一首切换展示进度歌手时长和标签1.2 最终运行截图图 1乐单 在 OpenHarmony 桌面环境里的运行效果。1.3 关注点速览关注点为什么重要在本项目中的体现应用标识避免 25 个 Demo 安装冲突com.nutpi.yuedanQML 状态决定界面是否能即时反馈用 currentIndex 和 playing 组合出播放状态不依赖真实音频解码构建参数决定换机器后能否继续构建-DQT_PREFIX../qtforharmony启动入口避免白屏和符号缺失同时保留main和qtmain二、工程骨架和配置2.1 信息总览项目内容应用展示名乐单项目目录名QtMusicListDemoBundle Namecom.nutpi.yuedanCMake projectyuedan_musiclist项目类型音乐清单核心功能展示歌曲列表和播放状态 UI这张表建议和 README 中的信息保持一致。项目多了以后最怕的是展示名、包名和目录名互相错位。2.2 目录结构qt_for_harmony/ ├── qtforharmony/ ├── QtMusicListDemo/ │ ├── 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.yuedan是否唯一entry/build-profile.json5native 构建参数Qt SDK 相对路径entry/src/main/cppC 入口和 QMLmain.qml是否进入 qrcREADME.md项目说明构建和启动命令是否完整三、界面结构怎么拆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../qtforharmonySDK 文件请到文末保留的 SDK 仓库自取并放到项目同级目录qtforharmony中。3.2 CMakeLists.txtcmake_minimum_required(VERSION 3.5.0) project(yuedan_musiclist) 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.qrcRCCqresourceprefix/filemain.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 } }实际工程中乐单 使用的是songModel。这些数据都在 QML 内部适合单机演示。五、入口函数与窗口刷新5.1 函数职责表函数职责currentSong()切换当前展示内容nextSong()切换当前展示内容previousSong()切换当前展示内容progressRatio()计算界面统计值playStateText()封装业务逻辑这些函数没有故意抽得很复杂。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 }这段不是要照搬而是说明当前项目的函数组织方式先从模型拿当前数据再执行用户动作最后让绑定表达式刷新界面六、构建安装流程6.1 保留双入口externCvoidqtmain(){staticResponsiveQuickView*viewnullptr;if(view!nullptr){return;}configureSurfaceFormat();viewnewResponsiveQuickView();loadQml(view);}intmain(intargc,char*argv[]){configureSurfaceFormat();QGuiApplicationapp(argc,argv);ResponsiveQuickView view;if(!loadQml(view)){return-1;}returnapp.exec();}之前遇到过只导出qtmain时运行时报Symbol not found: main的情况所以这一批 Qt Demo 都统一保留两个入口。6.2 标题和 QML 加载boolloadQml(ResponsiveQuickView*view){view-setTitle(QStringLiteral(乐单));view-setSource(QUrl(QStringLiteral(qrc:/main.qml)));if(view-status()QQuickView::Error){qWarning()Failed to load qrc:/main.qml;returnfalse;}view-showResponsive();returntrue;}这里标题要和应用展示名保持一致。运行时如果日志出现 QML 加载失败优先检查qml.qrc。七、日志和窗口验证7.1 构建 HAPcdQtMusicListDemo /Users/luqingjiedemac/ohos/command-line-tools/bin/hvigorw assembleHap--stacktrace构建完成后HAP 默认输出在下面这个路径entry/build/default/outputs/default/entry-default-unsigned.hap7.2 安装和启动hdcinstall-rentry/build/default/outputs/default/entry-default-unsigned.hap hdc shell aa start-aEntryAbility-bcom.nutpi.yuedan-mentry启动命令里的 Bundle Name 必须和AppScope/app.json5一致。7.3 构建失败先看哪里QT_PREFIX是否指向同级qtforharmony。CMakeLists.txt的 project 名称是否已经替换。main.qml是否存在于qml.qrc中。八、常见问题8.1 过滤关键日志hdc shell hilog-x-z500|rg-nQtForOpenHarmony|QPAForOpenHarmony|yuedan|Failed|failed|Error|error|qrc|main function|Symbol not found|QML|ReferenceError|TypeError我重点看这些信号是否出现Symbol not found: main是否出现Failed to load qrc:/main.qml是否出现 QML 的ReferenceError或TypeError是否能看到应用进程还在运行8.2 截图命令hdc shell snapshot_display-f/data/local/tmp/yuedan_screen.jpeg hdcfilerecv /data/local/tmp/yuedan_screen.jpeg ./qt-docs/assets/yuedan-screen.jpeg截图不是装饰它是确认首屏真的渲染出来的证据。8.3 当前项目验证点点击歌曲后标题变化播放按钮文案变化上一首下一首循环正常经验这类小项目最适合把功能做窄把构建、启动、截图、日志验证做完整。九、发布前自检9.1 常见问题表现象可能原因处理方式白屏QML 没加载成功或窗口未刷新检查qrc:/main.qml和日志找不到 main只保留了qtmain同时导出main和qtmain构建找不到 QtQT_PREFIX路径错误使用-DQT_PREFIX../qtforharmony启动失败Bundle Name 写错对照com.nutpi.yuedan窗口缩放不刷新根对象没有跟随窗口尺寸使用响应式QQuickView刷新策略9.2 排查顺序先看 HAP 是否重新构建。再看安装的包名是不是当前项目。然后看日志里有没有 QML 或 main 符号错误。最后才去调整具体控件布局。十、后续扩展10.1 可以继续做的功能接入本地音频文件加入播放队列增加歌词面板这些扩展不建议一口气全做。Qt 适配鸿蒙的早期样例优先目标应该是结构稳定、运行稳定、验证路径稳定。10.2 工程增强方向QtObject { id: appState property int currentIndex: 0 property bool dirty: false property string message: ready }当页面状态继续变多可以把零散属性收进一个QtObject这样顶层不会越来越乱。10.3 清理和重建rm-rfentry/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_sdk11.3 为什么只保留三个链接这批文章用于项目发布和读者复现。链接过多会分散注意力所以正文只保留社区入口、OpenHarmony 文档和 Qt SDK 仓库三个入口。总结乐单 这个 Demo 的重点是展示歌曲列表和播放状态 UI。从工程角度看它已经覆盖了 Qt for OpenHarmony 项目里最关键的几步配置 Qt SDK、打包 QML、保留双入口、构建 HAP、安装启动和截图验证。如果你也在做 Qt 适配鸿蒙可以先用这种小项目把链路跑稳再逐步接入更真实的数据和业务。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力
鸿蒙PC:Qt适配OpenHarmony实战【乐单】:不接音频引擎,也能先把播放清单 UI 跑起来
前言欢迎加入鸿蒙PC开发者社区共同打造开发者工具生态鸿蒙PC开发者社区 https://harmonypc.csdn.net/项目开源地址https://atomgit.com/lqjmac/qtld这篇不是从概念开始而是从一个已经能安装、能启动、能截图的 Qt for OpenHarmony 小项目开始复盘。本文记录第 18 个 Qt 适配鸿蒙小项目乐单。它的定位是音乐清单核心功能是展示歌曲列表和播放状态 UI。音乐类示例先做 UI 状态就够了当前曲目、播放暂停、进度条和歌曲列表。乐单 的工程价值不是功能复杂而是把 Qt Quick、OpenHarmony HAP、QML 资源和窗口刷新串成一条可复现路径。提示本文按真实工程路径和真实 Bundle Name 编写命令可以直接对照项目目录执行。推荐阅读顺序确认项目目录、中文展示名和 Bundle Name。检查 Qt SDK 相对路径避免构建机路径绑定。运行构建、安装、启动和日志过滤命令。本文包含的重点元素项目截图配置表格构建命令QML 片段常见问题表一、先看成品定位1.1 项目解决什么问题乐单不是为了把 音乐清单 做到复杂而是为了验证 Qt Quick 在鸿蒙桌面窗口里的完整链路。这个链路包含资源配置、Native CMake、QML 首屏、双入口导出、窗口自适应、HAP 构建和设备启动。主要功能如下展示歌曲清单点击歌曲切换当前播放项播放暂停状态切换上一首下一首切换展示进度歌手时长和标签1.2 最终运行截图图 1乐单 在 OpenHarmony 桌面环境里的运行效果。1.3 关注点速览关注点为什么重要在本项目中的体现应用标识避免 25 个 Demo 安装冲突com.nutpi.yuedanQML 状态决定界面是否能即时反馈用 currentIndex 和 playing 组合出播放状态不依赖真实音频解码构建参数决定换机器后能否继续构建-DQT_PREFIX../qtforharmony启动入口避免白屏和符号缺失同时保留main和qtmain二、工程骨架和配置2.1 信息总览项目内容应用展示名乐单项目目录名QtMusicListDemoBundle Namecom.nutpi.yuedanCMake projectyuedan_musiclist项目类型音乐清单核心功能展示歌曲列表和播放状态 UI这张表建议和 README 中的信息保持一致。项目多了以后最怕的是展示名、包名和目录名互相错位。2.2 目录结构qt_for_harmony/ ├── qtforharmony/ ├── QtMusicListDemo/ │ ├── 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.yuedan是否唯一entry/build-profile.json5native 构建参数Qt SDK 相对路径entry/src/main/cppC 入口和 QMLmain.qml是否进入 qrcREADME.md项目说明构建和启动命令是否完整三、界面结构怎么拆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../qtforharmonySDK 文件请到文末保留的 SDK 仓库自取并放到项目同级目录qtforharmony中。3.2 CMakeLists.txtcmake_minimum_required(VERSION 3.5.0) project(yuedan_musiclist) 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.qrcRCCqresourceprefix/filemain.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 } }实际工程中乐单 使用的是songModel。这些数据都在 QML 内部适合单机演示。五、入口函数与窗口刷新5.1 函数职责表函数职责currentSong()切换当前展示内容nextSong()切换当前展示内容previousSong()切换当前展示内容progressRatio()计算界面统计值playStateText()封装业务逻辑这些函数没有故意抽得很复杂。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 }这段不是要照搬而是说明当前项目的函数组织方式先从模型拿当前数据再执行用户动作最后让绑定表达式刷新界面六、构建安装流程6.1 保留双入口externCvoidqtmain(){staticResponsiveQuickView*viewnullptr;if(view!nullptr){return;}configureSurfaceFormat();viewnewResponsiveQuickView();loadQml(view);}intmain(intargc,char*argv[]){configureSurfaceFormat();QGuiApplicationapp(argc,argv);ResponsiveQuickView view;if(!loadQml(view)){return-1;}returnapp.exec();}之前遇到过只导出qtmain时运行时报Symbol not found: main的情况所以这一批 Qt Demo 都统一保留两个入口。6.2 标题和 QML 加载boolloadQml(ResponsiveQuickView*view){view-setTitle(QStringLiteral(乐单));view-setSource(QUrl(QStringLiteral(qrc:/main.qml)));if(view-status()QQuickView::Error){qWarning()Failed to load qrc:/main.qml;returnfalse;}view-showResponsive();returntrue;}这里标题要和应用展示名保持一致。运行时如果日志出现 QML 加载失败优先检查qml.qrc。七、日志和窗口验证7.1 构建 HAPcdQtMusicListDemo /Users/luqingjiedemac/ohos/command-line-tools/bin/hvigorw assembleHap--stacktrace构建完成后HAP 默认输出在下面这个路径entry/build/default/outputs/default/entry-default-unsigned.hap7.2 安装和启动hdcinstall-rentry/build/default/outputs/default/entry-default-unsigned.hap hdc shell aa start-aEntryAbility-bcom.nutpi.yuedan-mentry启动命令里的 Bundle Name 必须和AppScope/app.json5一致。7.3 构建失败先看哪里QT_PREFIX是否指向同级qtforharmony。CMakeLists.txt的 project 名称是否已经替换。main.qml是否存在于qml.qrc中。八、常见问题8.1 过滤关键日志hdc shell hilog-x-z500|rg-nQtForOpenHarmony|QPAForOpenHarmony|yuedan|Failed|failed|Error|error|qrc|main function|Symbol not found|QML|ReferenceError|TypeError我重点看这些信号是否出现Symbol not found: main是否出现Failed to load qrc:/main.qml是否出现 QML 的ReferenceError或TypeError是否能看到应用进程还在运行8.2 截图命令hdc shell snapshot_display-f/data/local/tmp/yuedan_screen.jpeg hdcfilerecv /data/local/tmp/yuedan_screen.jpeg ./qt-docs/assets/yuedan-screen.jpeg截图不是装饰它是确认首屏真的渲染出来的证据。8.3 当前项目验证点点击歌曲后标题变化播放按钮文案变化上一首下一首循环正常经验这类小项目最适合把功能做窄把构建、启动、截图、日志验证做完整。九、发布前自检9.1 常见问题表现象可能原因处理方式白屏QML 没加载成功或窗口未刷新检查qrc:/main.qml和日志找不到 main只保留了qtmain同时导出main和qtmain构建找不到 QtQT_PREFIX路径错误使用-DQT_PREFIX../qtforharmony启动失败Bundle Name 写错对照com.nutpi.yuedan窗口缩放不刷新根对象没有跟随窗口尺寸使用响应式QQuickView刷新策略9.2 排查顺序先看 HAP 是否重新构建。再看安装的包名是不是当前项目。然后看日志里有没有 QML 或 main 符号错误。最后才去调整具体控件布局。十、后续扩展10.1 可以继续做的功能接入本地音频文件加入播放队列增加歌词面板这些扩展不建议一口气全做。Qt 适配鸿蒙的早期样例优先目标应该是结构稳定、运行稳定、验证路径稳定。10.2 工程增强方向QtObject { id: appState property int currentIndex: 0 property bool dirty: false property string message: ready }当页面状态继续变多可以把零散属性收进一个QtObject这样顶层不会越来越乱。10.3 清理和重建rm-rfentry/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_sdk11.3 为什么只保留三个链接这批文章用于项目发布和读者复现。链接过多会分散注意力所以正文只保留社区入口、OpenHarmony 文档和 Qt SDK 仓库三个入口。总结乐单 这个 Demo 的重点是展示歌曲列表和播放状态 UI。从工程角度看它已经覆盖了 Qt for OpenHarmony 项目里最关键的几步配置 Qt SDK、打包 QML、保留双入口、构建 HAP、安装启动和截图验证。如果你也在做 Qt 适配鸿蒙可以先用这种小项目把链路跑稳再逐步接入更真实的数据和业务。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力