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

求助: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:00:49