def _visualize_automaton(self, fa=None, title="有限自动机"):
if fa is None:
fa = self.current_fa
if not fa:
return
self.figure.clear()
ax = self.figure.add_subplot(111)
G = nx.MultiDiGraph()
# 1. 构建图结构(节点 + 边)
for state in fa.states:
G.add_node(state)
edge_data = []
for from_state, symbol_dict in fa.transitions.items():
for symbol, to_states in symbol_dict.items():
for to_state in to_states:
edge_data.append((from_state, to_state, symbol))
for from_state, to_state, symbol in edge_data:
G.add_edge(from_state, to_state, symbol=symbol)
# 2. 布局优化
try:
pos = nx.kamada_kawai_layout(G)
except:
pos = nx.spring_layout(G, k=0.5, iterations=50)
# 3. 绘制节点(含终结状态双圈)
nx.draw_networkx_nodes(
G, pos,
node_size=800,
node_color='white',
edgecolors='black',
ax=ax
)
# 标记终结状态(双圈)
for state in fa.final_states:
if state in pos:
x, y = pos[state]
outer = plt.Circle((x, y), 0.15, fill=False, edgecolor='black', linewidth=2)
inner = plt.Circle((x, y), 0.12, fill=True, color='white', edgecolor='black', linewidth=2)
ax.add_artist(outer)
ax.add_artist(inner)
ax.set_aspect('equal')
# 4. 绘制边(优化弯曲、颜色区分重叠边)
edges = G.edges(data=True, keys=True)
# 用字典记录 <起点-终点> 对应的边符号列表,避免重复处理
edge_symbol_map = defaultdict(list)
for u, v, _, data in edges:
edge_symbol_map[(u, v)].append(data["symbol"])
# 为每组边分配不同颜色(可选,区分重叠边)
color_map = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
for idx, (u, v) in enumerate(edge_symbol_map.keys()):
group_idx = idx % len(color_map)
# 绘制当前 <u-v> 所有边(按颜色循环)
for symbol in edge_symbol_map[(u, v)]:
nx.draw_networkx_edges(
G, pos,
edgelist=[(u, v)],
width=2,
alpha=0.8,
arrows=True,
arrowsize=20,
connectionstyle='arc3,rad=0.3',
edge_color=color_map[group_idx],
ax=ax
)
# 5. 绘制边标签(优化版:动态避让重叠)
for (u, v), symbols in edge_symbol_map.items():
total_edges = len(symbols)
# 计算边的基础弧度(用于标签偏移)
x0, y0 = pos[u]
x1, y1 = pos[v]
dx = x1 - x0
dy = y1 - y0
base_rad = 0.3 if total_edges == 1 else 0.2
for i, symbol in enumerate(symbols):
# 计算边中点
mx, my = (x0 + x1) / 2, (y0 + y1) / 2
# 计算边的角度(弧度制)
angle = math.atan2(dy, dx)
# 多条边时,分散标签位置(基于边索引调整弧度)
if total_edges > 1:
# 让标签沿边的垂直方向分散
rad_offset = 0.1 * (i - (total_edges - 1) / 2)
else:
rad_offset = 0
# 计算最终偏移(结合基础弧度和分散偏移)
offset_x = (base_rad + rad_offset) * math.sin(angle)
offset_y = -(base_rad + rad_offset) * math.cos(angle)
# 自环边特殊处理(标签放在环外侧)
if u == v:
offset_x = 0.2 # 固定右侧偏移
offset_y = 0.1 # 微调垂直位置
ax.text(
mx + offset_x, my + offset_y, symbol,
fontsize=12,
fontfamily='SimHei',
ha='center', va='center',
bbox=dict(facecolor='white', alpha=0.9, edgecolor='none', pad=0.2)
)
# 6. 绘制节点标签、起始箭头
nx.draw_networkx_labels(
G, pos,
font_size=12,
font_family='SimHei',
ax=ax
)
if fa.start_state and fa.start_state in pos:
start_x, start_y = pos[fa.start_state]
ax.arrow(
start_x, start_y + 0.35,
0, -0.2,
head_width=0.05,
head_length=0.05,
fc='black', ec='black',
linewidth=2,
zorder=5
)
ax.set_title(f"{title} 可视化", fontsize=16)
ax.axis('off')
self.canvas.draw()帮我重新写这个绘图函数,目的是为了图形化NFA,DFA