鸿蒙PC:Qt适配OpenHarmony实战【明算】:用 Qt Quick 做一个轻量四则运算计算器
欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/项目开源地址:https://AtomGit.com/lqjmac/qt_mingsuan明算。明算是一个轻量计算器,它把数字输入、四则运算、退格、清空和错误提示集中在一个 Qt Quick 页面里。项目定位是简易计算器,核心功能是加减乘除四则运算。本文会从工程结构、Qt
前言
欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/
项目开源地址:https://AtomGit.com/lqjmac/qt_mingsuan
这篇文章记录第 2 个 Qt 适配鸿蒙小项目:明算。
明算是一个轻量计算器,它把数字输入、四则运算、退格、清空和错误提示集中在一个 Qt Quick 页面里。
项目定位是 简易计算器,核心功能是 加减乘除四则运算。
本文会从工程结构、Qt SDK 配置、QML 界面、入口函数、窗口缩放验证、安装运行和常见问题几个角度完整拆解。
为了便于继续扩展,文章中的命令、路径和 Bundle Name 都按实际项目填写。
提示:本文不是只展示界面截图,而是把一个可构建、可安装、可启动的 Qt for OpenHarmony 项目拆开讲清楚。
一、项目定位与学习目标
1.1 为什么选择这个项目
计算器项目很适合验证按钮网格、文本状态、输入反馈和窗口缩放,因为它的交互密度比时钟更高。
从学习路径看,明算 的价值不在于功能复杂,而在于它足够小,适合集中验证 Qt Quick 在鸿蒙窗口里的运行方式。
做这种小项目时,我更关心三件事:能不能构建、能不能显示、窗口变化时能不能立即刷新。
这三件事都跑通以后,再继续堆业务功能才比较踏实。
1.2 本文会覆盖哪些知识点
主要学习点如下:
- 数字输入
- 加减乘除
- 小数点处理
- 正负号切换
- 除零提示
- Qt SDK 使用相对路径配置
main和qtmain双入口保留- HAP 构建、安装、启动命令
- 最大化和还原时的窗口刷新检查
1.3 最终效果截图

图 1:明算 在 OpenHarmony 桌面环境中的运行效果。
1.4 推荐阅读顺序
如果你是第一次做 Qt 适配鸿蒙,可以按下面顺序读:
- 先看项目基本信息,确认目录和包名。
- 再看 Qt SDK 规则,避免
QT_PREFIX写错。 - 然后看入口函数,理解为什么保留
main和qtmain。 - 最后看构建、安装和窗口验证命令。
二、项目基本信息
2.1 信息总览
| 项目 | 内容 |
|---|---|
| 应用展示名 | 明算 |
| 项目目录名 | QtCalculatorLite |
| Bundle Name | com.nutpi.mingsuan |
| 项目类型 | 简易计算器 |
| 核心功能 | 加减乘除四则运算 |
| 核心实现 | QML 状态机 + 响应式 QQuickView |
| 界面风格 | 深色计算面板 |
这个表格建议直接写进 README 或项目清单中。
当项目数量逐渐增加时,目录名、中文名和 Bundle Name 的对应关系非常重要。
2.2 目录结构
qt_for_harmony/
├── qtforharmony/
├── QtCalculatorLite/
│ ├── 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 环境,路径就会失效。
相对路径的好处是清晰、稳定、可迁移。
可以按下面步骤检查:
- 确认项目位于
qt_for_harmony目录下。 - 确认 SDK 位于
qt_for_harmony/qtforharmony。 - 确认
entry/build-profile.json5里写的是../qtforharmony。
四、工程配置拆解
4.1 CMakeLists.txt 的关键点
cmake_minimum_required(VERSION 3.5.0)
project(qtcalculatorlite)
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 启动流程
启动链路可以理解为:
- HarmonyOS Stage Ability 创建 XComponent 容器。
- QPA 插件加载
libentry.so。 main或qtmain初始化 Qt 应用环境。- C++ 侧加载
qrc:/main.qml。 - 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
Grid {
columns: 4
spacing: 10
Repeater {
model: ["7", "8", "9", "÷", "4", "5", "6", "×"]
Rectangle {
width: 72
height: 72
radius: 8
Text { anchors.centerIn: parent; text: modelData }
MouseArea { anchors.fill: parent; onClicked: pressKey(modelData) }
}
}
}
这段代码体现了 明算 的核心功能:加减乘除四则运算。
真实项目里可以继续拆成多个 QML 组件,但演示项目保持单文件更利于阅读。
6.3 UI 层级表
| 层级 | 作用 | 设计建议 |
|---|---|---|
| 根 Rectangle | 承载背景和全局尺寸 | 颜色和尺寸统一从这里派生 |
| 页面 Column/Row | 控制整体排版 | 避免控件互相覆盖 |
| 功能卡片 | 展示核心信息 | 半径不超过 8px,保持工具感 |
| 按钮区域 | 触发业务动作 | 用明确文案和稳定尺寸 |
| 列表或指标区 | 承载动态数据 | 设置 clip,避免越界 |
七、核心逻辑拆解
7.1 数据模型
function pressKey(key) {
if (key >= "0" && key <= "9") {
appendDigit(key)
return
}
if (key === "+" || key === "-" || key === "×" || key === "÷") {
chooseOperator(key)
}
}
这个模型只做最小必要的数据组织。
演示项目不建议一上来引入数据库、网络层或复杂状态管理。
先把交互链路跑通,再逐步增强。
7.2 业务动作
if (operator === "÷" && rightValue === 0) {
displayText = "不能除以 0"
hasError = true
return
}
业务动作保持短小有一个好处:问题发生时更容易定位。
比如按钮点击无响应时,可以直接确认是 MouseArea、函数调用还是模型更新的问题。
7.3 交互验证步骤
建议每次改完 QML 后都做下面这组验证:
- 启动应用,确认首屏不白屏。
- 点击主要按钮,确认状态变化。
- 最大化窗口,确认没有黑边。
- 还原窗口,确认内容立刻刷新。
- 查看日志,确认没有 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.mingsuan -m entry
sleep 2
hdc shell snapshot_display -f /data/local/tmp/mingsuan_screen.jpeg
hdc file recv /data/local/tmp/mingsuan_screen.jpeg /tmp/mingsuan_screen.jpeg
hdc shell uitest dumpLayout -p /data/local/tmp/layout_mingsuan.json -d 0
如果截图正常、布局树里的 XComponent bounds 与窗口内部区域一致,基本可以确认窗口刷新链路是通的。
九、构建、安装与运行
9.1 构建 HAP
cd /new/ugc/qt_for_harmony/QtCalculatorLite
/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.mingsuan -m entry
hdc shell pidof com.nutpi.mingsuan
如果 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 推荐排查顺序
遇到问题时不要同时改很多地方。
建议按下面顺序处理:
- 先确认 HAP 能构建成功。
- 再确认应用能被
aa start拉起。 - 然后检查 hilog 是否有入口符号问题。
- 最后看 QML 布局、交互和窗口缩放。
经验:构建问题、启动问题、渲染问题要分开排查。混在一起改,最后很难判断是哪一步修好的。
十一、质量自检
11.1 运行验证矩阵
| 验证项 | 预期结果 | 当前项目建议 |
|---|---|---|
| 普通启动 | 显示首屏 | 看到 明算 主界面 |
| 主要交互 | 功能状态变化 | 加减乘除四则运算 |
| 最大化 | 界面铺满窗口 | 无黑边,无延迟刷新 |
| 还原 | 尺寸恢复正常 | 不缩成点,不等待点击刷新 |
| 日志 | 无入口错误 | 不出现 Symbol not found: main |
11.2 发布前检查清单
发布项目或文章前,我建议至少检查这些内容:
- README 是否写明 SDK 仓库和
QT_PREFIX规则 - Bundle Name 是否和其他项目冲突
- 应用展示名是否已经产品化
- HAP 是否能重新构建
- 截图是否来自实际运行效果
- 最大化和还原是否已经验证
十二、可以继续扩展什么
12.1 功能增强方向
围绕 明算,后续可以做这些增强:
- 把“数字输入”做成更完整的产品级体验
- 把“加减乘除”做成更完整的产品级体验
- 把“小数点处理”做成更完整的产品级体验
- 把“正负号切换”做成更完整的产品级体验
- 把“除零提示”做成更完整的产品级体验
- 增加本地持久化能力
- 增加主题切换
- 增加更多输入校验和异常提示
12.2 工程增强方向
工程层面可以按下面路线演进:
- 把通用的 ResponsiveQuickView 抽成公共模板。
- 把 README 的 SDK 规则变成统一脚本检查。
- 把截图验证命令纳入日常验收流程。
- 把项目列表和 Bundle Name 放进表格统一维护。
十三、完整命令速查
13.1 从构建到启动
cd /new/ugc/qt_for_harmony/QtCalculatorLite
/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.mingsuan -m entry
13.2 截图和布局树
hdc shell snapshot_display -f /data/local/tmp/mingsuan_screen.jpeg
hdc file recv /data/local/tmp/mingsuan_screen.jpeg /tmp/mingsuan_screen.jpeg
hdc shell uitest dumpLayout -p /data/local/tmp/layout_mingsuan.json -d 0
hdc file recv /data/local/tmp/layout_mingsuan.json /tmp/layout_mingsuan.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 工程规则复制到更多产品化小应用里。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
更多推荐


所有评论(0)