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

如何在Python Shiny中提取、保存及访问pyvis.network的操作变更?

解决Python Shiny + Pyvis 捕获网络变更并保存的方案

核心思路

Pyvis的manipulation功能基于vis.js实现,要捕获前端的节点/边增删操作,需在生成的HTML中添加自定义JavaScript,把变更数据传递回Shiny后端,同步更新后端的Pyvis网络对象后,再实现保存功能。

步骤1:自定义Pyvis HTML模板

Pyvis允许自定义输出模板,我们需要在模板中添加监听vis.js事件的代码,通过Shiny的Shiny.setInputValue将变更数据发送给后端。

创建模板文件custom_template.html,内容如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Network</title>
    <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
    <style type="text/css">
        #mynetwork {
            width: 100%;
            height: 600px;
            border: 1px solid lightgray;
        }
    </style>
</head>
<body>
    <div id="mynetwork"></div>
    <script type="text/javascript">
        // 加载网络数据
        var nodes = new vis.DataSet({{ nodes }});
        var edges = new vis.DataSet({{ edges }});
        var container = document.getElementById("mynetwork");
        var data = { nodes: nodes, edges: edges };
        var options = {{ options }};
        var network = new vis.Network(container, data, options);

        // 监听节点添加事件
        network.on("addNode", function(params) {
            Shiny.setInputValue("network_change", {
                type: "add_node",
                node: params.nodes[0]
            }, {priority: "event"});
        });

        // 监听节点删除事件
        network.on("deleteNode", function(params) {
            Shiny.setInputValue("network_change", {
                type: "delete_node",
                node_ids: params.nodes
            }, {priority: "event"});
        });

        // 监听边添加事件
        network.on("addEdge", function(params) {
            Shiny.setInputValue("network_change", {
                type: "add_edge",
                edge: params.edges[0]
            }, {priority: "event"});
        });

        // 监听边删除事件
        network.on("deleteEdge", function(params) {
            Shiny.setInputValue("network_change", {
                type: "delete_edge",
                edge_ids: params.edges
            }, {priority: "event"});
        });
    </script>
</body>
</html>

步骤2:Shiny应用实现

在Shiny应用中,使用自定义模板生成Pyvis网络,接收前端传来的变更并更新网络对象,同时添加保存按钮。

from shiny import App, ui, render, reactive
from pyvis.network import Network
import json

# 初始化Pyvis网络
def init_network():
    net = Network(height="600px", width="100%", directed=False)
    net.enable_physics(True)
    # 启用manipulation功能
    net.set_options("""
    {
      "manipulation": {
        "enabled": true,
        "addNode": true,
        "addEdge": true,
        "deleteNode": true,
        "deleteEdge": true,
        "editNode": true,
        "editEdge": true
      }
    }
    """)
    # 添加初始节点示例
    net.add_node(1, label="Node 1")
    net.add_node(2, label="Node 2")
    net.add_edge(1, 2)
    return net

app_ui = ui.page_fluid(
    ui.h1("Pyvis Network Editor"),
    ui.output_ui("network"),
    ui.download_button("save_network", "Save Final Network"),
)

def server(input, output, session):
    # 用reactive变量存储网络对象,方便更新
    net = reactive.Value(init_network())

    @output
    @render.ui
    def network():
        # 使用自定义模板生成HTML
        current_net = net.get()
        # 将节点、边、选项转为JSON格式,适配模板
        nodes_json = json.dumps([n for n in current_net.nodes])
        edges_json = json.dumps([e for e in current_net.edges])
        options_json = json.dumps(current_net.options)
        
        with open("custom_template.html", "r") as f:
            template = f.read()
        
        # 替换模板中的占位符
        html_content = template.replace("{{ nodes }}", nodes_json)\
                               .replace("{{ edges }}", edges_json)\
                               .replace("{{ options }}", options_json)
        return ui.HTML(html_content)

    # 监听前端传来的网络变更
    @reactive.Effect
    @reactive.event(input.network_change)
    def update_network():
        change = input.network_change()
        current_net = net.get()
        
        if change["type"] == "add_node":
            node = change["node"]
            # Pyvis添加节点,处理id和label
            current_net.add_node(node["id"], label=node.get("label", f"Node {node['id']}"))
        elif change["type"] == "delete_node":
            for node_id in change["node_ids"]:
                current_net.remove_node(node_id)
        elif change["type"] == "add_edge":
            edge = change["edge"]
            current_net.add_edge(edge["from"], edge["to"], id=edge["id"])
        elif change["type"] == "delete_edge":
            for edge_id in change["edge_ids"]:
                # 通过id匹配并删除边
                for e in current_net.edges:
                    if e["id"] == edge_id:
                        current_net.remove_edge(e["from"], e["to"])
                        break
        # 更新reactive变量,触发网络重新渲染
        net.set(current_net)

    # 保存网络为HTML文件
    @session.download()
    def save_network():
        current_net = net.get()
        output_path = "final_network.html"
        current_net.write_html(output_path)
        return output_path

app = App(app_ui, server)

关键说明

  • 借助vis.js原生事件(addNode、deleteNode等)捕获操作,通过Shiny.setInputValue将数据同步到后端。
  • 使用Shiny的reactive.Value存储网络对象,确保变更后实时重新渲染网络。
  • 自定义模板是核心,默认Pyvis生成的HTML没有与Shiny通信的逻辑,需要手动添加交互回调。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 15:03:18