AI图形管理器设计与实现

接下来为大家带来的是一个功能完善的AI图形管理器,专为AI应用设计,支持节点管理、关系可视化和数据分析功能 并且能够用于实际应用 添加功能

目录:1. 功能说明

 

这个AI图形管理器提供了一套完整的图形管理和分析工具,专为AI应用设计:

主要功能

1. 节点管理**:

   - 添加/删除/编辑节点

   - 支持节点类型(输入层、隐藏层、输出层等)

   - 自定义节点权重和属性

2. 边管理**:

   - 添加/删除边

   - 支持边权重

   - 可视化连接关系

3. 图形操作**:

   - 随机生成AI模型结构

   - 清空图形

   - 图形分析(连通性、节点度、权重分布等)

 

4. 文件操作:

   - 保存/加载图形(JSON格式)

   - 导出高质量图像(PNG/PDF)

 

5. 可视化:

   -基于NetworkX和Matplotlib的交互式可视化

   - 按节点类型自动着色

   - 显示节点标签和权重

 

   以下为使用场景

1. 神经网络可视化:展示神经网络结构,包括输入层、隐藏层和输出层

2. 知识图谱管理:创建和管理AI领域的知识图谱

3. 决策树表示:可视化AI决策过程

4. :数据流分析:展示数据在AI系统中的流动和处理过程

 

技术特点

- 使用NetworkX作为图数据结构后端

- 基于Matplotlib的可视化展示

- Tkinter GUI界面,跨平台运行

- JSON格式的持久化存储

- 完整的异常处理和用户反馈

 

使用方法

1. 运行程序后,您可以通过"添加节点"按钮创建新节点

2. 使用"添加边"按钮连接节点

3. 随机生成功能可以快速创建测试图形

4. 使用分析功能获取图形的统计信息

5. 保存/加载功能允许您持久化存储图形

 

代码展示:

import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import networkx as nx
import numpy as np
import random
import json
import uuid

class AIGraphManager:
    def __init__(self, root):
        self.root = root
        self.root.title("AI图形管理器 v1.0")
        self.root.geometry("1200x800")
        self.root.configure(bg="#2c3e50")
        
        # 创建图形数据结构
        self.graph = nx.DiGraph()
        self.selected_node = None
        self.node_colors = {}
        
        # 设置样式
        self.style = ttk.Style()
        self.style.theme_use("clam")
        self.style.configure("TFrame", background="#34495e")
        self.style.configure("TButton", background="#3498db", foreground="white", font=("Arial", 10))
        self.style.configure("TLabel", background="#34495e", foreground="#ecf0f1", font=("Arial", 10))
        self.style.configure("Treeview", background="#ecf0f1", fieldbackground="#ecf0f1", font=("Arial", 9))
        self.style.configure("Treeview.Heading", background="#3498db", foreground="white", font=("Arial", 10, "bold"))
        
        # 创建主框架
        self.main_frame = ttk.Frame(root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 创建左侧控制面板
        self.control_frame = ttk.LabelFrame(self.main_frame, text="图形控制", padding=(10, 5))
        self.control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        
        # 创建右侧图形显示区域
        self.graph_frame = ttk.LabelFrame(self.main_frame, text="图形可视化", padding=(10, 5))
        self.graph_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 设置图形显示区域
        self.fig = plt.Figure(figsize=(8, 6), dpi=100)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.graph_frame)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 初始化图形
        self.init_graph()
        
        # 创建控制面板组件
        self.create_control_panel()
        
        # 创建底部状态栏
        self.status_bar = ttk.Label(root, text="就绪 | 节点数: 0 | 边数: 0", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 更新状态
        self.update_status()
    
    def create_control_panel(self):
        # 节点管理
        node_frame = ttk.LabelFrame(self.control_frame, text="节点管理", padding=(5, 5))
        node_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(node_frame, text="添加节点", command=self.add_node).pack(fill=tk.X, pady=2)
        ttk.Button(node_frame, text="删除节点", command=self.delete_node).pack(fill=tk.X, pady=2)
        ttk.Button(node_frame, text="编辑节点", command=self.edit_node).pack(fill=tk.X, pady=2)
        
        # 边管理
        edge_frame = ttk.LabelFrame(self.control_frame, text="边管理", padding=(5, 5))
        edge_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(edge_frame, text="添加边", command=self.add_edge).pack(fill=tk.X, pady=2)
        ttk.Button(edge_frame, text="删除边", command=self.delete_edge).pack(fill=tk.X, pady=2)
        
        # 图形操作
        graph_frame = ttk.LabelFrame(self.control_frame, text="图形操作", padding=(5, 5))
        graph_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(graph_frame, text="随机生成", command=self.generate_random_graph).pack(fill=tk.X, pady=2)
        ttk.Button(graph_frame, text="清空图形", command=self.clear_graph).pack(fill=tk.X, pady=2)
        ttk.Button(graph_frame, text="分析图形", command=self.analyze_graph).pack(fill=tk.X, pady=2)
        
        # 文件操作
        file_frame = ttk.LabelFrame(self.control_frame, text="文件操作", padding=(5, 5))
        file_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(file_frame, text="保存图形", command=self.save_graph).pack(fill=tk.X, pady=2)
        ttk.Button(file_frame, text="加载图形", command=self.load_graph).pack(fill=tk.X, pady=2)
        ttk.Button(file_frame, text="导出图像", command=self.export_image).pack(fill=tk.X, pady=2)
        
        # 节点列表
        list_frame = ttk.LabelFrame(self.control_frame, text="节点列表", padding=(5, 5))
        list_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 创建树形视图
        columns = ("id", "label", "type", "weight")
        self.node_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=15)
        
        # 设置列属性
        self.node_tree.column("id", width=80, anchor=tk.W)
        self.node_tree.column("label", width=120, anchor=tk.W)
        self.node_tree.column("type", width=80, anchor=tk.W)
        self.node_tree.column("weight", width=60, anchor=tk.CENTER)
        
        # 设置列标题
        self.node_tree.heading("id", text="ID", anchor=tk.W)
        self.node_tree.heading("label", text="标签", anchor=tk.W)
        self.node_tree.heading("type", text="类型", anchor=tk.W)
        self.node_tree.heading("weight", text="权重", anchor=tk.CENTER)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.node_tree.yview)
        self.node_tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.node_tree.pack(fill=tk.BOTH, expand=True)
        
        # 绑定选择事件
        self.node_tree.bind("<<TreeviewSelect>>", self.on_node_select)
    
    def init_graph(self):
        self.fig.clf()
        self.ax = self.fig.add_subplot(111)
        self.ax.set_title("AI图形可视化", fontsize=14)
        self.ax.set_facecolor("#ecf0f1")
        self.ax.axis('off')
        self.canvas.draw()
    
    def update_graph(self):
        self.fig.clf()
        self.ax = self.fig.add_subplot(111)
        self.ax.set_title("AI图形可视化", fontsize=14)
        self.ax.set_facecolor("#ecf0f1")
        
        if len(self.graph.nodes) == 0:
            self.ax.text(0.5, 0.5, "没有可显示的图形数据\n请添加节点或加载图形", 
                        ha='center', va='center', fontsize=12, color='gray')
            self.canvas.draw()
            return
        
        # 生成布局
        pos = nx.spring_layout(self.graph, seed=42)
        
        # 为不同类型的节点设置不同的颜色
        node_colors = [self.node_colors.get(self.graph.nodes[node]['type'], '#3498db') 
                       for node in self.graph.nodes]
        
        # 绘制节点
        nx.draw_networkx_nodes(self.graph, pos, node_size=700, 
                              node_color=node_colors, alpha=0.9, ax=self.ax)
        
        # 绘制边
        nx.draw_networkx_edges(self.graph, pos, width=1.5, 
                              edge_color='#7f8c8d', alpha=0.7, ax=self.ax)
        
        # 绘制节点标签
        node_labels = {node: f"{data['label']}\n({data['type']})" 
                      for node, data in self.graph.nodes(data=True)}
        nx.draw_networkx_labels(self.graph, pos, labels=node_labels, 
                               font_size=9, font_color='white', font_weight='bold', ax=self.ax)
        
        # 绘制边标签
        edge_labels = {(u, v): f"{data.get('weight', '')}" 
                      for u, v, data in self.graph.edges(data=True)}
        nx.draw_networkx_edge_labels(self.graph, pos, edge_labels=edge_labels, 
                                   font_size=8, font_color='#2c3e50', ax=self.ax)
        
        self.canvas.draw()
        self.update_node_list()
        self.update_status()
    
    def update_node_list(self):
        # 清空当前列表
        for item in self.node_tree.get_children():
            self.node_tree.delete(item)
        
        # 添加节点到列表
        for node, data in self.graph.nodes(data=True):
            self.node_tree.insert("", "end", values=(
                node[:8] + "...", 
                data.get('label', 'N/A'),
                data.get('type', '未知'),
                data.get('weight', 0)
            ))
    
    def update_status(self):
        node_count = len(self.graph.nodes)
        edge_count = len(self.graph.edges)
        self.status_bar.config(text=f"就绪 | 节点数: {node_count} | 边数: {edge_count}")
    
    def add_node(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("添加节点")
        dialog.geometry("300x250")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        
        ttk.Label(dialog, text="节点标签:").pack(pady=(10, 0))
        label_entry = ttk.Entry(dialog, width=30)
        label_entry.pack(pady=5)
        label_entry.focus_set()
        
        ttk.Label(dialog, text="节点类型:").pack()
        node_type = ttk.Combobox(dialog, values=["输入层", "隐藏层", "输出层", "数据", "处理", "决策"])
        node_type.pack(pady=5)
        node_type.set("隐藏层")
        
        ttk.Label(dialog, text="初始权重:").pack()
        weight_entry = ttk.Entry(dialog, width=30)
        weight_entry.pack(pady=5)
        weight_entry.insert(0, str(round(random.uniform(0.1, 1.0), 2)))
        
        ttk.Label(dialog, text="其他属性:").pack()
        attr_entry = ttk.Entry(dialog, width=30)
        attr_entry.pack(pady=5)
        attr_entry.insert(0, "属性=值")
        
        def on_ok():
            label = label_entry.get()
            n_type = node_type.get()
            weight = weight_entry.get()
            attr = attr_entry.get()
            
            if not label:
                messagebox.showerror("错误", "节点标签不能为空!")
                return
            
            try:
                weight_val = float(weight) if weight else 0.0
            except ValueError:
                messagebox.showerror("错误", "权重必须是数字!")
                return
            
            # 创建唯一ID
            node_id = str(uuid.uuid4())
            
            # 添加节点到图形
            attributes = {"label": label, "type": n_type, "weight": weight_val}
            
            # 解析额外属性
            if attr and "=" in attr:
                key, value = attr.split("=", 1)
                attributes[key.strip()] = value.strip()
            
            self.graph.add_node(node_id, **attributes)
            
            # 为节点类型分配颜色
            if n_type not in self.node_colors:
                colors = ['#e74c3c', '#2ecc71', '#9b59b6', '#f1c40f', '#1abc9c', '#d35400']
                self.node_colors[n_type] = colors[len(self.node_colors) % len(colors)]
            
            self.update_graph()
            dialog.destroy()
        
        ttk.Button(dialog, text="确定", command=on_ok).pack(side=tk.LEFT, padx=20, pady=10)
        ttk.Button(dialog, text="取消", command=dialog.destroy).pack(side=tk.RIGHT, padx=20, pady=10)
    
    def delete_node(self):
        if not self.selected_node:
            messagebox.showinfo("信息", "请先选择一个节点")
            return
            
        if messagebox.askyesno("确认", "确定要删除选中的节点吗?"):
            # 查找完整的节点ID
            full_id = None
            for node in self.graph.nodes:
                if node.startswith(self.selected_node):
                    full_id = node
                    break
            
            if full_id:
                self.graph.remove_node(full_id)
                self.selected_node = None
                self.update_graph()
    
    def edit_node(self):
        if not self.selected_node:
            messagebox.showinfo("信息", "请先选择一个节点")
            return
        
        # 查找完整的节点ID和节点数据
        full_id = None
        node_data = {}
        for node in self.graph.nodes:
            if node.startswith(self.selected_node):
                full_id = node
                node_data = self.graph.nodes[node]
                break
        
        if not full_id:
            return
            
        dialog = tk.Toplevel(self.root)
        dialog.title("编辑节点")
        dialog.geometry("300x250")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        
        ttk.Label(dialog, text="节点标签:").pack(pady=(10, 0))
        label_entry = ttk.Entry(dialog, width=30)
        label_entry.pack(pady=5)
        label_entry.insert(0, node_data.get('label', ''))
        label_entry.focus_set()
        
        ttk.Label(dialog, text="节点类型:").pack()
        node_type = ttk.Combobox(dialog, values=["输入层", "隐藏层", "输出层", "数据", "处理", "决策"])
        node_type.pack(pady=5)
        node_type.set(node_data.get('type', '隐藏层'))
        
        ttk.Label(dialog, text="节点权重:").pack()
        weight_entry = ttk.Entry(dialog, width=30)
        weight_entry.pack(pady=5)
        weight_entry.insert(0, str(node_data.get('weight', 0)))
        
        ttk.Label(dialog, text="其他属性:").pack()
        attr_entry = ttk.Entry(dialog, width=30)
        attr_entry.pack(pady=5)
        
        # 显示额外属性(除了label、type、weight)
        extra_attrs = [f"{k}={v}" for k, v in node_data.items() 
                      if k not in ['label', 'type', 'weight']]
        if extra_attrs:
            attr_entry.insert(0, "; ".join(extra_attrs))
        
        def on_ok():
            label = label_entry.get()
            n_type = node_type.get()
            weight = weight_entry.get()
            attr = attr_entry.get()
            
            if not label:
                messagebox.showerror("错误", "节点标签不能为空!")
                return
            
            try:
                weight_val = float(weight) if weight else 0.0
            except ValueError:
                messagebox.showerror("错误", "权重必须是数字!")
                return
            
            # 更新节点属性
            self.graph.nodes[full_id]['label'] = label
            self.graph.nodes[full_id]['type'] = n_type
            self.graph.nodes[full_id]['weight'] = weight_val
            
            # 更新节点类型颜色
            if n_type not in self.node_colors:
                colors = ['#e74c3c', '#2ecc71', '#9b59b6', '#f1c40f', '#1abc9c', '#d35400']
                self.node_colors[n_type] = colors[len(self.node_colors) % len(colors)]
            
            # 解析额外属性
            if attr:
                for pair in attr.split(';'):
                    if '=' in pair:
                        key, value = pair.split('=', 1)
                        key = key.strip()
                        value = value.strip()
                        if key and key not in ['label', 'type', 'weight']:
                            self.graph.nodes[full_id][key] = value
            
            self.update_graph()
            dialog.destroy()
        
        ttk.Button(dialog, text="确定", command=on_ok).pack(side=tk.LEFT, padx=20, pady=10)
        ttk.Button(dialog, text="取消", command=dialog.destroy).pack(side=tk.RIGHT, padx=20, pady=10)
    
    def add_edge(self):
        if len(self.graph.nodes) < 2:
            messagebox.showinfo("信息", "至少需要两个节点才能添加边")
            return
            
        dialog = tk.Toplevel(self.root)
        dialog.title("添加边")
        dialog.geometry("300x200")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        
        ttk.Label(dialog, text="起点节点:").pack(pady=(10, 0))
        from_node = ttk.Combobox(dialog, values=[self.graph.nodes[n]['label'] for n in self.graph.nodes])
        from_node.pack(pady=5)
        
        ttk.Label(dialog, text="终点节点:").pack()
        to_node = ttk.Combobox(dialog, values=[self.graph.nodes[n]['label'] for n in self.graph.nodes])
        to_node.pack(pady=5)
        
        ttk.Label(dialog, text="权重 (可选):").pack()
        weight_entry = ttk.Entry(dialog, width=30)
        weight_entry.pack(pady=5)
        
        def on_ok():
            from_label = from_node.get()
            to_label = to_node.get()
            weight = weight_entry.get()
            
            if not from_label or not to_label:
                messagebox.showerror("错误", "必须选择起点和终点节点!")
                return
            
            # 查找节点ID
            from_id = None
            to_id = None
            for node, data in self.graph.nodes(data=True):
                if data['label'] == from_label:
                    from_id = node
                if data['label'] == to_label:
                    to_id = node
            
            if not from_id or not to_id:
                messagebox.showerror("错误", "无法找到节点!")
                return
            
            if from_id == to_id:
                messagebox.showerror("错误", "不能连接节点到自身!")
                return
                
            if self.graph.has_edge(from_id, to_id):
                messagebox.showerror("错误", "这两个节点之间已经存在边!")
                return
                
            # 添加边
            try:
                weight_val = float(weight) if weight else 1.0
                self.graph.add_edge(from_id, to_id, weight=weight_val)
            except ValueError:
                messagebox.showerror("错误", "权重必须是数字!")
                return
            
            self.update_graph()
            dialog.destroy()
        
        ttk.Button(dialog, text="确定", command=on_ok).pack(side=tk.LEFT, padx=20, pady=10)
        ttk.Button(dialog, text="取消", command=dialog.destroy).pack(side=tk.RIGHT, padx=20, pady=10)
    
    def delete_edge(self):
        if len(self.graph.edges) == 0:
            messagebox.showinfo("信息", "图形中没有边")
            return
            
        dialog = tk.Toplevel(self.root)
        dialog.title("删除边")
        dialog.geometry("300x150")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        
        ttk.Label(dialog, text="起点节点:").pack(pady=(10, 0))
        from_node = ttk.Combobox(dialog, values=[self.graph.nodes[n]['label'] for n in self.graph.nodes])
        from_node.pack(pady=5)
        
        ttk.Label(dialog, text="终点节点:").pack()
        to_node = ttk.Combobox(dialog, values=[self.graph.nodes[n]['label'] for n in self.graph.nodes])
        to_node.pack(pady=5)
        
        def on_ok():
            from_label = from_node.get()
            to_label = to_node.get()
            
            if not from_label or not to_label:
                messagebox.showerror("错误", "必须选择起点和终点节点!")
                return
            
            # 查找节点ID
            from_id = None
            to_id = None
            for node, data in self.graph.nodes(data=True):
                if data['label'] == from_label:
                    from_id = node
                if data['label'] == to_label:
                    to_id = node
            
            if not from_id or not to_id:
                messagebox.showerror("错误", "无法找到节点!")
                return
            
            if not self.graph.has_edge(from_id, to_id):
                messagebox.showerror("错误", "这两个节点之间没有边!")
                return
                
            # 删除边
            self.graph.remove_edge(from_id, to_id)
            self.update_graph()
            dialog.destroy()
        
        ttk.Button(dialog, text="确定", command=on_ok).pack(side=tk.LEFT, padx=20, pady=10)
        ttk.Button(dialog, text="取消", command=dialog.destroy).pack(side=tk.RIGHT, padx=20, pady=10)
    
    def generate_random_graph(self):
        # 清空现有图形
        self.graph.clear()
        self.node_colors = {}
        
        # 生成5-10个节点
        num_nodes = random.randint(5, 10)
        node_types = ["输入层", "隐藏层", "输出层", "数据", "处理", "决策"]
        
        for i in range(num_nodes):
            node_id = str(uuid.uuid4())
            node_type = random.choice(node_types)
            weight = round(random.uniform(0.1, 1.0), 2)
            
            self.graph.add_node(node_id, 
                              label=f"节点{i+1}", 
                              type=node_type,
                              weight=weight)
            
            # 为节点类型分配颜色
            if node_type not in self.node_colors:
                colors = ['#e74c3c', '#2ecc71', '#9b59b6', '#f1c40f', '#1abc9c', '#d35400']
                self.node_colors[node_type] = colors[len(self.node_colors) % len(colors)]
        
        # 添加边(连接节点)
        nodes = list(self.graph.nodes)
        for i in range(len(nodes)):
            # 每个节点连接到1-3个其他节点
            num_edges = random.randint(1, min(3, len(nodes)-1)
            targets = random.sample(nodes, num_edges)
            for target in targets:
                if nodes[i] != target and not self.graph.has_edge(nodes[i], target):
                    weight = round(random.uniform(0.1, 1.0), 2)
                    self.graph.add_edge(nodes[i], target, weight=weight)
        
        self.update_graph()
        messagebox.showinfo("信息", f"已生成随机图形: {num_nodes}个节点, {len(self.graph.edges)}条边")
    
    def clear_graph(self):
        if messagebox.askyesno("确认", "确定要清空整个图形吗?"):
            self.graph.clear()
            self.node_colors = {}
            self.selected_node = None
            self.update_graph()
    
    def analyze_graph(self):
        if len(self.graph.nodes) == 0:
            messagebox.showinfo("信息", "图形为空,无法进行分析")
            return
            
        # 创建分析结果窗口
        result_window = tk.Toplevel(self.root)
        result_window.title("图形分析结果")
        result_window.geometry("600x400")
        result_window.transient(self.root)
        
        # 创建文本区域显示结果
        text_area = tk.Text(result_window, wrap=tk.WORD, font=("Courier New", 10))
        scrollbar = ttk.Scrollbar(result_window, command=text_area.yview)
        text_area.configure(yscrollcommand=scrollbar.set)
        
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 添加分析结果
        text_area.insert(tk.END, "="*50 + "\n")
        text_area.insert(tk.END, "图形分析报告\n")
        text_area.insert(tk.END, "="*50 + "\n\n")
        
        # 基本信息
        text_area.insert(tk.END, f"节点总数: {len(self.graph.nodes)}\n")
        text_area.insert(tk.END, f"边总数: {len(self.graph.edges)}\n\n")
        
        # 节点类型统计
        type_count = {}
        for node, data in self.graph.nodes(data=True):
            node_type = data.get('type', '未知')
            type_count[node_type] = type_count.get(node_type, 0) + 1
        
        text_area.insert(tk.END, "节点类型分布:\n")
        for t, count in type_count.items():
            text_area.insert(tk.END, f"  {t}: {count}个节点\n")
        text_area.insert(tk.END, "\n")
        
        # 平均度
        degrees = [deg for _, deg in self.graph.degree()]
        avg_degree = sum(degrees) / len(degrees) if degrees else 0
        text_area.insert(tk.END, f"平均节点度: {avg_degree:.2f}\n\n")
        
        # 连通性分析
        if nx.is_weakly_connected(self.graph):
            text_area.insert(tk.END, "图形是弱连通的\n")
        else:
            text_area.insert(tk.END, "图形不是弱连通的\n")
            text_area.insert(tk.END, f"连通分量数: {nx.number_weakly_connected_components(self.graph)}\n")
        
        # 权重分析
        weights = [data['weight'] for _, data in self.graph.nodes(data=True)]
        min_weight = min(weights) if weights else 0
        max_weight = max(weights) if weights else 0
        avg_weight = sum(weights) / len(weights) if weights else 0
        
        text_area.insert(tk.END, "\n节点权重分析:\n")
        text_area.insert(tk.END, f"  最小权重: {min_weight:.2f}\n")
        text_area.insert(tk.END, f"  最大权重: {max_weight:.2f}\n")
        text_area.insert(tk.END, f"  平均权重: {avg_weight:.2f}\n")
        
        # 边权重分析
        if len(self.graph.edges) > 0:
            edge_weights = [data['weight'] for _, _, data in self.graph.edges(data=True)]
            min_edge = min(edge_weights) if edge_weights else 0
            max_edge = max(edge_weights) if edge_weights else 0
            avg_edge = sum(edge_weights) / len(edge_weights) if edge_weights else 0
            
            text_area.insert(tk.END, "\n边权重分析:\n")
            text_area.insert(tk.END, f"  最小权重: {min_edge:.2f}\n")
            text_area.insert(tk.END, f"  最大权重: {max_edge:.2f}\n")
            text_area.insert(tk.END, f"  平均权重: {avg_edge:.2f}\n")
        
        text_area.configure(state=tk.DISABLED)
    
    def save_graph(self):
        if len(self.graph.nodes) == 0:
            messagebox.showinfo("信息", "没有数据可保存")
            return
            
        file_path = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON 文件", "*.json"), ("所有文件", "*.*")]
        )
        
        if not file_path:
            return
            
        try:
            # 转换图形数据为可序列化的格式
            graph_data = {
                "nodes": [],
                "edges": []
            }
            
            # 添加节点
            for node, data in self.graph.nodes(data=True):
                node_data = {"id": node}
                node_data.update(data)
                graph_data["nodes"].append(node_data)
            
            # 添加边
            for u, v, data in self.graph.edges(data=True):
                edge_data = {
                    "source": u,
                    "target": v
                }
                edge_data.update(data)
                graph_data["edges"].append(edge_data)
            
            # 保存节点颜色
            graph_data["node_colors"] = self.node_colors
            
            # 写入文件
            with open(file_path, 'w') as f:
                json.dump(graph_data, f, indent=2)
                
            messagebox.showinfo("成功", f"图形已保存到: {file_path}")
        except Exception as e:
            messagebox.showerror("错误", f"保存文件时出错: {str(e)}")
    
    def load_graph(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("JSON 文件", "*.json"), ("所有文件", "*.*")]
        )
        
        if not file_path:
            return
            
        try:
            with open(file_path, 'r') as f:
                graph_data = json.load(f)
            
            # 清空现有图形
            self.graph.clear()
            self.node_colors = {}
            
            # 添加节点
            for node_data in graph_data.get("nodes", []):
                node_id = node_data.pop("id")
                self.graph.add_node(node_id, **node_data)
                
                # 恢复节点颜色
                node_type = node_data.get('type')
                if node_type and node_type in graph_data.get("node_colors", {}):
                    self.node_colors[node_type] = graph_data["node_colors"][node_type]
            
            # 添加边
            for edge_data in graph_data.get("edges", []):
                source = edge_data.pop("source")
                target = edge_data.pop("target")
                self.graph.add_edge(source, target, **edge_data)
            
            self.update_graph()
            messagebox.showinfo("成功", f"已加载图形: {len(self.graph.nodes)}个节点, {len(self.graph.edges)}条边")
        except Exception as e:
            messagebox.showerror("错误", f"加载文件时出错: {str(e)}")
    
    def export_image(self):
        if len(self.graph.nodes) == 0:
            messagebox.showinfo("信息", "没有数据可导出")
            return
            
        file_path = filedialog.asksaveasfilename(
            defaultextension=".png",
            filetypes=[("PNG 图像", "*.png"), ("PDF 文件", "*.pdf"), ("所有文件", "*.*")]
        )
        
        if not file_path:
            return
            
        try:
            # 创建一个新图形用于导出
            export_fig = plt.Figure(figsize=(10, 8), dpi=150)
            ax = export_fig.add_subplot(111)
            
            # 生成布局
            pos = nx.spring_layout(self.graph, seed=42)
            
            # 为不同类型的节点设置不同的颜色
            node_colors = [self.node_colors.get(self.graph.nodes[node]['type'], '#3498db') 
                          for node in self.graph.nodes]
            
            # 绘制节点
            nx.draw_networkx_nodes(self.graph, pos, node_size=800, 
                                  node_color=node_colors, alpha=0.9, ax=ax)
            
            # 绘制边
            nx.draw_networkx_edges(self.graph, pos, width=1.8, 
                                  edge_color='#7f8c8d', alpha=0.7, ax=ax)
            
            # 绘制节点标签
            node_labels = {node: f"{data['label']}\n({data['type']})" 
                          for node, data in self.graph.nodes(data=True)}
            nx.draw_networkx_labels(self.graph, pos, labels=node_labels, 
                                   font_size=10, font_color='white', font_weight='bold', ax=ax)
            
            # 绘制边标签
            edge_labels = {(u, v): f"{data.get('weight', '')}" 
                          for u, v, data in self.graph.edges(data=True)}
            nx.draw_networkx_edge_labels(self.graph, pos, edge_labels=edge_labels, 
                                       font_size=9, font_color='#2c3e50', ax=ax)
            
            ax.set_title("AI图形可视化", fontsize=16)
            ax.axis('off')
            
            # 保存图像
            export_fig.savefig(file_path, bbox_inches='tight')
            messagebox.showinfo("成功", f"图像已导出到: {file_path}")
        except Exception as e:
            messagebox.showerror("错误", f"导出图像时出错: {str(e)}")
    
    def on_node_select(self, event):
        selected_item = self.node_tree.selection()
        if selected_item:
            item = self.node_tree.item(selected_item[0])
            self.selected_node = item['values'][0]  # 存储截断的ID
    
    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    root = tk.Tk()
    app = AIGraphManager(root)
    app.run()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值