求助:Python手动绘制可提取矩阵并运行算法的网络图程序实现
手动交互式网络图绘制工具实现方案
以下是基于Tkinter + NetworkX的完整实现,满足手动绘制顶点、创建边,以及图算法、矩阵提取的需求:
核心代码实现
import tkinter as tk from tkinter import messagebox, simpledialog import networkx as nx import numpy as np class GraphEditor: def __init__(self, root): self.root = root self.root.title("交互式网络图编辑器") # 初始化NetworkX图对象 self.G = nx.Graph() self.node_counter = 0 self.nodes = {} # 存储节点ID到画布元素的映射: {node_id: (x, y, oval_id, text_id)} self.selected_node = None # 边模式下选中的第一个节点 self.mode = "idle" # 当前模式: idle/vertex/edge # 创建菜单栏 menubar = tk.Menu(root) root.config(menu=menubar) # 模式菜单 mode_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="模式", menu=mode_menu) mode_menu.add_command(label="放置顶点", command=self.set_vertex_mode) mode_menu.add_command(label="创建边", command=self.set_edge_mode) mode_menu.add_command(label="退出模式", command=self.set_idle_mode) # 功能菜单 func_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="功能", menu=func_menu) func_menu.add_command(label="提取邻接矩阵", command=self.show_adjacency_matrix) func_menu.add_command(label="提取关联矩阵", command=self.show_incidence_matrix) func_menu.add_command(label="DFS遍历", command=self.run_dfs) func_menu.add_command(label="BFS遍历", command=self.run_bfs) func_menu.add_command(label="Dijkstra最短路径", command=self.run_dijkstra) # 创建绘图画布 self.canvas = tk.Canvas(root, width=800, height=600, bg="white") self.canvas.pack(fill=tk.BOTH, expand=True) self.canvas.bind("<Button-1>", self.canvas_click) def set_vertex_mode(self): self.mode = "vertex" self.root.title("交互式网络图编辑器 - 放置顶点模式") def set_edge_mode(self): self.mode = "edge" self.selected_node = None self.root.title("交互式网络图编辑器 - 创建边模式") def set_idle_mode(self): self.mode = "idle" self.selected_node = None self.root.title("交互式网络图编辑器") def canvas_click(self, event): x, y = event.x, event.y if self.mode == "vertex": # 添加顶点 node_id = self.node_counter self.G.add_node(node_id, pos=(x, y)) # 在画布绘制顶点 oval_id = self.canvas.create_oval(x-10, y-10, x+10, y+10, fill="lightblue", outline="black") text_id = self.canvas.create_text(x, y, text=str(node_id), fill="black") self.nodes[node_id] = (x, y, oval_id, text_id) self.node_counter += 1 elif self.mode == "edge": # 查找点击位置附近的节点 clicked_node = self.get_clicked_node(x, y) if clicked_node is None: messagebox.showwarning("提示", "未点击到有效顶点") return if self.selected_node is None: # 选中第一个节点,高亮显示 self.selected_node = clicked_node x1, y1, oval_id, _ = self.nodes[clicked_node] self.canvas.itemconfig(oval_id, fill="orange") else: # 连接两个节点,创建边 if self.selected_node == clicked_node: messagebox.showwarning("提示", "不能连接同一个顶点") # 取消选中 x1, y1, oval_id, _ = self.nodes[self.selected_node] self.canvas.itemconfig(oval_id, fill="lightblue") self.selected_node = None return # 添加边到NetworkX图 self.G.add_edge(self.selected_node, clicked_node) # 在画布绘制边 x1, y1, _, _ = self.nodes[self.selected_node] x2, y2, _, _ = self.nodes[clicked_node] self.canvas.create_line(x1, y1, x2, y2, fill="black", width=2) # 恢复第一个节点的颜色 x1, y1, oval_id, _ = self.nodes[self.selected_node] self.canvas.itemconfig(oval_id, fill="lightblue") self.selected_node = None def get_clicked_node(self, x, y): # 遍历所有节点,检查点击位置是否在顶点范围内 for node_id, (nx, ny, oval_id, _) in self.nodes.items(): if (nx - 10 <= x <= nx + 10) and (ny - 10 <= y <= ny + 10): return node_id return None def show_adjacency_matrix(self): if len(self.G.nodes) == 0: messagebox.showinfo("提示", "当前没有顶点,无法生成邻接矩阵") return adj_matrix = nx.adjacency_matrix(self.G).todense() matrix_str = np.array2string(adj_matrix, formatter={'int': lambda x: str(x)}) messagebox.showinfo("邻接矩阵", matrix_str) def show_incidence_matrix(self): if len(self.G.nodes) == 0 or len(self.G.edges) == 0: messagebox.showinfo("提示", "当前顶点或边数量不足,无法生成关联矩阵") return inc_matrix = nx.incidence_matrix(self.G).todense() matrix_str = np.array2string(inc_matrix, formatter={'float': lambda x: str(int(x))}) messagebox.showinfo("关联矩阵", matrix_str) def run_dfs(self): if len(self.G.nodes) == 0: messagebox.showinfo("提示", "当前没有顶点,无法执行DFS") return start_node = simpledialog.askinteger("DFS遍历", "请输入起始顶点ID:", minvalue=0, maxvalue=self.node_counter-1) if start_node is None or start_node not in self.G.nodes: messagebox.showwarning("提示", "无效的起始顶点ID") return dfs_tree = nx.dfs_tree(self.G, source=start_node) dfs_order = list(dfs_tree.nodes()) messagebox.showinfo("DFS遍历结果", f"遍历顺序: {' -> '.join(map(str, dfs_order))}") def run_bfs(self): if len(self.G.nodes) == 0: messagebox.showinfo("提示", "当前没有顶点,无法执行BFS") return start_node = simpledialog.askinteger("BFS遍历", "请输入起始顶点ID:", minvalue=0, maxvalue=self.node_counter-1) if start_node is None or start_node not in self.G.nodes: messagebox.showwarning("提示", "无效的起始顶点ID") return bfs_tree = nx.bfs_tree(self.G, source=start_node) bfs_order = list(bfs_tree.nodes()) messagebox.showinfo("BFS遍历结果", f"遍历顺序: {' -> '.join(map(str, bfs_order))}") def run_dijkstra(self): if len(self.G.nodes) == 0: messagebox.showinfo("提示", "当前没有顶点,无法执行Dijkstra算法") return start_node = simpledialog.askinteger("Dijkstra算法", "请输入起始顶点ID:", minvalue=0, maxvalue=self.node_counter-1) end_node = simpledialog.askinteger("Dijkstra算法", "请输入目标顶点ID:", minvalue=0, maxvalue=self.node_counter-1) if start_node is None or end_node is None or start_node not in self.G.nodes or end_node not in self.G.nodes: messagebox.showwarning("提示", "无效的顶点ID") return if start_node == end_node: messagebox.showinfo("提示", "起始顶点与目标顶点相同,路径长度为0") return try: path = nx.dijkstra_path(self.G, source=start_node, target=end_node) length = nx.dijkstra_path_length(self.G, source=start_node, target=end_node) messagebox.showinfo("Dijkstra最短路径结果", f"最短路径: {' -> '.join(map(str, path))}\n路径长度: {length}") except nx.NetworkXNoPath: messagebox.showwarning("提示", "两个顶点之间没有路径") if __name__ == "__main__": root = tk.Tk() app = GraphEditor(root) root.mainloop()
功能说明
- 顶点放置: 点击菜单栏「模式」→「放置顶点」,在画布上点击任意位置即可添加顶点,顶点自动按顺序编号
- 边创建: 点击菜单栏「模式」→「创建边」,先点击第一个顶点(顶点会变为橙色),再点击第二个顶点,即可创建连接两者的边;若点击同一个顶点或空白区域,会取消选中状态
- 邻接/关联矩阵提取: 通过「功能」菜单选择对应选项,会弹出窗口显示矩阵内容(基于NetworkX内置方法生成)
- 图算法运行:
- DFS/BFS: 输入起始顶点ID,弹出遍历顺序结果
- Dijkstra: 输入起始和目标顶点ID,返回最短路径及长度(无路径时提示)
关键实现细节
- 用
self.nodes字典维护顶点ID与画布元素的映射,方便点击检测和视觉反馈 - 模式切换通过
self.mode变量控制画布点击事件的处理逻辑 - 所有图结构操作依赖NetworkX完成,画布仅负责视觉呈现,确保后续算法、矩阵提取的准确性
内容的提问来源于stack exchange,提问作者Has891
相关产品推荐
相关产品推荐

