QHexViewNext 鸿蒙化经验总结:Qt 桌面工具到鸿蒙 PC 的实践
一、开源软件 QHexView 与 QHexViewNext 在鸿蒙上的差异
QHexView 是 virinext 开发的开源十六进制查看器,MIT 协议,约 849 行 C++ Qt 控件。它原本是纯 Linux/Windows 桌面工具,本次目标是将其移植到 HarmonyOS PC 平台。
与 MeldNext 的"UI 层 ArkTS 重写"路线不同,QHexViewNext 选择保留 Qt Widgets UI + libqohos 平台插件路线——Qt 源码几乎零修改即可运行。
| 维度 | 原生 QHexView(Linux/Windows) | QHexViewNext(鸿蒙 PC) |
|---|---|---|
| UI 框架 | Qt Widgets(QMainWindow/QMenuBar) | Qt Widgets(完全保留) |
| 编程语言 | C++(Qt) | C++(Qt) |
| 运行形态 | 桌面进程 | HAP(entry 模块) |
| UI 渲染 | QPA(窗口系统插件) | libqohos(QPA for HarmonyOS)+ XComponent |
| 差异引擎 | —(十六进制查看) | —(十六进制查看,原样保留) |
| 构建系统 | qmake / CMake | CMake + Hvigor |
| 部署方式 | 安装包 | HAP / APP(签名分发) |
| bundleName | — | com.develop.opensource.qhexviewnext |
| 设备支持 | x86/x86_64 | tablet、2in1(arm64) |
| 商用分发 | 源码开源 | AGC 上架 |
核心结论:QHexViewNext 100% 保留 Qt C++ 源码,仅通过 libqohos(Qt 平台插件)桥接鸿蒙 XComponent;改动集中在 CMake 工程化、字体/调色板适配与打包签名。
二、技术架构与技术原理
2.1 总体架构
┌─────────────────────────────────────────────────────────────┐
│ ArkTS 壳 (UI 入口) │
│ QAbilityStage.ets / QAbility.ets / OhosExportModules.ts │
│ 创建 XComponent surface · 加载 native 模块 · NAPI 注册 │
└─────────────────────────────┬───────────────────────────────┘
调用 / 桥接
┌─────────────────────────────────────────────────────────────┐
│ libqohos.so (Qt 平台插件 QPA) │
│ 桥接 Qt 窗口系统与鸿蒙 XComponent · 事件分发 · 调色板 │
└─────────────────────────────┬───────────────────────────────┘
调用
┌─────────────────────────────────────────────────────────────┐
│ Qt 5.12.12 库 │
│ Core / Gui / Widgets / DBus + libc++_shared.so 运行时 │
└─────────────────────────────┬───────────────────────────────┘
调用
┌─────────────────────────────────────────────────────────────┐
│ 应用本体 │
│ libqhexview.so (132KB) │
│ QHexView · MainWindow · 菜单栏 · 数据存储 │
└─────────────────────────────────────────────────────────────┘
运行时 so 依赖:libqohos.so + libQt5{Core,Gui,Widgets,DBus}.so
+ libc++_shared.so + libqhexview.so(共 7 个)
2.2 核心技术原理
2.2.1 libqohos(Qt 平台插件 QPA)桥接机制
Qt 的跨平台能力依赖 QPA(Qt Platform Abstraction)——每个平台一个插件实现窗口/渲染/输入。libqohos.so 就是 Qt for HarmonyOS 的 QPA 插件:
- 将 Qt 的窗口系统映射到鸿蒙 XComponent(surface 模式)
- 将 Qt 事件循环接入鸿蒙事件分发
- 提供 QClipboard / 调色板 / 光标等系统能力映射
QHexViewNext 不直接接触 N-API——Qt 源码调用 Qt API,libqohos 内部完成对鸿蒙 NAPI 的封装。这是与 MeldNext 最大的路线差异。
2.2.2 XComponent + Qt 渲染
libqohos 内部通过 XComponent(type=“surface”)获得原生渲染表面:
ArkTS 创建 XComponent → 传入 surfaceId
↓
libqohos: OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId)
↓
Qt 渲染引擎在 surface 上绘制(软件渲染,无需 OpenGL ES)
↓
eglSwapBuffers 上屏
QHexView 是纯 2D 控件(文本绘制为主),使用 Qt 软件渲染路径即可,不需要 OpenGL ES(MeldNext 的 Scintilla 编辑器才需要)。
2.2.3 QClipboard / 文件对话框适配
- 剪贴板:Qt
QApplication::clipboard()由 libqohos 映射到鸿蒙 pasteboard,需要在 module.json5 申请READ_PASTEBOARD权限 - 文件选择器:
QFileDialog::getOpenFileName由 libqohos 映射到鸿蒙文件选择器,需要FILE_ACCESS_PERSIST权限持久化文件访问
2.2.4 调色板适配(防黑底)
libqohos 会把鸿蒙系统深色主题带进 Qt 调色板,导致 QMenuBar 黑底黑字不可读:
QPalette lightPalette;
lightPalette.setColor(QPalette::Window, QColor("#F0F0F0"));
lightPalette.setColor(QPalette::WindowText, Qt::black);
lightPalette.setColor(QPalette::Base, Qt::white);
lightPalette.setColor(QPalette::Text, Qt::black);
lightPalette.setColor(QPalette::Button, QColor("#F0F0F0"));
lightPalette.setColor(QPalette::ButtonText, Qt::black);
app.setPalette(lightPalette); // 不要 setStyle,避免改变控件外观
三、底层依赖介绍及移植要求
3.1 移植库一览
QHexViewNext 依赖 Qt 5.12.12 for OHOS 最小子集(98MB):
| 库/组件 | 版本 | 在 QHexViewNext 中的角色 | 移植方式 | 复用率 |
|---|---|---|---|---|
| Qt5Core | 5.12.12 | 事件循环/字符串/QSettings | 预编译 so + 头文件 | 100% |
| Qt5Gui | 5.12.12 | 字体/绘制/剪贴板 | 预编译 so + 头文件 | 100% |
| Qt5Widgets | 5.12.12 | QMainWindow/QMenuBar/QHexView | 预编译 so + 头文件 | 100% |
| Qt5DBus | 5.12.12 | libqohos 依赖(DT_NEEDED) | 预编译 so | 100% |
| libqohos | — | QPA 平台插件 | Qt SDK 提供 | 100% |
| libc++_shared | — | C++ 标准库运行时 | NDK 提供 | 100% |
| QHexView 源码 | virinext | 应用本体 | 原样保留 | ~99% |
QHexView 源码仅改动 2 处(字体 + 标题),复用率 ~99%,远高于 MeldNext 的 95%。
3.2 移植到 HarmonyOS 需要满足的要求
3.2.1 编译工具链
- 编译器:Clang 15(OHOS NDK 自带,非 GCC)
- 标准:CMake 3.5+,必须使用
ohos.toolchain.cmake或 DevEco 的hmos.toolchain.cmake - 架构:
-DOHOS_ARCH=arm64-v8a(鸿蒙 PC 为 arm64)
3.2.2 CMake 关键写法
# 必做①:候选路径存在性校验(防 .cxx 缓存假命中)
set(_QT_PREFIX_CANDIDATES "")
if(DEFINED QT_PREFIX AND NOT QT_PREFIX STREQUAL "")
list(APPEND _QT_PREFIX_CANDIDATES "${QT_PREFIX}")
endif()
list(APPEND _QT_PREFIX_CANDIDATES
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/qt-5.12.12-ohos-arm64"
"C:/Qt/5.12.12-ohos-arm64")
set(QT_PREFIX "")
foreach(_cand IN LISTS _QT_PREFIX_CANDIDATES)
if(EXISTS "${_cand}/lib/cmake/Qt5/Qt5Config.cmake")
set(QT_PREFIX "${_cand}"); break()
else()
message(WARNING "QT_PREFIX 候选路径无效,已跳过: ${_cand}")
endif()
endforeach()
# 必做②:交叉 toolchain 下显式钉死模块 config 目录
set(Qt5Core_DIR "${QT_PREFIX}/lib/cmake/Qt5Core" CACHE PATH "" FORCE)
set(Qt5Gui_DIR "${QT_PREFIX}/lib/cmake/Qt5Gui" CACHE PATH "" FORCE)
set(Qt5Widgets_DIR "${QT_PREFIX}/lib/cmake/Qt5Widgets" CACHE PATH "" FORCE)
set(CMAKE_AUTOMOC ON)
3.2.3 Qt 源码改造(字体)
QFont font("Noto Sans Mono", 10);
font.setStyleHint(QFont::Monospace);
setFont(font);
鸿蒙 PC 自带 Noto Sans Mono 字体,确保三栏(地址 / Hex / ASCII)严格等宽对齐。
3.2.4 运行时 .so 清单
entry/libs/arm64-v8a/ 必须包含 7 个 .so:
| .so | 大小 | 作用 |
|---|---|---|
| libqhexview.so | 132KB | 应用本体(用户代码) |
| libqohos.so | 5.5MB | Qt 平台插件 QPA |
| libQt5Widgets.so | 6.4MB | Qt 控件 |
| libQt5Core.so | 5.0MB | Qt 内核 |
| libQt5Gui.so | 4.5MB | Qt GUI |
| libQt5DBus.so | 557KB | libqohos 依赖(DT_NEEDED) |
| libc++_shared.so | 1.3MB | C++ 标准库(预编模式下必须手动放) |
四、移植流程
4.1 总体移植流程
QHexViewNext 鸿蒙化
│
┌───────────┬───────┴───────┬─────────────┐
▼ ▼ ▼ ▼
QHexView.cpp main.cpp CMakeLists.txt third_party/
(Qt 控件) (入口+调色板) (OHOS toolchain) Qt 5.12.12 for OHOS
~849 行 C++ 98MB 最小子集
└───────────┴───────┬───────┴─────────────┘
▼
┌──────────────────────────────┐
│ 交叉编译 (Clang 15 + AUTOMOC) │
│ → libqhexview.so │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ hvigor 打包 + 签名 │
│ → entry-default-signed.hap │
│ (24MB, 7 个 .so) │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 华为鸿蒙 PC (2in1 · arm64) │
│ hdc install / Run │
└──────────────────────────────┘
保留 Qt C++ 源码 · 不改写 ArkUI · libqohos 平台插件桥接
5 个阶段:环境准备 → Qt 子集提取 → CMake 编译 → 集成 DevEco → 签名 + 安装。
4.2 编译指令
4.2.1 CMake 编译(自动由 Hvigor 调用)
entry/build-profile.json5:
"buildOption": {
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": "",
"abiFilters": ["arm64-v8a"]
}
}
手动调试命令(模拟 Hvigor):
SDK="<DevEco>/sdk/default/openharmony/native"
"$SDK/build-tools/cmake/bin/cmake.exe" -B <tmp> -G Ninja \
-DCMAKE_TOOLCHAIN_FILE="$SDK/build/cmake/ohos.toolchain.cmake" \
-DOHOS_STL=c++_shared -DOHOS_ARCH=arm64-v8a -DOHOS_PLATFORM=OHOS \
-DCMAKE_MAKE_PROGRAM="$SDK/build-tools/cmake/bin/ninja.exe"
"$SDK/build-tools/cmake/bin/cmake.exe" --build <tmp> --parallel
4.2.2 Qt 最小子集提取
从 Qt 5.12.12 for OHOS 提取:
include/、lib/cmake/、lib/libQt5{Core,Gui,Widgets,DBus}.so、mkspecs/(全量)bin/主机工具:不能只拷 moc.exe——Qt5 的 ConfigExtras.cmake 启动时对 qmake/moc/rcc/uic 做存在性检查,用grep -rh -oE 'bin/[A-Za-z0-9_]+\.exe' lib/cmake/ | sort -u列出全部引用plugins/:按Qt5*Plugin.cmake中的 RELEASE 路径逐个拷贝插件 .so- 清除烙死路径:自编译 Qt 会把构建机 sysroot 烙进
Qt5GuiConfigExtras.cmake,需改为动态定位
4.2.3 转测分发(预编 .so 模式)
┌──────────────┐ robocopy ┌──────────────┐ hvigor ┌──────────────┐
│ 开发机 │ ──── 暂存 ────→ │ 转测包 │ ────→ │ 测试机 │
│ │ 转换 │ (精简 ~16MB) │ │ │
│ 完整工程 │ │ │ │ 零 Qt 依赖 │
│ + third_party │ │ 7 个预编 .so │ │ + 自动签名 │
│ + 编 C++ │ │ → entry/libs │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
robocopy <dev> <stage> /E /XD .cxx build .hvigor oh_modules .idea signing /XF *.iml *.p12 *.p7b *.cer
4.3 上下层交互方式
4.3.1 ArkTS ↔ libqohos 桥接
QHexViewNext 的 ArkTS 壳(QtSDK 官方模板,原样使用):
// QAbilityStage.ets —— 生命周期入口
// QAbility.ets —— 创建 XComponent,把 surfaceId 传给 native
// OhosExportModules.ts —— NAPI 模块注册表(删掉 lazy import 关键字)
Qt 源码完全不知道鸿蒙存在——所有鸿蒙交互都由 libqohos 承接。
4.3.2 系统能力映射
| Qt API | 鸿蒙系统能力 | 需要的权限 |
|---|---|---|
| QApplication::clipboard() | pasteboard | READ_PASTEBOARD |
| QFileDialog | 文件选择器 | FILE_ACCESS_PERSIST |
| QSettings | 数据持久化 | STORE_PERSISTENT_DATA |
| QProcess | 进程管理 | PREPARE_APP_TERMINATE |
4.4 关键配置文件
| 配置文件 | 作用 |
|---|---|
AppScope/app.json5 | bundleName / 版本 / 图标(app_name) |
build-profile.json5(根) | signingConfigs + products(signingConfig 引用不能删) |
build-profile.json5(entry) | externalNativeOptions(CMake 配置) |
module.json5 | 模块配置 + 权限声明(精简到 5 个) |
oh-package.json5 | 依赖声明(libentry.so / libqohos.so) |
signing/* | 签名材料(release.p12/cer/p7b,不入 git) |
4.5 注意事项
- moc.exe 是 MinGW 编译的(不是 MSVC)——诊断 0xc0000135 时要查
llvm-readobj --coff-imports导入表,PATH 加C:\Program Files\Git\mingw64\bin - libc++_shared.so 预编模式下必须手动放入 entry/libs,否则 libqohos 加载失败闪退
- signingConfig 引用不能删:转测包清签名只清
signingConfigs: [],保留 product 的"signingConfig": "default" - 权限最小化:17 个删到 5 个(SYSTEM_FLOAT_WINDOW 等一律删,AGC 受限权限审核严)
- 签名算法 SHA256withECDSA,Profile 用发布版(
qhexviewnextRelease.p7b) - 工程路径不含空格和中文(Qt 工具链兼容性)
- 混淆永不启用(libqohos 按名字回调 ArkTS,混淆断桥接闪退)
五、移植总结
5.1 移植模式
| 模式 | 适用 | 工作量 | QHexViewNext 采用 |
|---|---|---|---|
| 源码内嵌 + N-API | 小型库(diffutils/uchardet) | ★☆☆ | 否 |
| 源码适配 + XComponent | 需原生渲染的组件(Scintilla) | ★★★ | 否 |
| 保留 Qt + QPA 平台插件 | Qt 系应用 | ★★☆ | ✅ 是 |
| Lycium/HNP 独立二进制 | 命令行工具(Git/SVN) | ★☆☆ | 否 |
5.2 关键技术决策
- 保留 Qt Widgets UI,不改写 ArkUI——改造成本几乎为零
- C++ 源码 ~99% 复用,仅改字体/标题 2 处
- libqohos 平台插件承接全部鸿蒙桥接(QPA 机制)
- third_party 内置 Qt 子集,工程可移植、测试机免安装
- 预编 .so 转测分发,测试机零 C++ 编译
5.3 效果
- 代码复用率:Qt 源码 ~99% 未修改
- 功能完整性:十六进制查看、打开文件、复制粘贴、跳转、菜单均正常
- 体积:HAP 24MB(其中 Qt 框架库 23MB,应用本体仅 132KB/0.5%)
HAP 24MB 体积构成(应用本体仅 0.5%):
libQt5Widgets.so 6.4M ████████████████ 27%
libqohos.so 5.5M ██████████████ 23%
libQt5Core.so 5.0M ████████████ 21%
libQt5Gui.so 4.5M ███████████ 19%
libc++_shared.so 1.3M ███ 5%
libQt5DBus.so 0.6M █ 2%
libqhexview.so 0.1M ▏ 0.5% ← 应用本体
其他(资源 + ArkTS) 0.6M ▏ 2%
5.4 真实运行效果
鸿蒙 PC 真机(1920×1280)打开本地文件:

三栏等宽布局(地址 / Hex / ASCII)严格按列对齐,菜单栏 / 弹窗 / 选中态正常,UI 与原版 Qt 程序一致。
更多推荐

所有评论(0)