鸿蒙PC迁移:Avogadro Qt 分子编辑器鸿蒙PC适配全记录
一、写在前面
欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区:https://harmonypc.csdn.net/
项目开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_Avogadro
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
这篇文章记录的是 Avogadro 2 在 HarmonyOS PC / 鸿蒙真机环境中的一次适配过程。
Avogadro 2 是计算化学、分子建模、教学和材料科学领域常用的一款分子编辑器 / 可视化工具。它的核心用途是把分子结构(原子、化学键、坐标)以三维方式渲染出来,支持球棍模型、空间填充模型、线框模型等多种表示方式,并能读写 XYZ / CJSON / CML / PDB 等多种化学文件格式。
原版 Avogadro 2 是一个 C++ / Qt 桌面项目,主体由 Qt Widgets 桌面界面、Qt-free 的可移植核心库(avogadro/core、avogadro/io),以及一套基于桌面 OpenGL 4.0 的硬件加速渲染管线(avogadro/rendering)组成。它和 Electron 项目完全不一样,也不是 ArkUI 原生项目。适配鸿蒙 PC 时,关键问题不是“前端资源路径怎么加载”,而是:
- 怎样让一个 Qt Widgets 桌面程序进入鸿蒙 Stage 模型;
- 怎样让 native
libentry.so作为 HAP 的 native library 被 QPA 插件加载并启动; - 怎样让 Qt 界面绘制到鸿蒙 PC 窗口(XComponent)中;
- 怎样把 Qt for Harmony SDK、QPA 插件和 Qt 动态库打进 HAP;
- 桌面 OpenGL 4.0 渲染在鸿蒙上只有 OpenGL ES 3.x,没有桌面 GL,三维渲染怎么落地;
- 怎样先做出一个可运行、可旋转、可缩放、可切换分子的 Avogadro Harmony 可视化版本。
这条路线的目标很明确:先把 Avogadro 作为一个 Qt Widgets 应用稳定跑到鸿蒙 PC 真机上,并能交互式查看分子三维结构,再逐步评估完整可移植核心(avogadro/core + avogadro/io)和 OpenGL → OpenGL ES 渲染栈的深度迁移。

二、项目背景:Avogadro 是 Qt/C++ 桌面应用
适配前先看项目性质。
Avogadro 2 不是 Electron 应用,也不是 WebView 应用。它的主体是 C++ / Qt 桌面程序,原始桌面版本主要面向 Windows、macOS 和 Linux。整个代码库大致分成三层:
- 可移植核心:
avogadro/core、avogadro/io、avogadro/calc、avogadro/quantumio。这部分是 Qt-free、GL-free 的,只依赖 Eigen 和几个 header-only 的第三方库(nlohmann、pugixml、struct、toml++、cppoptlib),天然适合交叉编译到 arm64-ohos。 - 渲染层:
avogadro/rendering,基于桌面 OpenGL 4.0 / GLSL#version 400/ GLEW。鸿蒙上只有 OpenGL ES 3.x / EGL / Vulkan,没有桌面 GL,这一层是迁移里最硬的骨头。 - Qt Widgets 桌面 UI:窗口、工具栏、文件对话框等。
这次适配新增的鸿蒙工程目录是:
ohos_avogadrolibs/
├── avogadro/ # 上游核心库(core / io / rendering ...)
├── thirdparty/ # header-only 第三方依赖
├── CMakeLists.txt # 上游桌面构建(保持不动)
└── harmony_pc/ # 鸿蒙侧新增工程,桌面构建完全不受影响
├── AppScope/
│ └── app.json5 # bundleName: org.openchemistry.avogadro
├── build-profile.json5 # app 级签名 / product 配置
├── hvigorfile.ts / oh-package.json5
├── qtforharmony_sdk/ # 工程自带的 Qt 5.15 for HarmonyOS(arm64-v8a)
└── entry/
├── build-profile.json5 # externalNativeOptions -> CMake
├── libs/arm64-v8a/ # 打进 HAP 的预编译 .so(QPA 插件、ssl、media)
└── src/main/
├── module.json5 # deviceTypes: 2in1 / tablet
├── ets/ # ArkTS 桥
│ ├── abilitystage/MyAbilityStage.ets
│ ├── entryability/EntryAbility.ets
│ └── pages/Index.ets
└── cpp/
├── CMakeLists.txt
└── avogadro_ohos_shell.cpp # 分子查看器 shell
关键约束是:上游 Avogadro 源码保持不动,所有鸿蒙相关的东西都放在 harmony_pc/ 里,所以桌面构建(cmake -S . -B build)完全不受影响。
当前适配里最核心的文件是:
harmony_pc/entry/src/main/cpp/avogadro_ohos_shell.cpp # Qt Widgets 分子查看器
harmony_pc/entry/src/main/cpp/CMakeLists.txt # SHELL / FULL 双模式构建
harmony_pc/entry/build-profile.json5 # CMake 参数 / abiFilters
harmony_pc/AppScope/app.json5 # 包名与应用信息

三、路线选择:借鉴 TupiTube / Krita / Natron 的 Qt for Harmony 思路
适配 Avogadro 时,参考了之前几个 Qt → HarmonyOS 工程:ok-tupitube.desk-ohos、ok-krita-ohos、ok-Natron-ohos。
这里要特别区分:参考的是这些项目的 Qt for Harmony SDK、Stage 工程组织方式和 native Qt 启动管线,而不是把它们的业务代码拿来跑。最终真机上安装的包始终是 Avogadro:
bundleName: org.openchemistry.avogadro
Ability: EntryAbility
Module: entry
AppScope/app.json5 中应用信息配置为:
{
"app": {
"bundleName": "org.openchemistry.avogadro",
"vendor": "openchemistry",
"versionCode": 2000000,
"versionName": "2.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
鸿蒙工程的设备类型面向 PC / 平板:
"deviceTypes": [
"2in1",
"tablet"
]
Qt SDK 通过 entry/build-profile.json5 的 CMake 参数传入。Avogadro 这次特意用的是工程自带的 Qt SDK(不引用其他项目的 SDK):
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "-DQT_PREFIX=qtforharmony_sdk -DAVOGADRO_FULL_APP=AUTO",
"cppFlags": "",
"abiFilters": ["arm64-v8a"]
}
这里 QT_PREFIX=qtforharmony_sdk 是相对路径,CMake 里会把它解析成 harmony_pc/qtforharmony_sdk 的绝对路径,并校验 lib/cmake/Qt5/Qt5Config.cmake 是否存在。这一点是整个适配能跑起来的基础——QT_PREFIX 必须指向 Qt for Harmony SDK,否则 CMake 找不到 Qt5Config.cmake、Qt Widgets 和 QPA 平台插件。工程内置的是 Qt 5.15.12 for HarmonyOS(arm64-v8a,OHOS SDK 18)。
另一个关键参数是 AVOGADRO_FULL_APP,它决定了构建模式(这是这次适配的核心设计):
| 模式 | 取值 | 编译什么 | 状态 |
|---|---|---|---|
| SHELL | AUTO / OFF(默认) |
只编 avogadro_ohos_shell.cpp——一个自包含的 Qt Widgets 分子查看器,只链接 Qt5 Core/Gui/Widgets/Svg/Network/OpenGL,不依赖 avogadrolibs 源码 |
✅ 可运行 |
| FULL | ON |
把 Avogadro 的 Qt-free 核心(avogadro/core + avogadro/io)编进 libentry.so,用真实文件解析驱动查看器。需要 -DAVOGADRO_DEPS_PREFIX(Eigen3)+ 生成头文件 |
🚧 进行中 |
本次选择的第一阶段路线是:
- 不重写 ArkUI 主界面;
- 不一次性迁完整核心库和 OpenGL 渲染栈;
- 先用 Harmony Stage 工程承载 Qt Widgets;
- 用 C++ 写一个自包含的分子查看器 SHELL(CPU 渲染,绕过 OpenGL ES 移植);
- 真机验证窗口、工具栏、分子三维显示、旋转、缩放、切换表示法;
- 再根据后续目标决定是否继续迁完整核心和 GLES 渲染。
这条路线适合快速验证 Qt 桌面工具在鸿蒙 PC 上的窗口、输入、构建、安装和运行链路。

四、鸿蒙工程壳:Stage Ability + XComponent + QPA → libentry.so
鸿蒙侧仍然是标准 Stage 工程。整条启动管线是这样串起来的:
HarmonyOS 启动 org.openchemistry.avogadro(EntryAbility)
-> EntryAbility.onWindowStageCreate() 加载 pages/Index
-> Index.ets 构建一个 XComponent(libraryname: plugins_platforms_qopenharmony)
-> EntryAbility 调用 qpa.startQtApplication(this)
-> QPA 插件在 XComponent 上创建 EGL/GLES 表面,并 dlopen() libentry.so
-> 解析 libentry.so 导出的 extern "C" qtmain
-> qtmain -> main() -> QApplication -> Avogadro 主窗口渲染进这个表面
Index.ets 里的核心就是一个 XComponent,它的 libraryname 指向 QPA 平台插件:
XComponent({
id: this.windowId,
type: XComponentType.NODE,
libraryname: 'plugins_platforms_qopenharmony'
})
.width('100%')
.height('100%');
MyAbilityStage.ets 在工程启动时把 QPA 插件挂上去,并通过 onAcceptWant 返回主窗口实例 key:
onCreate(): void {
qpa.attachAbilityStage(this);
}
onAcceptWant(want: Want): string {
// 首次启动返回 avogadro_main_window
...
}
EntryAbility.ets 在窗口 stage 创建后,真正拉起 Qt 应用:
import qpa from 'libplugins_platforms_qopenharmony.so';
async onWindowStageCreate(windowStage: Window.WindowStage) {
...
await this.launchParamSet();
this.startQtApplication();
}
private startQtApplication() {
qpa.startQtApplication(this); // QPA 插件随后 dlopen libentry.so 并调用 qtmain
}
native 侧的 CMakeLists.txt 把入口编译成 entry(即 libentry.so)。SHELL 模式下只编一个源文件:
add_library(entry SHARED
"${CMAKE_CURRENT_SOURCE_DIR}/avogadro_ohos_shell.cpp")
target_compile_features(entry PRIVATE cxx_std_17)
target_compile_definitions(entry PRIVATE
APP_NAME="Avogadro"
OPENHARMONY
NAPI_DISABLE_CPP_EXCEPTIONS)
set_target_properties(entry PROPERTIES BUILD_RPATH "$ORIGIN")
target_link_libraries(entry PRIVATE
Qt5::Core
Qt5::Gui
Qt5::Widgets
Qt5::Svg
Qt5::Network
Qt5::OpenGL)
这里有一个踩过的坑写进了注释:链接 Qt5::Gui 会让 hvigor 的 collectAllLibs 把 Qt 的图片格式插件(qgif/qico/qjpeg/qsvg)一起打包,而 qsvg 插件又依赖 libQt5Svg.so,所以必须同时链接 Qt5::Svg,否则 QPA 插件加载 libentry.so 时会失败,真机上表现为白窗口。另外 QPA 平台插件本身是被 ArkTS 加载的,不需要在这里链接。
C++ 入口通过导出的 qtmain 被 QPA 插件调用:
int main(int argc, char** argv)
{
QApplication app(argc, argv);
app.setApplicationName(QStringLiteral("Avogadro"));
app.setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
app.setStyleSheet(QLatin1String(kStyleSheet)); // Avogadro 风格深色主题
auto* w = new MainWindow();
w->show();
return app.exec();
}
#if defined(Q_OS_OPENHARMONY)
// Qt OpenHarmony 平台插件 dlopen() libentry.so 后调用 "qtmain"
extern "C" int qtmain(int argc, char* argv[])
{
return main(argc, argv);
}
#endif
五、SHELL 分子查看器:先做可运行的 MVP
完整核心库 + OpenGL 渲染栈的迁移不是第一阶段目标。为了先把真机可用链路跑通,这次实现了一个自包含的 Qt Widgets 分子查看器 avogadro_ohos_shell.cpp。
它的主窗口 MainWindow 是一个标准 QMainWindow:顶部一条工具栏,中间一块自绘的分子画布 MoleculeView(继承 QWidget),底部一个状态栏。
当前 SHELL 查看器的能力包括:
- 内置 5 种分子:水 (H₂O)、甲烷 (CH₄)、氨 (NH₃)、乙醇 (C₂H₆O)、苯 (C₆H₆);
- 三种表示法:球棍(Ball and stick)、空间填充(Space filling)、线框(Wireframe);
- CPK / Jmol 元素配色、按共价半径定球大小、基于距离的化学键自动识别(bond perception);
- 拖拽旋转、滚轮缩放、Reset view 复位视角;
- 画家算法的深度排序 + 雾化(远处变暗),让三维层次更自然;
- 打开 .xyz 文件(
QFileDialog,真机上后续可接 OHOS 文件选择器); - 状态栏显示分子式 + 原子数 + 键数。
画布的交互逻辑很直接——滚轮缩放、左键拖拽改 yaw/pitch,重绘时用 QMatrix4x4 做旋转再投影到屏幕:
void wheelEvent(QWheelEvent* e) override {
const double steps = e->angleDelta().y() / 120.0;
m_zoom *= std::pow(1.15, steps);
m_zoom = qBound(0.2, m_zoom, 8.0);
update();
}
void mouseMoveEvent(QMouseEvent* e) override {
if (!m_dragging) return;
const QPoint d = e->pos() - m_last;
m_last = e->pos();
m_yaw += d.x() * 0.01;
m_pitch += d.y() * 0.01;
m_pitch = qBound(-M_PI / 2 + 0.01, m_pitch, M_PI / 2 - 0.01);
update();
}
工具栏里两个下拉框分别切换“当前分子”和“表示法”:
m_repCombo->addItems({QStringLiteral("Ball and stick"),
QStringLiteral("Space filling"),
QStringLiteral("Wireframe")});
六、为什么先用 QPainter 做 CPU 渲染,而不是 OpenGL
这是 Avogadro 适配里最需要解释的一个决策。
原版 Avogadro 的三维渲染(avogadro/rendering)是桌面 OpenGL 4.0:GLSL #version 400、用 GLEW 加载函数、球用 impostor、gl_FragData / glBindFragDataLocation 等等。而 HarmonyOS 上只有 OpenGL ES 3.x / EGL / Vulkan,没有桌面 GL。直接把渲染层搬过来是跑不起来的,需要一次完整的 GL 4.0 → GLES 3.0 移植,已核实的“必改清单”包括:
- sized internal format:
texture2d.cpp/solidpipeline.cpp用了无尺寸的GL_DEPTH_COMPONENT/GL_RGB,GLES3 要求带尺寸的格式并匹配类型; - GLSL ES:
#version 400→#version 300 es,补 precision 限定符,把gl_FragData/glBindFragDataLocation换成layout(location=)输出; - 球 impostor 用了
gl_FragDepth(GLES3 合法,但会关掉 early-Z,需在目标 GPU 上实测);圆柱是真三角网格,可原样移植; glLineWidth > 1在 GLES3 core 不支持,线框几何要么丢掉,要么用四边形画;- 丢掉 GLEW(
glewInit),改用 OHOS NDK 的 EGL/GLES 头; - EGL context 是线程亲和的,所有 GL 调用要留在 XComponent 表面线程上。
为了先让应用跑起来、可演示,SHELL 直接绕开了这一整套:三维场景用 QPainter 在 CPU 上做深度排序光栅化(画家算法),所以这个 shell 不需要任何 OpenGL ES shader 移植就能在真机上交互运行。这是“先能跑、再优化”的典型取舍——OpenGL → GLES 的移植留给后续 Phase 3。
七、构建、安装和真机运行
本次工程路径:
/XM/ohos_avogadrolibs/harmony_pc
构建时需要指定 DevEco SDK 环境变量,否则可能遇到 hvigor / SDK 版本不匹配问题,例如:
(0 , hvigor_1.WithParamReplacement) is not a function
最终使用的构建命令是:
cd /XM/ohos_avogadrolibs/harmony_pc
env DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk \
OHOS_BASE_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony \
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node \
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js \
--mode module -p module=entry -p product=default assembleHap \
--analyze=normal --parallel --incremental --no-daemon --stacktrace
native 部分的 CMake 构建由 entry/build-profile.json5 → externalNativeOptions 驱动,会把 -DQT_PREFIX=qtforharmony_sdk -DAVOGADRO_FULL_APP=AUTO 传给 CMakeLists.txt,只编 arm64-v8a。
签名后的 HAP 路径:
harmony_pc/entry/build/default/outputs/default/entry-default-signed.hap
安装到真机:
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc install -r \
/XM/ohos_avogadrolibs/harmony_pc/entry/build/default/outputs/default/entry-default-signed.hap
启动应用:
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc shell aa start \
-b org.openchemistry.avogadro \
-a EntryAbility
强停应用:
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc shell aa force-stop org.openchemistry.avogadro
查看安装信息(确认包名 / ABI / SDK 版本):
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc shell bm dump -n org.openchemistry.avogadro
关键输出应包含:
bundleName: org.openchemistry.avogadro
versionName: 2.0.0
cpuAbi: arm64-v8a

八、真机验证流程
最终真机验证按下面这条路径走:
安装 HAP
-> 启动 Avogadro(aa start)
-> 默认加载乙醇(Ethanol)球棍模型
-> 用 "Molecule:" 下拉框切换到苯(Benzene)等其他分子
-> 用 "View:" 下拉框切换 Ball and stick / Space filling / Wireframe
-> 鼠标拖拽旋转、滚轮缩放
-> 观察底部状态栏的分子式 + 原子数 + 键数
-> Reset view 复位
切换表示法和切换分子是验证“工具栏交互 + 重绘”是否正常的最直接方式;旋转和缩放则验证鼠标 / 滚轮事件是否正确传到了 Qt 窗口。



九、当前版本边界和后续方向
这次适配完成的是 Avogadro 在鸿蒙 PC 真机上的第一阶段可运行版本(SHELL)。它已经解决了几个关键问题:
- HarmonyOS Stage 工程接入;
- 工程自带 Qt for Harmony SDK(Qt 5.15.12 / arm64-v8a)接入;
- ArkTS → XComponent → QPA →
libentry.so→qtmain的完整启动管线; - Qt Widgets native 应用启动与窗口显示;
- 链接
Qt5::Svg解决 qsvg 插件依赖导致的白窗口问题; - Avogadro 包名和应用信息替换;
- HAP 构建、签名、安装;
- 真机三维分子显示、拖拽旋转、滚轮缩放、表示法切换、分子切换;
- 用 CPU
QPainter渲染绕开 OpenGL ES 移植,先把应用跑起来。
后续方向(roadmap):
- Phase 2 — 接入可移植核心(
-DAVOGADRO_FULL_APP=ON):把avogadro/core+avogadro/io编进libentry.so,用FileFormatManager加载真实 XYZ / CJSON / CML / PDB 文件替换内置分子。前提是为 arm64-ohos 提供 Eigen3(-DAVOGADRO_DEPS_PREFIX),并产出源码 include 的生成头文件(*export.h、avogadro/core/version.h、元素 / 空间群数据表)。 - Phase 3 — 渲染移植(OpenGL 4.0 → OpenGL ES 3.0):按第六节的“必改清单”把
avogadro/rendering移到 GLES3 / GLSL-ES,换用 EGL/GLES,丢掉 GLEW。 - Phase 4 — 原生外壳(可选):长期可考虑彻底脱 Qt,把移植后的 GLES 渲染器直接挂进 ArkUI 的 XComponent(NAPI),用 ArkTS 工具栏 +
DocumentViewPicker文件选择器,做出更原生的鸿蒙体验。
十、总结
这次 Avogadro 适配不是一次简单的套壳,也不是 Electron 项目的资源搬运。它更接近一次 Qt 桌面工具在鸿蒙 PC 上的 native 运行验证,而且因为 Avogadro 自带一套桌面 OpenGL 渲染栈,它比一般 Qt Widgets 工具更能体现“鸿蒙没有桌面 GL”这个现实约束下的工程取舍。
整个过程可以概括为:
识别原项目技术栈(Qt Widgets + 可移植核心 + 桌面 OpenGL)
-> 新增 harmony_pc Stage 工程,上游源码保持不动
-> CMake 接入工程自带 Qt for Harmony SDK(Core/Gui/Widgets/Svg/Network/OpenGL)
-> 设计 SHELL / FULL 双模式,先编可运行的 SHELL
-> 用 QPainter CPU 渲染绕开 OpenGL ES 移植
-> 替换包名和应用信息
-> 构建签名 HAP
-> 安装到鸿蒙 PC 真机验证三维分子显示与交互
最终结果是,一个以 org.openchemistry.avogadro 为包名的 Avogadro Harmony PC 应用,可以在真机上加载内置分子、以球棍 / 空间填充 / 线框三种方式三维显示,并支持拖拽旋转和滚轮缩放。虽然它还不是完整上游 Avogadro(核心库与 OpenGL 渲染栈仍在迁移中),但已经具备了第一阶段可运行、可演示、可继续扩展的基础。
更多推荐




所有评论(0)