使用Tkinter绘制箭头失败求助(含DFS实现代码)
DFS算法可视化中箭头无法显示的问题排查与修复
我在实现深度优先搜索(DFS)算法时,尝试用draw_arrow函数绘制节点间的箭头,但箭头始终无法显示。将draw_arrow移到window.mainloop()后也没能解决问题,以下是我的代码:
import random import tkinter as tk window = tk.Tk() window.geometry("800x800") canvas = tk.Canvas(window, width=800, height=800) canvas.pack() class Node: def __init__(self, number): self.number = number self.f = None self.d = None self.neighbor = [] self.pi = None self.color = "white" self.y = random.randint(100, 700) self.x = random.randint(100, 700) class Graph: def __init__(self): self.adjacency_list = [] self.edges = {} self.lines = [] self.arrows= [] def read_numbers(file_path): result = [] with open(file_path, 'r') as file: for line in file: numbers = line.strip().split() if len(numbers) == 2: num_array = [int(numbers[0]), int(numbers[1])] result.append(num_array) return result def create_adjacency_list(array): adj = [] numbers = [] nodes = [] for pair in array: if pair[0] not in numbers: new_node1 = Node(pair[0]) numbers.append(new_node1.number) adj.append(new_node1) if pair[1] not in numbers: new_node2 = Node(pair[1]) numbers.append(new_node2.number) adj.append(new_node2) for pair in array: main = next((x for x in adj if x.number == pair[1]), None) secondary = next((x for x in adj if x.number == pair[1]), None) main.neighbor.append(secondary) draw_arrow(canvas,main,secondary) return adj class Time(): def __init__(self, value): self.value = value def draw_circle(canvas, radius, node): x = node.x y = node.y canvas.create_oval(x - radius, y - radius, x + radius, y + radius, outline='black') canvas.create_text(x, y, text=str(node.number), fill='black', font=("Ariel",20)) def draw_arrow(canvas, start, end): return canvas.create_line(start.x, start.y, end.x, end.y, arrow=tk.LAST) def create_circles(graph, canvas): numbers = [] circles = [] for i in graph.adjacency_list: if i.number not in numbers: numbers.append(i.number) circles.append(draw_circle(canvas, 30, i)) for j in i.neighbor: if j.number not in numbers: numbers.append(j.number) circles.append(draw_circle(canvas, 30, j)) return circles def create_vertexes(graph): numbers = [] vertexes = [] for i in graph.adjacency_list: if i.number not in numbers: numbers.append(i.number) vertexes.append(i) for j in i.neighbor: if j.number not in numbers: numbers.append(j.number) vertexes.append(j) return vertexes def DFS(start_node, time, vertexes): time.value = 0 for u in vertexes: if u.color == "white": DFS_VISIT(u, time) def DFS_VISIT(u, time): for i in u.neighbor: if i.color == "white": graph.edges[u, i]= 'tree' elif i.color == "gray": graph.edges[u, i] = "back" elif i.d < i.f: graph.edges[u, i] = "forward" else: graph.edges[u, i] = "cross" time.value = time.value + 1 u.d = time.value u.color = "gray" for v in u.neighbor: if v.color == "white": v.pi = u DFS_VISIT(v, time) time.value = time.value + 1 u.f = time.value u.color = "black" time = Time(0) graph = Graph() numbers = read_numbers("./numbers.txt") graph.adjacency_list = create_adjacency_list(numbers) vertexes = create_vertexes(graph) circles = create_circles(graph, canvas) for i in graph.adjacency_list: for j in i.neighbor: graph.arrows.append(draw_arrow(canvas, i, j)) print(len(graph.adjacency_list)) window.mainloop()
问题根源与修复方案
邻接列表构建错误
在create_adjacency_list函数中,错误地将main和secondary都指向了pair[1]对应的节点,导致每个节点的邻居是自身,箭头从节点指向自己,自然无法看到。正确写法应为:main = next((x for x in adj if x.number == pair[0]), None) secondary = next((x for x in adj if x.number == pair[1]), None)冗余绘制导致箭头被覆盖
create_adjacency_list中额外调用的draw_arrow会让箭头先于圆圈绘制,最终被圆圈覆盖,建议删除这一行冗余代码,只保留最后循环中的箭头绘制逻辑。增加空值判断避免报错
为防止节点查找失败出现空指针错误,可在添加邻居时增加非空判断:if main and secondary: main.neighbor.append(secondary)
修复后的create_adjacency_list函数:
def create_adjacency_list(array): adj = [] numbers = [] for pair in array: if pair[0] not in numbers: new_node1 = Node(pair[0]) numbers.append(new_node1.number) adj.append(new_node1) if pair[1] not in numbers: new_node2 = Node(pair[1]) numbers.append(new_node2.number) adj.append(new_node2) for pair in array: main = next((x for x in adj if x.number == pair[0]), None) secondary = next((x for x in adj if x.number == pair[1]), None) if main and secondary: main.neighbor.append(secondary) return adj
完成以上修改后,箭头即可正常显示在节点之间。
内容的提问来源于stack exchange,提问作者user17630065
相关产品推荐
相关产品推荐

