You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Voila仪表盘中JSME化学编辑器交互异常与独立部署问题

JSME化学编辑器集成Voila仪表板问题解决

问题梳理

  • 在JupyterHub直接打开ipynb时,JSME可正常使用,但仅能通过个人服务器路径加载JS文件
  • Voila渲染后,编辑器显示但按钮点击无响应
  • 分享仪表板时,仅自己的JupyterHub服务器启动时才能显示编辑器,且按钮仍失效;服务器关闭后用户仅能看到HTML按钮

核心原因

  1. 依赖个人服务器路径:原JS加载路径指向JupyterHub的用户会话路径,该路径仅在个人服务器启动时可访问,且无法被其他用户共享
  2. Voila环境不兼容:原代码使用IPython.notebook.kernel执行命令,但Voila环境中不存在该对象,导致按钮逻辑失效

解决方案

步骤1:本地托管JSME资源

  1. 下载JSME压缩包并解压,将所有文件放在ipynb所在目录的jsme子文件夹中
  2. 使用相对路径加载JS文件,摆脱对个人JupyterHub服务器的依赖

步骤2:适配Voila的交互逻辑

替换依赖IPython.notebook.kernel的代码,改用ipywidgets的DOM交互机制实现SMILES值传递,确保在Voila中正常响应

修改后的完整代码

import ipywidgets as widgets
from IPython.display import display, HTML
from pathlib import Path

# 验证JSME资源是否存在
jsme_dir = Path("./jsme")
assert jsme_dir.exists(), "请将JSME解压到当前目录的jsme文件夹下"

# 创建隐藏的接收组件,用于JS传递SMILES值
smiles_receiver = widgets.Text(layout=widgets.Layout(display="none"))

# 监听接收组件的变化,更新显示用的SMILES组件
def update_smiles_display(change):
    if change["new"]:
        text_widget.value = change["new"]

smiles_receiver.observe(update_smiles_display, names="value")

def jsme_widget():
    jsme_html = """
    <script type="text/javascript" src="./jsme/jsme.nocache.js"></script>
    <script type="text/javascript">
    let jsmeApplet;
    function jsmeOnLoad() {
        // 初始化JSME编辑器
        jsmeApplet = new JSApplet.JSME("appletContainer", "640px", "400px", {
            "options": "number,query,hydrogens,paste,atommovebutton,border,fullScreenIcon,contextmenu,nozoomgui"
        });
        var jme = `~A`;
        jsmeApplet.readMolFile(jme);
    }

    // 将SMILES传递给Python组件
    function sendSmiles() {
        const smiles = jsmeApplet.smiles();
        // 找到隐藏的接收组件并更新值
        const receiver = document.querySelector('input[data-widget-name="smiles_receiver"]');
        if (receiver) {
            receiver.value = smiles;
            receiver.dispatchEvent(new Event("input"));
        }
    }
    </script>
    <div style="display:flex; align-items:flex-end; gap:12px;">
        <div id="appletContainer"></div>
        <button onclick="sendSmiles()" style="padding:8px 16px; cursor:pointer;">Done!</button>
    </div>
    """
    return HTML(jsme_html)

# 可见的SMILES显示组件
text_widget = widgets.Text(description="SMILES:", layout=widgets.Layout(width="600px"))

# 渲染所有组件
display(text_widget)
display(smiles_receiver)
display(jsme_widget())

关键改进点

  • 相对路径加载:使用./jsme/jsme.nocache.js加载JS文件,Voila会自动服务当前目录下的静态资源,无需依赖个人JupyterHub服务器
  • DOM交互传递值:通过隐藏的ipywidgets组件接收JS传递的SMILES,再同步到显示组件,避免依赖Voila中不存在的IPython.notebook对象
  • 兼容性保证:代码同时支持JupyterHub和Voila环境,无需额外修改

内容的提问来源于stack exchange,提问作者Pepimouth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:35:08