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": # macOSchinese_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_namefont_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_jswith 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}(支持单个节点拖动)")