电路芯绘鸿蒙PC上架:把 413 个 C++ 文件的 Qt 电路 CAD 装进鸿蒙 PC 的沙箱

欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

把一个 Linux 桌面上的开源电路仿真套件搬到鸿蒙 PC,听起来只是「换平台编译」——真正动手才发现,从 Qt 平台插件、沙箱权限模型到应用市场审核,每一层都有自己的脾气。本文以上架全流程的视角复盘「电路芯绘」(基于 Qucs-S 24.4.1 的鸿蒙 PC 移植):需求定位、架构移植、功能开发、审核避坑、资质核对与上架后迭代一条龙走完,先看货架上长什么样,再一层层拆开货架底下的工程事实——Qt 在鸿蒙上的宿主模型、为什么沙箱里 QProcess 必死、2728 条界面翻译的工程化做法,以及一次「功能异常」打回带来的七处平台适配修复。

项目上架信息 / 工程事实
应用名电路芯绘
包名com.qucs.circuit
版本1.1.0 (10100000)(审核修复后的复提包)
设备形态2in1 / tablet(HarmonyOS PC)
上游项目Qucs-S 24.4.1(Qt 开源电路仿真套件,GPL)
技术栈Qt 5.12.12 + Qt on OpenHarmony 平台插件(libqohos)+ NAPI
核心库C++ 图形与业务引擎全量重编,.so 约 9.1 MB
权限5 项,逐项写明 reason
上架状态已在鸿蒙应用市场正式上架,复审通过

一、先看效果图:一个「能画、能配、能导出」的原理图工具

图 1 · 主视觉与内置示例工程

电路芯绘主视觉与内置示例工程

图 1:应用主视觉与内置示例工程。打开即有一份可编辑的放大器示例原理图——这是审核反馈直接催生的设计:审核员点开应用的第一眼,必须看到「真实功能在跑」,而不是一张空白画布。

图 2 · 原理图编辑与仿真配置

原理图编辑与仿真块配置

图 2:原理图编辑界面。元件、连线、节点命名与仿真块同屏:瞬态 / 交流 / 直流仿真块以「块」的形式直接挂在图纸上,频率、步数逐项可配——这是 Qucs 系一贯的「仿真配置也是图纸元素」设计哲学。

图 3 · 2in1 真机上的元件放置

2in1 真机上的元件放置

图 3:真机运行实拍。拖放放置、旋转镜像、无限点阵画布与完整键鼠快捷键在 2in1 设备上全部可用。桌面级 CAD 的交互密度(右键菜单、双击改参、框选平移)是这类移植最难「不掉价」的部分。


二、为什么选 Qucs-S,难在哪

电子类课程、竞赛与毕业设计需要电路绘制与仿真配置工具。浏览器方案依赖网络且交互打折,商业 EDA 则有授权问题。Qucs-S 是 GPL 的桌面开源电路仿真套件,功能密度合适、纯 C++/Qt 实现,是理想的移植对象。

但它的移植难度也是五个类型里最高的,难点集中在三个地方:

  1. UI 框架不是 Web 也不是 ArkUI,而是 Qt。整个应用的窗口、画布、对话框、菜单全部由 Qt 5.12.12 构建,意味着必须在鸿蒙上解决「Qt 应用如何成为 HarmonyOS 应用」的宿主问题。
  2. 沙箱模型与桌面 Linux 完全不同。Qucs-S 大量依赖 QProcess 启动外部子工具(仿真器、转换器、匹配电路综合器),而鸿蒙沙箱禁止应用自由 exec 子进程——这直接砍掉了原应用的一整条菜单。
  3. 审核要求「每个入口都经得起点击」。桌面软件里一个永远为空的标签页无人介意,审核会把它判定为「功能异常」。所有残留的空壳入口必须逐一清理或隐藏。

三、架构:QAbility 与 Qt 平台插件

Qt 应用在鸿蒙上的宿主模型由「Qt on OpenHarmony」平台插件(libqohos)提供,应用侧要做的是把 Qt 生命周期接进 AbilityKit:

EntryAbility(QAbility)                      ← ArkTS 壳,承接 Ability 生命周期
  └─ qpa.handleAbilityOnCreate(...)         ← 把 ability 上下文交给 Qt 平台插件
       └─ Qt QApplication / 主窗口           ← C++ 世界,原有代码几乎原样
            ├─ MainWindowNativeNode.ets      ← 主窗口原生节点
            ├─ SubWindowNativeNode.ets       ← 子窗口(对话框)
            ├─ FloatWindowNativeNode.ets     ← 浮窗
            └─ UiExtensionNativeNode.ets     ← embeddedUI 扩展挂载点

ArkTS 侧只保留四个 NativeNode 页面和 QAbilityStage,窗口内容全部由 Qt 自绘。module.json5 里 mainElement 指向 QAbility,并注册两个 embeddedUI 类型的 ExtensionAbility,供系统在需要内嵌 UI 的场景(如分屏)拉起 Qt 视图。

这个模型的收益是:413 个 C++ 源文件的图形与业务引擎可以近乎原样重编——画布、元件模型、绘图、对话框都不用改,改动集中在与平台边界相交的那一小圈代码里,用 #ifdef QUCS_OHOS_PORT 隔离,不污染其他平台的构建。

构建产物是单个 .so 约 9.1 MB,加上 Qt 自身的库与资源随 HAP 分发。全量重编由 build_ohos.cmd 一键完成,避免增量编译在跨工具链时产生的陈旧目标文件。


四、审核打回的七处修复:当 QProcess 遇上沙箱

首次提交被审核以「功能异常」打回,复核真机现象后定位出两条根因,最终收敛为七处平台适配修复。这一节是本文最值得移植者细读的部分。

根因一:工具菜单弹「无法启动程序」

qucs_actions.cpp 第 879 行附近的 launchTool() 用 QProcess 启动外部 qucs-* 子工具(匹配电路综合器、转换器等)。在桌面 Linux 上这是常规操作;在鸿蒙沙箱里,exec 系调用被禁止,QProcess 启动必然失败——工具菜单每一项都弹「无法启动程序」,审核判定功能异常。

修复不是「想办法 exec」,而是承认沙箱红线,把菜单收敛到真实可用的能力:

// qucs_init.cpp(#ifdef QUCS_OHOS_PORT 内)
// 工具菜单只保留进程内可完成的两个工具
toolMenu->addAction(callMatchAction);      // 匹配电路综合(进程内实现)
toolMenu->addAction(callConverterAction);  // 文件转换器(进程内实现)
simMenu->setEnabled(false);                // 仿真调度依赖外部模拟器内核
// simulateToolbar 不创建;projMenu 的 buildModule/loadModule 隐藏;
// viewMenu 的 viewOctaveDock 隐藏

凡是「点击后必然失败」的入口,宁可隐藏也不保留——这是上架型移植的铁律。

根因二:「文档」标签页点开无内容

「文档」(Content Tab,当前项目的文件管理器)首启为空,且十个分类里九个永远为空(无仿真数据、无 Octave、无 HDL),双击空分类看起来就像「点不开」。审核的体感是:这个按钮坏了。

修复分两半:

  • 首启给一份真实工程。env_setup.cpp 在图形框架启动前把随包的 demo_amplifier_prj/ 与 demo_amplifier.sch 拷入沙箱工作区。这里有个 Qucs 的冷知识:Qucs 只认以 _prj 结尾的目录为工程,项目列表只显示这类目录,所以示例必须以工程目录形态落位。
  • 永远为空的分类直接删掉。projectView.cpp 的 refresh() 末尾倒序 removeRow,把没有文件的分类从树里移除,每个项目录只留有文件的分类。注意必须倒序删除,正序删除会让 QModelIndex 失效连带删除兄弟行。
// projectView.cpp refresh() 末尾(QUCS_OHOS_PORT)
for (int i = rowCount() - 1; i >= 0; --i) {
  if (categoryIsEmpty(i)) removeRow(i);   // 倒序,索引不失效
}

连带修复:删行引发的错位

删了空分类后,exportSchematic() 原来写死 item(6,0) 按索引定位「原理图」分类的代码就错了位。修复改为按表头文字 tr("Schematics") 动态定位——凡是依赖行号索引的逻辑,删行之后都要改成按内容定位。

修复清单汇总

#现象根因修复
1工具菜单弹「无法启动程序」QProcess 起外部子工具被沙箱禁止工具菜单只留进程内可用的两项
2仿真按钮无效依赖外部仿真调度simMenu 禁用、仿真工具栏不创建
3工程构建入口无效依赖外部工具链buildModule/loadModule 隐藏
4Octave 停靠页空白无 Octave 运行时viewOctaveDock 隐藏
5器件库标签空白无器件库数据LibGroup 不再 addTab
6「文档」点开无内容首启无工程目录首启拷入示例工程 + 删除空分类
7导出原理图错位删行后写死索引失效按表头文字 tr("Schematics") 定位

七处修复全部收敛在 #ifdef QUCS_OHOS_PORT 内,重新出包复提,复审通过。


五、2728 条翻译:一次不进 CMake 的工程化中文化

Qt 的翻译链路是 .ts 源文件 → lrelease 编译 → .qm 运行时资源。坑在于:翻译文件不参与 CMake 构建,鸿蒙侧的构建脚本不会自动编译它们,直接打包 .ts 等于没翻译。

正确做法是用宿主机 Qt 自带的 lrelease 手工编译,再把 .qm 放进 HAP 的 resfile:

lrelease.exe qucs_zh_CN.ts -qm resfile\translations\qucs_zh_CN.qm

翻译内容覆盖 2728 条:菜单、对话框、状态栏,以及上一节提到的工程分类名——Datasets → 数据集、Data Displays → 数据显示页、Schematics → 原理图、Symbols → 符号、Content of %1 → %1 的内容。术语按电子工程教材惯例定名,Verilog / VHDL / SPICE / Octave 等专有名词保留原文。

一个容易被忽略的点:翻译要「趁删完空壳入口之后」再做终稿——否则翻了一堆最终被隐藏的菜单项,工作量白费。顺序应该是:先收敛功能 → 再做翻译 → 最后出包。


六、五项权限的取舍:每一项都要能回答「为什么」

上架应用的权限表是审核必看项。电路芯绘最终保留五项权限,每一项都写了 reason 和 usedScene,并能在审核问答里给出功能对应关系:

权限为什么必须要
READ_PASTEBOARDQt 剪贴板读取(原理图复制粘贴)依赖它,无此权限粘贴功能不可用
FILE_ACCESS_PERSIST用户授权打开过的文件,下次打开同一文件免重复授权
PREPARE_APP_TERMINATE关窗时拦截保存未完成的原理图编辑内容
STORE_PERSISTENT_DATA保存应用设置(QSettings)与工作区目录等本地持久化数据
GET_FILE_ICON打开/保存的系统文件选择器中显示文件图标

值得一提的反而是没有申请什么:没有网络权限(整个应用完全离线)、没有敏感系统能力。Qt 应用默认的权限表往往带着一堆占位空项 {},审核会追问每一项的用途——精简权限表这件事,值一次完整的真机回归。


七、上架资质核对单与真机验收

上架材料的准备,本质是回答「审核员会看到什么、问到什么」。电路芯绘的核对单如下:

材料工程来源要点
包名与版本app.json5com.qucs.circuit,复提包 versionCode 10100000 / 1.1.0 严格递增
签名材料发布 profile 绑定包名调试/发布材料不混用,p12/p7b 全部不入库
权限表module.json55 项权限逐项 reason + usedScene,无占位空项
界面预览图真机截屏主界面、编辑与仿真配置、真机元件放置三张全为实拍,不用渲染效果图
简介文案README 提炼只写可自证的能力(本地离线、桌面级交互、完整中文化),不预支未实现功能
分级与分类工具 / 3+无 UGC、无网络、无账号体系,分级从严从低

真机验收按逐入口走查执行:九个菜单、每个标签与按钮在 HUAWEI MateBook Pro(HarmonyOS 7.0.0,简体中文)上逐一点击,凡无真实响应的入口先修复或隐藏再提包。这条纪律直接来自首次审核的教训——审核环境就是真机 + 简体中文,任何「开发机能跑、真机点不动」的入口都会被打回。


八、上架后:审核驱动的版本迭代

首提 1.0.0 被以「功能异常」打回,修复七处平台适配后以 1.1.0(versionCode 10100000)复提,复审通过上架。这一轮迭代节奏值得记录:

  • 打回 → 定位 → 修复 → 回归 → 复提:审核反馈的两条现象(工具菜单报错、文档页空白)都附带真机环境信息,定位成本远低于黑盒猜测;把「复现路径」问清楚再动手,是审核沟通的第一原则。
  • versionCode 必须递增:复提包 10100000 对应 1.1.0,与商店详情页展示的版本严格对应;只改展示版本号不改 versionCode,升级链路会断。
  • 功能收敛优先于功能补充:这一版的「新增」,是把被砍掉的不可用入口换成了稳定可用的两个进程内工具和一份随包示例工程——上架应用的迭代顺序,永远是先保证「每个入口都真实」,再谈扩展。

后续演进路线(仿真进程内闭环实现「画 → 仿 → 看波形」、器件库与示例模板随包分发、实验报告导出)都建立在这个前提上。


九、构建链路与可复现

整条构建链固化在两个脚本里,无 IDE 也能复现:

1. build_ohos.cmd          # C++ 引擎全量重编(413 文件 → .so 9.1MB)
2. lrelease                 # 翻译 .ts → .qm(手工,不进 CMake)
3. hvigorw assembleHap     # ArkTS 壳 + 资源打包 + 签名

工程里值得注意的细节:

  • entry/libs/arm64-v8a 目录只允许放 .so——任何扫描产物、文本文件都会被资源收集逻辑打进 HAP,曾让包体无故膨胀上百 MB。
  • 签名 profile 与包名 com.qucs.circuit 绑定,调试材料与发布材料不能混用;签名配置中的敏感字段(p12 路径、密码)不入库,.gitignore 排除全部签名材料。
  • 随包资源 resfile/examples/ 放示例原理图,与首启拷贝逻辑对应。

十、踩坑清单(写给下一位移植者)

  1. 沙箱禁 exec 是架构级约束。所有「主程序 + 子工具」形态的 Qt 应用,移植前先盘点 QProcess 的每一处调用,逐一决定:进程内重写、隐藏入口、还是随包带库。审核不会接受「点了弹错误框」的任何入口。
  2. Qt 翻译不进构建系统,必须手工 lrelease,且 .qm 要放进 HAP 可见的资源路径。
  3. Qucs 只认 _prj 结尾目录为工程。示例工程命名错了,首启拷进去也看不见。
  4. 删树节点要倒序,凡是按行号/索引写死的定位逻辑,删行后全部改为按内容定位。
  5. libs/arm64-v8a 只放 .so,构建产物扫描会把杂物打进包体。
  6. 权限表逐项写 reason,空占位 {} 是审核风险点。
  7. 先收敛功能、再翻译、最后出包,顺序反了会浪费翻译工作量。

十一、结语

电路芯绘的上架过程证明了两件事:其一,Qt 这类「重 UI 框架」应用在鸿蒙 PC 上的移植是成立的,前提是接受平台插件 + Ability 宿主模型,并把改动严格圈在平台边界;其二,应用市场审核对「功能完整性」的判定标准与桌面软件的传统完全不同——桌面软件的空菜单是「尚未实现」,应用市场里的空菜单是「功能异常」。

移植的上半场是让程序跑起来,下半场是让每一个入口都经得起点击。这两场都打完,货架上的那个「安装」按钮才真正兑现。

源码与构建说明见开源仓库;本文所有代码摘录均来自工程实际提交。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐