欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/

欢迎在 PC 社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

开源地址(AtomGit 仓库):https://atomgit.com/OpenHarmonyPCDeveloper/ohos_PSPP.git

开源组织(AtomGit):https://atomgit.com/OpenHarmonyPCDeveloper


统计分析软件和数据库客户端一样,是科研、教学与业务分析每天都在用、却很少被当成「基础设施」的基础设施。本文分享「格物统计」(based on GNU PSPP):它在鸿蒙 PC 上用 ArkTS + ArkUI 重建了整套图形界面与交互,把统计计算放进一个由 OHOS NDK 编译的 C++ native 内核,再通过 NAPI 把两端接起来。文章侧重技术实现——为什么不能直接编译上游、架构怎么分层、数据层与 NAPI 边界怎么设计、CSV / XLSX / SAV 怎么读写、图表与 PDF 怎么做到三端刻度一致,以及 388 项数值断言是怎么来的。
在这里插入图片描述


一、项目定位:原生重建,不是把 GTK 塞进壳里

上游 GNU PSPP 是一套成熟的统计分析软件,核心代码二十余万行、定义了一百四十多条命令。但它的运行时依赖很重:GLib、GTK、GSL、Cairo、libxml2 一个都不少。鸿蒙 PC 上没有 GTK 运行时,也没有现成的 GSL 交叉编译产物,把上游 C 源码直接搬过来编译这条路基本走不通。

所以这里没有走「翻译源码」或「套壳运行」的路线,而是做了一个明确的取舍:

  • 界面与交互:用 ArkTS + ArkUI 全新实现,遵循桌面工作台的操作习惯;
  • 统计内核:以上游的算法、公式、数据结构与文件格式定义为参照,用 C++ 重写为 libpsppcore.so;
  • 跨语言边界:只保留一个 NAPI 桥接层,负责参数解析、JSON 序列化与进程内数据集管理;
  • 交付形态:所有东西打成一个 HAP,装上即用,不存在独立进程、HNP、终端命令或外部服务。

它不是「把 Linux 程序塞进鸿蒙壳里跑」,而是一个纯原生 GUI 应用:界面、事件循环、状态管理全部是 ArkUI 声明式范式,统计计算是 HAP 内的 native 库。默认不申请任何权限、不发起网络请求,数据只在本地读取与计算。

项目的四个目标:

  1. 平台补位:为鸿蒙 PC 提供一套可日常使用的图形化统计分析工具;
  2. 原生体验:桌面级三栏布局、菜单栏 / 工具栏 / 状态栏,键鼠优先,同时保持触控可用;
  3. 对齐核心流程:覆盖「数据导入 → 变量定义 → 数据转换 → 统计分析 → 图表 → 结果归档 → 报告导出」全链路;
  4. 结果可核验:统计数值由 native 内核真实计算,并与 numpy / scipy 参考值逐项对拍,不用 mock 数据。
层次技术
UIArkTS + ArkUI 声明式范式,pages / view / common 分层
领域逻辑ArkTS:文件选择、分页取数、分析参数、结果归档、导出
跨语言桥接NAPI(C++):统一导出函数 + JSON 字符串 + ArrayBuffer
统计内核C++17:core / io / stats / math / syntax / output
数值基础自研 math/:矩统计、分布函数、矩阵、特征分解、聚类
构建DevEco Studio + hvigor;native 层 CMake + OHOS NDK 工具链

功能特性(主线):

  • 数据管理:CSV / TXT / TSV / XLSX 导入,SAV 读写;单元格编辑、变量增删改、个案增删、排序、筛选;
  • 数据转换:计算变量、重编码、自动重编码、计数、秩变换、分组聚合、按权重展开个案、分组分析,共 13 项;
  • 统计分析:描述统计、频数、交叉表、相关、T 检验、方差分析、线性回归、二元 Logistic 回归、探索性分析、分组均值、信度、非参数检验、因子分析、聚类分析,共 19 项分析过程;
  • 语法:核心命令解析与执行,GUI 与语法共用同一统计引擎;
  • 图表:柱状图、折线图、饼图、直方图、散点图、箱线图;
  • 输出:统一结果查看器,HTML / CSV / SVG / PNG / PDF 导出。

功能矩阵中列出的 44 项功能全部落地;统计内核的 388 项断言与 numpy / scipy 参考值对拍通过。


二、架构分层与工程结构

整个系统的分层很干净:ArkTS 管文件、界面与渲染,C++ 只管内存中的数据集与统计计算,NAPI 做桥。

┌──────────────────────────────────────────────────────────────┐
│  ArkTS / ArkUI 表现层                                        │
│  pages/   MainWindowPage —— 菜单栏 / 工具栏 / 导航 / 状态栏  │
│  view/    DataGrid · VariableView · AnalysisPanel ·          │
│           SyntaxEditor · OutputViewer · ChartRenderer        │
│  common/  PsppCore · FileService · XlsxService/Parser ·      │
│           OutputExport · PdfExport · ChartMath · Theme       │
└───────────────────────────┬──────────────────────────────────┘
                            │ NAPI(libpsppcore.so)
                            │ JSON 字符串 + ArrayBuffer
┌───────────────────────────▼──────────────────────────────────┐
│  Native 统计内核(entry/src/main/cpp)                        │
│  napi_init.cpp   参数解析 / JSON 序列化 / 全局 Dataset        │
│  core/           Dataset / Variable:列式存储、筛选、排序     │
│  io/             data_io(CSV/TXT)· sav_io(.sav 读写)       │
│  stats/          engine:15 类过程实现(19 个分析入口)      │
│  math/           矩统计 · 分布/特殊函数 · 矩阵 · 特征分解 ·   │
│                  回归 · Logistic · 因子 · 聚类 · 顺序统计     │
│  syntax/         词法 / 递归下降 / 表达式求值                 │
│  output/         AnalysisResult(Table[] + Chart[])          │
└──────────────────────────────────────────────────────────────┘

对应的工程目录:

PSPP/
├── entry/src/main/
│   ├── ets/                         # ArkTS / ArkUI 表现层
│   │   ├── entryability/            # EntryAbility(PC 窗口适配)
│   │   ├── pages/                   # MainWindowPage 主窗口
│   │   ├── view/                    # 数据网格 / 变量视图 / 分析面板
│   │   │                            # 语法编辑器 / 结果查看器 / 图表渲染
│   │   └── common/                  # NAPI 封装 / 文件服务 / XLSX
│   │                                # HTML、SVG、PDF 导出 / 主题 / 类型
│   └── cpp/                         # C++ native 统计内核
│       ├── napi_init.cpp            # 唯一的跨语言边界
│       ├── core/                    # Dataset / Variable
│       ├── io/                      # CSV、TXT、SAV
│       ├── math/                    # 数值基础(替代 GSL)
│       ├── stats/                   # 15 类过程实现(19 个入口)
│       ├── syntax/                  # 语法解析与表达式求值
│       ├── output/                  # 统一结果模型
│       └── CMakeLists.txt           # libpsppcore.so
├── tests/                           # 宿主端测试、测试数据与报告
├── third_party/                     # 上游源码快照(仅存档,不参与构建)
├── LICENSE · SOURCE_NOTICE.md · THIRD_PARTY_NOTICES.md
└── README.md

当前源码规模大约是 1 万行 C++ native 内核 + 7.4 千行 ArkTS 表现层。这个比例说明一件事:统计软件的工作量并不只在「页面上有几个按钮」,大量成本在数值算法的正确性、文件格式的兼容性和跨语言数据边界上。

这条路线最大的好处是双向隔离:

  • native 内核不依赖任何 OHOS 平台 API,只依赖 C++ 标准库,因此可以在开发机上直接编译同一套源码做对照测试;
  • ArkTS 侧只面对 PsppCore 这一层强类型封装,不感知 native 句柄、内存与序列化细节;
  • 换统计过程或扩命令,主要改 stats/ 与 syntax/;改界面则完全不触碰数值层。

三、桌面级工作台:界面不是「手机应用放大版」

应用启动后是一个 1440×900 的桌面窗口,deviceTypes 同时声明通用设备与 2in1,PC 上安装即按桌面形态呈现。布局是典型的统计软件工作台:

  • 顶部:文件 / 编辑 / 数据 / 分析 / 图表 / 工具 / 帮助 七个菜单;
  • 工具栏:导入 CSV/TXT、导入 Excel、打开 SAV、导出 CSV、保存 SAV、追加行、删除末行、升序 / 降序、描述统计快捷入口、结果;
  • 左侧:数据 / 变量 / 分析 / Syntax / 结果 五个导航页;
  • 底部:状态栏,实时显示数据集规模、筛选状态与操作结果。

1. 数据视图

数据视图是电子表格式网格:列头带类型标注,支持单元格就地编辑、追加行、删除末行、按选中列升降序排序,以及每列一个筛选条件的快速筛选。状态栏会提示当前是全部个案还是筛选后的可见个案。

在这里插入图片描述

2. 变量视图

变量视图集中管理数据字典:变量名、数值类型、宽度、小数位、标签、测量尺度与值标签。导入 CSV 时会自动推断类型与格式,变量视图里的修改会即时同步到数据页。

在这里插入图片描述

3. 数据转换

「数据」菜单集成了 13 项预处理能力。以「计算变量」为例:填目标变量名与表达式,确定后新列即时写入数据表,状态栏提示影响的个案数与变量名。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 分析面板

「分析」页顶部下拉提供 19 项统计分析过程,按家族分组:描述与频数、比较均值、相关与回归、量表、非参数检验、降维与分类。统一交互模型是「选过程 → 勾变量 → 可选统计量/选项 → 运行分析」,结果自动进入结果页目录。

在这里插入图片描述
在这里插入图片描述

5. 结果与导出

结果页左侧按时间列出全部分析条目,右侧渲染统计表与图表。工具条提供导出 HTML 报告、导出 PDF 报告、导出当前 CSV、复制文本;单图还支持 SVG / PNG。

在这里插入图片描述


四、数据层:列式存储 + 可见行映射

统计软件的数据层有两个硬约束:一是要能扛住十万到百万行,二是筛选、排序、编辑不能把整表反复拷贝。Dataset 的设计围绕这两点展开。

4.1 列式存储与缺失值

数值列用 std::vector<double>,字符串列用 std::vector<std::string>,按列连续存放。这样做的好处是统计过程按列取值时局部性好,也避免了「一行一个对象」带来的指针跳转。

class Dataset {
public:
  int variable_count() const;
  const Variable& variable(int idx) const;

  // case_count() 返回"当前可见个案数":无筛选时等于全部个案数,
  // 启用筛选后仅统计未被排除的个案。所有行号参数均为可见行下标。
  int case_count() const { return static_cast<int>(visible_.size()); }
  int total_case_count() const { return n_cases_; }

  void set_filter_kept(const std::vector<bool>& keep);
  void clear_filter();
  int case_index(int row) const;   // 可见行下标 -> 实际个案下标

  double get_num(int row, int col) const;
  std::string get_str(int row, int col) const;
  void set_cell_from_text(int row, int col, const std::string& text);
  bool cell_is_missing(int row, int col) const;

  std::vector<double> valid_numeric(int col) const;
  void sort_cases(const std::vector<int>& cols, const std::vector<bool>& ascending);

private:
  std::vector<Variable> vars_;
  std::vector<std::vector<double>> num_cols_;
  std::vector<std::vector<std::string>> str_cols_;
  int n_cases_ = 0;
  std::vector<int> visible_;          // 可见个案的实际下标序列(升序)
  std::vector<bool> filter_kept_;
  bool filter_active_ = false;
};

缺失值统一用 SYSMIS 哨兵表示,字符串缺失用空串。valid_numeric() 返回非缺失序列,统计过程只需要关心「有效值」,不用在每个公式里重复判断缺失。变量元数据 Variable 则带着名称、标签、类型、宽度、小数位、格式、测量尺度、值标签与离散缺失值——这些信息既支撑变量视图,也支撑 SAV 读写。

4.2 筛选不重建数据,只重建映射

筛选是最容易把实现写「重」的地方。这里没有把符合条件的行拷贝成新表,而是维护一个 visible_ 数组:visible_[row] 给出可见行对应的实际个案下标。筛选时只重建映射,统计与分页都基于这层映射。

setFilter(col, op, value)
  → 逐行计算 keep[]
  → Dataset::set_filter_kept
  → rebuild_visible()
  → 返回 { totalRows, filtered, kept }

这样做的好处是:

  • 十万行数据筛选不会触发一次全表拷贝;
  • 取消筛选只需清空标记并重建映射;
  • 统计过程看到的 case_count() 天然就是「筛选后的样本量」。

删除与插入个案时也做了增量维护:插入位置之后的可见下标整体后移一位,删除时整体前移一位。排序只重排「可见个案占用的槽位」,被筛除的个案保持原样且不参与排序——这符合统计软件「只对当前样本排序」的语义。

4.3 分页取数

UI 不一次性拿全表。getPage(start, count) 返回当前页的二维文本数组与总数,ArkTS 侧只持有当前页。这样即使数据集很大,界面也不会因为一次巨大的 JSON 序列化而卡死。

{
  "start": 0,
  "total": 100000,
  "rows": [["18", "85", "103"], ["21", "76", "97"]]
}

分页、筛选、排序三条路径都收敛在 Dataset 上,统计过程读到的始终是同一份内存数据。


五、NAPI 边界:把 C++ 内核变成 ArkTS 可调用的对象

5.1 一个全局数据集,一组导出函数

napi_init.cpp 是唯一的跨语言边界。它维护一个进程内全局 Dataset,把 ArkTS 传进来的参数解析成 C++ 值,调用 stats/engine 或 io/,再把结果序列化成 JSON 字符串返回。

Dataset g_dataset;

napi_value LoadText(napi_env env, napi_callback_info info) {
  // 解析文本与导入选项
  ImportResult r = import_delimited(g_dataset, text, opt);
  if (!r.ok) return status_err(env, r.error);
  return status_ok(env, "\"delimiter\":" + json_string(std::string(1, r.delimiter_used)));
}

导出接口按职责分成几组:

分类接口
数据集newDataset loadText loadSav saveSav exportText
查询getVariables getDatasetInfo getPage uniqueValues
编辑setCell addVariable deleteVariable renameVariable setVariableProps insertCase deleteCase sortCases
筛选setFilter clearFilter
统计descriptives frequencies crosstabs correlations ttestOneSample ttestIndependent ttestPaired oneway regression logistic explore means reliability factor cluster nparChisquare nparMannWhitney nparKruskalWallis nparWilcoxon
数据变换aggregate count autorecode rankVar weightExpand
语法runSyntax

返回约定很统一:普通操作返回 PsppStatus(ok / error / rows / cols),单次分析返回 PsppResult(表格 + 图表 + 元数据),语法批量执行返回 PsppSyntaxRun,分页返回 PsppPage,.sav 则走 ArrayBuffer。

5.2 ArkTS 侧强类型封装

PsppCore.ets 用 index.d.ts 的类型声明,把每个导出函数包成静态方法。上层不感知 NAPI 细节,只拿到 TypeScript 接口。

function parseResult(json: string): PsppResult {
  try {
    return JSON.parse(json) as PsppResult;
  } catch (e) {
    return {
      command: '', title: '解析失败', description: '', ok: false,
      error: '内核返回结果解析失败', tables: [], charts: [], metadata: {}
    };
  }
}

export class PsppCore {
  static descriptives(vars: number[], stats: string[]): PsppResult {
    return parseResult(psppcore.descriptives(JSON.stringify(vars), JSON.stringify(stats)));
  }

  static getPage(start: number, count: number): PsppPage {
    try {
      return JSON.parse(psppcore.getPage(start, count)) as PsppPage;
    } catch (e) {
      return { start: 0, total: 0, rows: [] };
    }
  }
}

这里的取舍很明确:用 JSON 字符串做 ABI,而不是在 NAPI 层构造复杂的 JS 对象图。

  • 好处是边界简单、调试直观、C++ 侧不需要维护一整套 napi_value 构造代码;
  • 代价是多一次序列化 / 反序列化,但通过分页把单次数据量控制住,实际体感可以接受;
  • 出错时 ArkTS 侧统一降级为空结果并提示,不会因为一次解析失败把页面搞崩。

.sav 是例外:它是二进制格式,走 ArrayBuffer 直接传字节,避免把二进制塞进 JSON。


六、文件格式:CSV / XLSX / SAV 三条输入路径

格式读取写入实现要点
CSV / TXT / TSV支持支持自动分隔符探测、引号包裹、"" 转义、类型推断、缺失标记
XLSX支持—ZIP 解压 + 共享字符串 / 工作表 XML → CSV → 复用文本导入管线
SAV支持支持176 字节文件头、字典记录、值标签、压缩模式 0/1/2

6.1 CSV:先猜分隔符,再逐行解析

分隔符探测取前 5 行,在 ,、\t、;、| 四个候选里逐个统计「每行字段数的分布」,选「字段数一致的行最多、字段数最多」的那个。统计时会跳过双引号内部的候选分隔符,避免把带逗号的文本字段误判成多列。

候选分隔符 d
  → 对每一行统计字段数 n(引号内不计)
  → 取出现次数最多的字段数 agree 与对应 fields
  → score = agree * fields
  → 选 score 最大的 d

确认分隔符后,逐行解析支持双引号包裹、"" 转义、CRLF / LF / 单独 CR 三种换行,以及可配置的缺失值标记。变量名会被清洗成合法标识符,数字列与字符串列在装载时自动推断。

6.2 XLSX:解压后回到同一条导入管线

XLSX 本质是一个 ZIP 包。XlsxService 用 @ohos.zlib 把文件解压到缓存目录,XlsxParser 解析 xl/sharedStrings.xml 与首个工作表 XML,把单元格还原成二维文本表,再由 rowsToCsv 转成 CSV,最后交给 native 的 loadText。

这条路径的关键设计是不产生第二套数据装载逻辑:XLSX → CSV → import_delimited,与普通 CSV 导入走完全相同的类型推断、变量名清洗与缺失处理。XlsxParser 本身是纯函数(只做 XML 文本 → 二维数组),因此可以在宿主端直接单测,不需要跑模拟器。

6.3 SAV:自包含的二进制编解码

.sav 是统计软件的系统文件格式,结构比 CSV 复杂得多:176 字节文件头、变量字典记录、值标签、文档记录、扩展记录、数据区。sav_io 没有依赖 zlib,而是内置了一个 DEFLATE 解压器(RFC 1951 + RFC 1950 封装),因此压缩模式 2 的文件也能直接读。

三种压缩模式的处理:

模式含义处理
0无压缩直接按 case 字节布局读取
1bytecode 压缩按 opcode 还原:0 补齐、252 EOF、253 8 字节字面量、254 8 个空格、255 SYSMIS,其余为数值编码
2zlib 压缩内置 inflate(stored / fixed / dynamic Huffman)解压后按 case 读取

超长字符串按 252 / 255 字节分段处理;变量标签、值标签、测量尺度、缺失值都从字典记录还原到 Variable 元数据。写入侧当前输出无压缩 .sav,但结构完整,可被上游工具回读。


七、图表与报告:同一套刻度,三种输出

统计结果里,图表最怕「屏幕上一个样、导出另一个样」。这里的做法是把坐标轴计算抽成纯函数 ChartMath.ets,Canvas 渲染、SVG 导出、PDF 导出三处共用。

export function niceAxis(min: number, max: number, tickCount: number): AxisRange {
  const range = max - min;
  const rawStep = range / Math.max(1, tickCount);
  const mag = Math.pow(10, Math.floor(Math.log10(rawStep)));
  const norm = rawStep / mag;
  let step: number;
  if (norm <= 1) step = 1 * mag;
  else if (norm <= 2) step = 2 * mag;
  else if (norm <= 5) step = 5 * mag;
  else step = 10 * mag;

  const niceMin = Math.floor(min / step) * step;
  const niceMax = Math.ceil(max / step) * step;
  // 生成 ticks……
  return { min: niceMin, max: niceMax, ticks };
}

支持六类图表:柱状图、折线图、饼图、直方图、散点图、箱线图。图表数据由 native 侧以 JSON 形式带出(categories / series / points / boxes),ArkTS 侧只负责绘制。

7.1 自包含 PDF 1.4 生成器

PDF 导出没有引第三方库,而是直接生成 PDF 1.4 字节流:页面 595×842(A4)、对象表、xref、trailer、startxref。文字走两套字体:

  • 纯 ASCII 用标准 Type1 Helvetica / Helvetica-Bold;
  • 含中文用 Type0 复合字体 + STSong-Light(UniGB-UCS2-H 编码,Adobe-GB1)。

这两套字体都是 PDF 规范里的标准字体 / 预定义 CMap,不需要内嵌字体文件,主流阅读器都能正常显示。表格是矢量网格,图表是矢量绘制,分页由 ensureSpace 控制。

7.2 HTML / CSV / PNG / SVG

  • HTML 报告:完整还原全部表格与图表,散点图以 SVG 内嵌,可直接在浏览器打开;
  • CSV:当前表格或全部结果表导出;
  • SVG:数据驱动矢量图,适合放进论文与汇报;
  • PNG:由 Canvas.toDataURL 落盘。

八、构建、测试与能力边界

8.1 环境与构建

项目要求
IDE / SDKDevEco Studio(含 HarmonyOS SDK)
Native 工具链OHOS NDK(clang / CMake),用于编译 arm64 native 内核
宿主端测试(可选)clang++、python3、Node.js(XLSX / PDF 用例)
目标设备2in1 PC 与通用设备(deviceTypes 同时声明)

命令行构建:

# 使用 DevEco Studio 自带 hvigorw
HVIGOR=/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw
export HVIGOR_USER_HOME="$PWD/.hvigor-home"
"$HVIGOR" assembleHap --mode module -p product=default

产物是签名或未签名的 HAP,native 内核 libpsppcore.so 随包分发,设备上不需要预装任何统计库。

8.2 测试

宿主端在开发机上直接编译同一套 native 源码(排除 NAPI 绑定)做对照测试:

bash tests/run_all_tests.sh
用例内容结果
统计内核对照测试388 项断言:描述 / 频数 / 交叉表 / 相关 / T / ANOVA / 回归 / Logistic / 探索 / 分组均值 / 信度 / 非参数 / 因子 / 聚类 / 数据变换 / 排序筛选 / CSV·SAV 回环 / 语法 / 10 万行PASS
SAV 编解码回环由 CSV 生成 .sav 并逐单元格回读比对PASS
XLSX 解析解析真实 xlsx,与期望 CSV 逐字节对拍PASS
PDF 导出内置结构校验(xref / 页数 / 中文可还原),可选 qpdf --checkPASS

这套测试能成立的前提是 native 内核不依赖 OHOS 平台 API——它只依赖 C++ 标准库,所以开发机上用 clang++ 就能编译,测试不需要模拟器、不需要 HDC,也不需要每次打包。

8.3 当前能力状态(T0 / T1 / T2)

T0 基础能力(能不能编、能不能装、能不能起)——已完成:native 内核交叉编译通过;HAP 构建通过;2in1 设备形态与 1440×900 主窗口适配完成;默认不申请权限、不联网。

T1 主要能力——已完成:数据管理、13 项数据转换、19 项统计分析、6 类图表、语法编辑器、HTML / PDF / CSV / PNG / SVG 导出全部落地;功能矩阵列出的 44 项功能 100% 覆盖;388 项断言对拍通过。

T2 待办与取舍:原版 PSPP 定义了 141 条命令,本版覆盖核心统计与数据变换命令,GLM、CTABLES、宏等长尾命令尚未实现;.por(portable)与 .spv(输出)格式未支持;语法自动补全、输出树右键菜单等 GUI 辅助功能还未补齐。这些属于后续迭代项,不影响核心分析链路。

需要如实说明的边界:测试覆盖的是统计内核的数值与格式正确性,不等于所有 UI 路径都已被自动化覆盖;界面交互仍以人工验证与截图记录为准。


九、开源与合规

  • 本项目是 GNU PSPP 的移植衍生作品,整体以 GPL-3.0-or-later 分发;
  • 运行时只依赖 HarmonyOS SDK 与标准 C++ 运行库,未链接 GSL / GLib / GTK / Cairo / libxml2;
  • 统计内核保留了上游的算法、公式、数据结构与文件格式定义,但代码是面向 OHOS NDK 的重新编写;
  • 仓库内提供 LICENSE、SOURCE_NOTICE.md、THIRD_PARTY_NOTICES.md,说明源码来源、修改范围与获取方式。

十、总结

格物统计把「原生重建」这条路走通了:界面与领域逻辑用 ArkTS / ArkUI 实现,统计计算放进 C++ native 内核,NAPI 做桥,最终打成单一 HAP。它不是把桌面程序塞进兼容层,而是一套从头组织的鸿蒙 PC 桌面应用。

回头看有三点经验值得同类项目借鉴:

  • 数值层与界面层必须解耦:统计内核只依赖 C++ 标准库,才能享受宿主端直接编译、直接对拍的红利;
  • 跨语言边界越简单越好:一组导出函数 + JSON 字符串 + 少量 ArrayBuffer,比在 NAPI 层维护复杂对象图更容易长期维护;
  • 能力状态要按用户路径如实标注:能启动不等于能分析,界面出现不等于结果正确——T0 / T1 / T2 与 388 项断言的划分,就是为了把「已验证的」与「待完成的」说清楚。

开源鸿蒙 PC 需要的正是这类日常基础设施软件。无论是统计命令的扩展、长尾格式的兼容,还是桌面交互的打磨,都欢迎参与共建。

开源地址(AtomGit 仓库):https://atomgit.com/OpenHarmonyPCDeveloper/ohos_PSPP.git

Logo

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

更多推荐