把 Jupyter Notebook 搬到鸿蒙 PC:同栈双胞胎的 Notebook-first 移植实战
把 Jupyter Notebook 搬到鸿蒙 PC同栈双胞胎的 Notebook-first 移植实战欢迎加入开源鸿蒙 PC 社区https://harmonypc.csdn.net/欢迎在 PC 社区平台申请新建项目https://atomgit.com/OpenHarmonyPCDeveloper适配开源地址https://atomgit.com/OpenHarmonyPCDeveloper/ohos_jupyterNotebook写在前面这是最有意思的一篇——因为它不是从零开始移植一个应用而是从已经跑通的姊妹工程里分叉出另一个产品形态。Jupyter 生态里有个特殊现象Notebook 7 的前端就是 JupyterLab 组件构建的。上游jupyter/notebook从 7.0 开始放弃了老nbclassic的 jQuery 代码库改为复用 Lab 的 React/Phosphor 前端。这意味着如果你已经把 JupyterLab 移植到了鸿蒙 PC你就已经顺手完成了 Jupyter Notebook 90% 的移植工作——剩下的是产品身份分叉和 UI 形态收敛。任务清单要求两个产品分列交付、独立安装、独立评分所以这次的核心问题不是「能不能跑」Lab 已经验证而是「怎么把一套已验证的技术栈切成两个能共存、可独立演进的交付物」。一、为什么 Notebook 7 是「最容易的硬骨头」先看清楚上游关系jupyter/notebook (Notebook 7) │ 前端复用 ▼ jupyterlab/jupyterlab (Lab 组件库) │ OHOS 适配已验证姊妹工程 ohos_JupyterLab ▼ OpenHarmony Electron (libelectron.so) node-static 浏览器内 PythonNotebook 7 Lab 前端 single-document 外壳。上游用pageConfig控制产品形态配置JupyterLab 产品Jupyter Notebook 产品pageConfig.modemultiple-documentsingle-document启动 URL/labLauncher 多文档入口/lab/tree/Untitled.ipynb直接打开笔记本Launcher 卡片页有禁用左侧文件浏览器有禁用TOC 侧栏有禁用bundleNameorg.jupyter.lab.ohosorg.jupyter.notebook.ohos环境变量前缀JUPYTERLAB_*NOTEBOOK_*兼容JUPYTERLAB_*所以移植策略非常清晰fork 姊妹工程 → 改产品身份配置 → 保持技术栈完全复用。二、从 Lab 分叉bootstrap 脚本做了什么两个仓库之间用一个 bootstrap 脚本同步 runtime约 2000 行 Electron 主进程代码不重复维护nodescripts/bootstrap-from-notebook.mjs# Notebook → 姊妹方向nodescripts/bootstrap-from-lab.mjs# 反向Lab 仓库持有源脚本做的字符串/配置级重写org.jupyter.lab.ohos → org.jupyter.notebook.ohos productUi: lab → productUi: notebook /labLauncher 入口 → /lab/tree/Untitled.ipynb JUPYTERLAB_*优先 → NOTEBOOK_*优先JUPYTERLAB_* 兼容 hnpPackages type: private → type: public ← 关键差异见 §四工程判断这种「同栈双产品」的架构下bootstrap 脚本就是唯一的真相源。所有产品身份相关的东西都集中在这一个脚本里避免两个仓库手工 drift。三、架构和 Lab 完全同栈只有入口不同三种服务模式与 Lab 完全一致模式环境变量场景node-static默认NOTEBOOK_OHOS_SERVER_MODEnode-static静态前端 浏览器内内核零原生依赖pythonNOTEBOOK_OHOS_FORCE_PYTHON1设备上跑python -m jupyterlab需 aarch64 发行版externalNOTEBOOK_EXTERNAL_SERVER_URL…连接已有 Jupyter Server为什么默认 node-static 而不是 python 模式pyzmq/libsodium 在设备侧仍然脆弱官方文档原话交叉编译一个能跑 Jupyter Server 的 Python 环境的工程成本远超「静态前端 浏览器内解释器」的方案。这是整个 Jupyter 双胞胎移植里最重要的一个工程决策。四、HNP 双胞胎共存机制本次最大坑两个产品都打包了一个 HNP 原生包jupyterlab_python.hnp用于可选的原生 Python 能力。第一次装 Notebook 时直接翻车Install Failed: code:9568407 Failed to install the HAP because installing the native package failed.hilog 关键线索ProcessBundleInstallNative … hnp install: electron [HNP API] native package install! … package nameorg.jupyter.notebook.ohos already exist cfg ignore … MSG_ERR_NATIVE_INSTALL_FAILED根因HNP 包有public/private两种作用域类型作用域冲突规则public设备全局唯一同名 public 包只能装一个private单应用沙箱每个应用各持一份互不干扰两个产品声明了同名jupyterlab_python.hnp如果都是 public先装的占坑后装的报 9568407。修复方案不对称设计// Notebook先发布占 public 坑 hnpPackages: [ { package: jupyterlab_python.hnp, type: public } ] // Lab后发布用 private 共存 hnpPackages: [ { package: jupyterlab_python.hnp, type: private } ]改完必须重新assembleHap——模块元数据是烘焙进 HAP 的热改无效。方法论任何「同栈双胞胎」交付都要在 HNP 这一层显式设计共存策略且不对称一个 public 一个 private是最稳的——对称 public 会互相踢对称 private 浪费设备空间且语义不明。五、真机验收六个功能点逐一过设备HUAWEI MateBook ProHarmonyOS 7.0.0。5.1 启动直达笔记本single-document 的核心体验这就是 Notebook-first 与 Lab-first 的用户可感知差异打开就是笔记本光标在 cell 里可以直接打代码。Click to add a cell.提示、Mode: Command/Edit状态切换、Ln x, Col y行列号全部工作。5.2 Cell 执行与返回值显示注意[3]: None这一行——这是 Jupyter 对表达式返回值的自动回显print()返回 None。这个细节存在说明 Skulpt 内核的执行协议和前端的消息通道是完整实现的不是简单地把 stdout 打到页面上。5.3 菜单完整度20 菜单项、快捷键提示、二级菜单全部渲染正确。右下角还能看到系统任务栏BOSS 直聘、邮件、抖音——这是真机全屏截图不是模拟器。5.4 键盘快捷键帮助弹窗、滚动、关闭交互全部正常——Lab 前端的 Modal 组件在 OHOS Electron 上没有降级。5.5 ASCII 艺术字输出多行字符串渲染值得记录的一个细节这些 box 字符╔═╗║╚╝在r...raw 三引号字符串里会触发 Skulpt 词法器 bugSyntaxError: bad input改用普通...三引号或分行print()就完全正常。截图里的输出就是修复后的结果——这是 Skulpt 内核的边界不是平台问题。5.6 无 matplotlib 环境下的数据可视化本文最有价值的一节鸿蒙 PC 的 Jupyter 双胞胎默认内核是 Skulpt没有 matplotlib / numpy。但数据可视化需求是真实的——教学场景画个函数曲线、演示趋势不能没有。解法是纯标准库 ASCII 折线图完整代码约 40 行纯math 列表推导Skulpt 100% 兼容importmath# 1. 数据xs[i*0.4foriinrange(24)]ys[math.sin(x)*3x*0.25forxinxs]# 2. 画布W,H64,18min_y,max_ymin(ys),max(ys)min_x,max_xmin(xs),max(xs)defto_col(x):returnint((x-min_x)/(max_x-min_x)*(W-2))1defto_row(y):returnint((y-min_y)/(max_y-min_y)*(H-2))# 3. 画布初始化 坐标轴canvas[[ for_inrange(W)]for_inrange(H)]forrinrange(H):canvas[r][0]│forcinrange(W):canvas[H-1][c]─canvas[H-1][0]└# 4. 描点 插值连线foriinrange(len(xs)):colto_col(xs[i])rowH-1-to_row(ys[i])canvas[row][col]●ifi0:prev_colto_col(xs[i-1])spancol-prev_colifspan1:forcinrange(prev_col1,col):t(c-prev_col)*1.0/span yys[i-1](ys[i]-ys[i-1])*t canvas[H-1-to_row(y)][c]·# 5. 峰谷标注peak_iys.index(max(ys))low_iys.index(min(ys))canvas[H-1-to_row(ys[peak_i])][to_col(xs[peak_i])]▲canvas[H-1-to_row(ys[low_i])][to_col(xs[low_i])]▼# 6. 输出print(y_max {:.2f}.format(max_y))forrowincanvas:print(.join(row))print(y_min {:.2f} x: {:.1f} - {:.1f}.format(min_y,min_x,max_x))几个工程要点刻意不用 f-string部分 Skulpt 版本支持不稳全部.format()刻意不用 raw stringr是词法坑box 字符只出现在普通短字符串里插值连线拆成span/t/y三个中间变量避免长链表达式触发解析器边界[i]: None返回值回显、错误行号定位bad input on line N都正常工作这套「降级可视化」思路适用于任何没有图形栈的嵌入式 Python 环境——Skulpt、MicroPython、受限容器都是同一个问题域。六、双胞胎工程的坑位对照这次 Notebook 分叉把 Lab 踩过的坑原样再踩一遍但每个坑的解法都能直接复用成本大幅下降坑Lab 首次解决成本Notebook 复用成本CompileArkTS 10705000import lazy排查半天定位到compatibleSdkVersionStage: beta10bootstrap 直接钉住6.0.1(21)SignHap 00303074profile 绑 bundle摸清 debug profile 的 bundle 绑定机制5 分钟DevEco Fix 一次Install 9568320signingConfig: 半小时发现 DevEco 不回填 products 引用5 分钟知道看产物文件名Install 9568407HNP 冲突理解 public/private 作用域0bootstrap 写好publicresfile 放错模块白屏半天0脚本生成正确路径这就是「同栈双胞胎」模式的最大红利第一个产品的踩坑成本是沉没成本第二个产品的边际成本趋近于零。反过来如果两个产品各自独立移植这些坑要踩两遍。七、复现命令cdohos_JupyterNotebook# 1. 从 Lab 工程同步 runtime首次必做nodescripts/bootstrap-from-lab.mjs# 或按仓库实际脚本名# 2. 打包 runtime 到 web_engine resfilenodepkg/ohos/build-package.mjs# 3. 构建 HAPnodepkg/ohos/build-hap.mjs# 跳过 hvigor 仅打包资源# NOTEBOOK_OHOS_SKIP_HVIGOR1 node pkg/ohos/build-hap.mjs# 4. DevEco 自动签名 → 检查 products[].signingConfig default# 5. 安装 启动hdcinstall-relectron/build/default/outputs/default/electron-default-signed.hap hdc shell aa start-aEntryAbility-borg.jupyter.notebook.ohos环境变量速查NOTEBOOK_PORT8888、NOTEBOOK_TOKEN默认随机、NOTEBOOK_ROOT_DIRuserData/notebooks、NOTEBOOK_DISABLE_GPU1。八、给「同栈多产品」交付的方法论这套模式适用于所有「一个技术栈要交付多个产品形态」的场景IDE 的社区版/专业版、浏览器的稳定版/测试版、办公套件的多个组件先跑通一个再分叉第二个——第一个产品的踩坑日记就是第二个产品的施工图bootstrap 脚本是唯一真相源——产品身份的所有差异集中在一个可 review 的 diff 里禁止手工双仓库漂移HNP/权限/签名按 bundle 隔离——debug profile 绑 bundleNameHNP 设计不对称共存public/private一个都别复用UI 形态差异交给上游配置——pageConfig.mode这种官方开关比魔改前端代码稳定一个数量级降级路径先于完整路径——node-static 浏览器内内核先落地原生 Server 后补能跑的 60 分比跑不起来的 100 分有价值受限环境的可视化用 ASCII——没有图形栈时40 行纯标准库代码就能覆盖教学场景 80% 的画图需求回头看这个项目最大的价值不是「又移植了一个应用」而是验证了同栈双胞胎的工程模式Notebook 7 的分叉只花了 Lab 首次移植约 20% 的时间而这 80% 的节省全部来自 Lab 留下的踩坑记录和 bootstrap 脚本。如果你手上也有类似「一个底座多个产品」的移植任务强烈建议按这个模式组织仓库——第一个产品慢一点没关系它是在为后面的所有产品铺路。常见问题 FAQQ1和 JupyterLab 版有什么区别能同时装吗能共存。两者技术栈完全相同差异只在产品形态Notebook 打开就是单个笔记本single-document无 Launcher / 文件浏览器Lab 是多文档 Launcher。HNP 层做了不对称设计Notebook 用public、Lab 用private互不冲突。Q2为什么打开后不是经典的/tree老界面上游 Notebook 7 已经放弃老nbclassic的 jQuery 前端改为复用 Lab 组件。本工程走的是官方路线——Lab 静态资源 pageConfig.mode single-document视觉上是「单文档笔记本」但底层不是老/tree。Q3能装 numpy / matplotlib 吗默认不能。内核是 Skulpt浏览器内 Python 子集没有 C 扩展生态。两个出路切NOTEBOOK_EXTERNAL_SERVER_URL连远程 Jupyter Server 用真内核或者用纯标准库 ASCII 图表顶住教学场景见 §5.6 的 40 行折线图代码。Q4为什么r...三引号字符串报SyntaxError: bad inputSkulpt 词法器对 raw 三引号 box-drawing 多字节字符有边界 bug。去掉r前缀用普通...或把内容拆成多行print()就正常。真 Python 解释器跑同样代码没问题——这是内核限制不是平台 bug。Q5签名成功了还是报9568320 no signature file看产物文件名。unsigned说明build-profile.json5里products[].signingConfig还是空串——DevEco 自动签名只写材料不回填这个引用。手动改成default重新构建。Q6装了 Lab 再装 Notebook报9568407原生包失败HNP 同名冲突。两个产品的hnpPackages里声明了同名jupyterlab_python.hnppublic类型设备全局唯一后装的会被拒。用新版 HAPNotebookpublic/ Labprivate的不对称组合即可共存不想重编就先卸载另一个再装。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →