知识图谱网页版可视化可移动代码

发布于:2025-09-15 ⋅ 阅读:(9) ⋅ 点赞:(0)

1.代码

import networkx as nx
from pyvis.network import Network
import platform
from matplotlib import font_manager
import re
import os

# 动态设置中文字体
def setup_chinese_font():
    system = platform.system()
    chinese_fonts = []
    
    if system == "Windows":
        chinese_fonts = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
    elif system == "Darwin":  # macOS
        chinese_fonts = ['PingFang SC', 'Heiti TC', 'Noto Sans CJK SC', 'DejaVu Sans']
    elif system == "Linux":
        chinese_fonts = ['Noto Sans CJK SC', 'WenQuanYi Micro Hei', 'DejaVu Sans']
    else:
        chinese_fonts = ['DejaVu Sans']
    
    available_fonts = [f.name for f in font_manager.fontManager.ttflist]
    font_name = next((font for font in chinese_fonts if font in available_fonts), 'DejaVu Sans')
    
    if font_name != 'DejaVu Sans':
        print(f"使用字体: {font_name}")
    else:
        print("警告: 未找到中文字体,使用默认。建议安装 Noto Sans CJK。")
    return font_name

font_name = setup_chinese_font()

# 示例数据(替换为你的数据)
nodes = [
    {"id": "1", "label": "工艺知识", "group": "工艺知识", "size": 20}, 
    {"id": "2", "label": "机床", "group": "机床", "size": 20}, 
    {"id": "3", "label": "刀具", "group": "刀具", "size": 20},    
    {"id": "4", "label": "加工特征", "group": "加工特征", "size": 20},

    # 机床节点
    {"id": "M1", "label": "钻床M1", "group": "机床", "size": 20},
    {"id": "M2", "label": "数控铣床M2", "group": "机床", "size": 22},
    {"id": "M3", "label": "立铣M3", "group": "机床", "size": 18},
    {"id": "M4", "label": "镗床M4", "group": "机床", "size": 19},
    {"id": "M5", "label": "刨床M5", "group": "机床", "size": 17},
    {"id": "M6", "label": "磨床M6", "group": "机床", "size": 18},
    {"id": "M7", "label": "车床M7", "group": "机床", "size": 21},

    # 机床能耗节点
    {"id": "机床能耗35kj", "label": "35kj", "group": "能耗", "size": 20},
    {"id": "机床能耗40kj", "label": "40kj", "group": "能耗", "size": 20},
    {"id": "机床能耗45kj", "label": "45kj", "group": "能耗", "size": 20},
    {"id": "机床能耗50kj", "label": "50kj", "group": "能耗", "size": 20},
    {"id": "机床能耗65kj", "label": "65kj", "group": "能耗", "size": 20},   

    # 刀具节点
    {"id": "T1", "label": "钻头", "group": "刀具", "size": 15},
    {"id": "T2", "label": "立铣刀", "group": "刀具", "size": 16},
    {"id": "T3", "label": "镗刀", "group": "刀具", "size": 14},
    {"id": "T4", "label": "刨刀", "group": "刀具", "size": 13},
    {"id": "T5", "label": "铣刀1", "group": "刀具", "size": 15},
    {"id": "T6", "label": "铣刀2", "group": "刀具", "size": 16},
    {"id": "T7", "label": "倒角刀1", "group": "刀具", "size": 13},
    {"id": "T8", "label": "起槽刀", "group": "刀具", "size": 15},
    {"id": "T9", "label": "车刀", "group": "刀具", "size": 16},



    # 刀具能耗节点
    {"id": "刀具能耗3kj", "label": "3kj", "group": "能耗", "size": 20},
    {"id": "刀具能耗8kj", "label": "8kj", "group": "能耗", "size": 20},
    {"id": "刀具能耗10kj", "label": "10kj", "group": "能耗", "size": 20},
    {"id": "刀具能耗14kj", "label": "14kj", "group": "能耗", "size": 20},
    {"id": "刀具能耗15kj", "label": "15kj", "group": "能耗", "size": 20},

    # 加工特征
    {"id": "外圆", "label": "外圆", "group": "加工特征", "size": 20},
    {"id": "内孔", "label": "内孔", "group": "加工特征", "size": 20},
    {"id": "平面", "label": "平面", "group": "加工特征", "size": 20},
]

edges = [
    {"source": "1", "target": "2", "relation": "包括", "weight": 1},
    {"source": "1", "target": "3", "relation": "包括", "weight": 1},
    {"source": "1", "target": "4", "relation": "包括", "weight": 1},

    {"source": "2", "target": "M1", "relation": "配用机床", "weight": 1},
    {"source": "2", "target": "M2", "relation": "配用机床", "weight": 1},
    {"source": "2", "target": "M3", "relation": "配用机床", "weight": 1},
    {"source": "2", "target": "M4", "relation": "配用机床", "weight": 1},
    {"source": "2", "target": "M5", "relation": "配用机床", "weight": 1},
    {"source": "2", "target": "M6", "relation": "配用机床", "weight": 1},
    {"source": "2", "target": "M7", "relation": "配用机床", "weight": 1},

    {"source": "3", "target": "T1", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T2", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T3", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T4", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T5", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T6", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T7", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T8", "relation": "配用刀具", "weight": 1},
    {"source": "3", "target": "T9", "relation": "配用刀具", "weight": 1},
    

    {"source": "4", "target": "外圆", "relation": "加工特征", "weight": 1},
    {"source": "4", "target": "内孔", "relation": "加工特征", "weight": 1},
    {"source": "4", "target": "平面", "relation": "加工特征", "weight": 1},

    {"source": "M1", "target": "机床能耗40kj", "relation": "能耗关联", "weight": 1},
    {"source": "M2", "target": "机床能耗65kj", "relation": "能耗关联", "weight": 1},
    {"source": "M3", "target": "机床能耗35kj", "relation": "能耗关联", "weight": 1},
    {"source": "M4", "target": "机床能耗50kj", "relation": "能耗关联", "weight": 1},
    {"source": "M5", "target": "机床能耗45kj", "relation": "能耗关联", "weight": 1},
    {"source": "M6", "target": "机床能耗40kj", "relation": "能耗关联", "weight": 1},
    {"source": "M7", "target": "机床能耗45kj", "relation": "能耗关联", "weight": 1},

    {"source": "T1", "target": "刀具能耗3kj", "relation": "能耗关联", "weight": 1},
    {"source": "T2", "target": "刀具能耗3kj", "relation": "能耗关联", "weight": 1},
    {"source": "T3", "target": "刀具能耗8kj", "relation": "能耗关联", "weight": 1},
    {"source": "T4", "target": "刀具能耗15kj", "relation": "能耗关联", "weight": 1},
    {"source": "T5", "target": "刀具能耗10kj", "relation": "能耗关联", "weight": 1},
    {"source": "T6", "target": "刀具能耗15kj", "relation": "能耗关联", "weight": 1},
    {"source": "T7", "target": "刀具能耗10kj", "relation": "能耗关联", "weight": 1},
    {"source": "T8", "target": "刀具能耗10kj", "relation": "能耗关联", "weight": 1},
    {"source": "T9", "target": "刀具能耗14kj", "relation": "能耗关联", "weight": 1},
    
    
    
    {"source": "外圆", "target": "M7", "relation": "可用机床", "weight": 1},
    
]

      
# 创建 NetworkX 图
G = nx.Graph()
for node in nodes:
    G.add_node(node["id"], label=node["label"], group=node["group"], size=node["size"])
for edge in edges:
    G.add_edge(edge["source"], edge["target"], relation=edge["relation"], weight=edge["weight"])

# 创建 pyvis 网络
net = Network(height="100vh", width="100vw", directed=False, notebook=False)

net.force_atlas_2based(
    gravity=-150,
    central_gravity=0.1,
    spring_length=30,
    spring_strength=0.15,
    damping=0.4,
    overlap=0
)

# 固定颜色映射
color_map = {
    "工艺知识": "#87CEEB",   # 天蓝
    "机床": "#90EE90",       # 淡绿
    "刀具": "#FA8072",       # 珊瑚红
    "加工特征": "#FFD700",   # 金黄
    "能耗": "#DA70D6",        # 紫罗兰
    "加工机床": "#FFD600"   # 金黄
    
}

# 添加节点和边
for node_id, data in G.nodes(data=True):
    group = data["group"]
    color = color_map.get(group, "#D3D3D3")  # 默认灰色兜底
    net.add_node(
        node_id, label=data["label"], size=data["size"], color=color,
        font={'face': font_name, 'size': 16}, fixed=False
    )

for source, target, data in G.edges(data=True):
    net.add_edge(
        source, target, title=data["relation"], width=data["weight"],
        label=data["relation"], font={'size': 12, 'align': 'middle'}
    )

# 配置选项
options_json = f"""
{{
  "nodes": {{
    "font": {{
      "face": "{font_name}"
    }}
  }},
  "edges": {{
    "font": {{
      "face": "{font_name}"
    }}
  }},
  "physics": {{
    "enabled": true,
    "stabilization": {{
      "enabled": true,
      "iterations": 100,
      "updateInterval": 25
    }},
    "forceAtlas2Based": {{
      "gravitationalConstant": -150,
      "centralGravity": 0.1,
      "springLength": 30,
      "springConstant": 0.15,
      "damping": 0.4
    }},
    "minVelocity": 0.75,
    "solver": "forceAtlas2Based"
  }},
  "interaction": {{
    "dragNodes": true,
    "dragView": true,
    "zoomView": true
  }}
}}
"""
net.set_options(options_json)

# 自定义 JS
custom_js = """
<script type="text/javascript">
  network.on('stabilizationIterationsDone', function() {
    network.setOptions({ physics: { enabled: false } });
    network.redraw();
  });
</script>
"""

# 生成 HTML
html_file = "knowledge_graph.html"
net.write_html(html_file)

# 自定义 CSS
custom_css = f"""
<style>
  html, body {{
    margin: 0;
    padding: 0;
    height: 100vh;
    width: 100vw;
    overflow: hidden;
  }}
  #network {{
    position: absolute;
    top: 0;
    left: 0;
    height: 100vh !important;
    width: 100vw !important;
  }}
  .vis-network {{
    position: absolute;
    top: 0;
    left: 0;
    height: 100vh !important;
    width: 100vw !important;
    font-family: '{font_name}', 'Noto Sans CJK SC', 'SimHei', sans-serif !important;
  }}
  .vis-label {{
    font-family: '{font_name}', 'Noto Sans CJK SC', 'SimHei', sans-serif !important;
  }}
  .vis-edgelabel {{
    font-family: '{font_name}', 'Noto Sans CJK SC', 'SimHei', sans-serif !important;
  }}
</style>
"""

# 后处理:添加 CSS 和 JS
try:
    if os.path.exists(html_file):
        with open(html_file, "r", encoding="utf-8") as f:
            html_content = f.read()

        head_match = re.search(r'<head[^>]*>.*?</head>', html_content, re.IGNORECASE | re.DOTALL)
        if head_match:
            insert_pos = html_content.find('</head>', head_match.start())
            if insert_pos != -1:
                html_content = html_content[:insert_pos] + custom_css + html_content[insert_pos:]
        else:
            body_match = html_content.find('<body')
            if body_match != -1:
                html_content = html_content[:body_match] + '<head>' + custom_css + '</head>' + html_content[body_match:]

        body_end = html_content.find('</body>')
        if body_end != -1:
            html_content = html_content[:body_end] + custom_js + html_content[body_end:]
        else:
            html_content += custom_js

        with open(html_file, "w", encoding="utf-8") as f:
            f.write(html_content)
        print(f"CSS 和 JS 已成功添加至 {html_file},支持单个节点拖动而不影响其他节点")
    else:
        print(f"警告: {html_file} 不存在,无法添加 CSS 和 JS")
except Exception as e:
    print(f"后处理警告: {e} - HTML 已生成,但 CSS/JS 未添加。手动检查文件。")

print(f"知识图谱已保存为 {html_file}(支持单个节点拖动)")