|
|
# -*- coding: utf-8 -*-
|
|
|
# Time : 2023/10/30 15:49
|
|
|
# Author : lirunsheng
|
|
|
# User : l'r's
|
|
|
# Software: PyCharm
|
|
|
# File : X4.py
|
|
|
from tkinter import Tk
|
|
|
import random
|
|
|
import math
|
|
|
import time
|
|
|
import tkinter as tk
|
|
|
from tkinter import *
|
|
|
from tkinter import ttk
|
|
|
from tkinter import messagebox
|
|
|
import numpy as np
|
|
|
import networkx as nx
|
|
|
from PIL import Image, ImageTk
|
|
|
|
|
|
MODE = 0
|
|
|
BLUE = "#0080FF"
|
|
|
BLACK = "#000000"
|
|
|
RED = "#FFAAAA"
|
|
|
YELLOW = "#FFFACD"
|
|
|
LINE = '#c8d2c8'
|
|
|
GREY = '#070b19'
|
|
|
GREEN = '#5ba585'
|
|
|
NODE = '#33a8cd'
|
|
|
ZERO = 'gold'
|
|
|
MARK_NODE = 0
|
|
|
Specify_Nodes = ''
|
|
|
Edges_list = []
|
|
|
NAME = []
|
|
|
CLICK = [0, 0, 0]
|
|
|
|
|
|
List_Image = []
|
|
|
|
|
|
class Data:
|
|
|
def __init__(self, source: int, num: int, mold: int):
|
|
|
global NAME
|
|
|
if num <= 0 or num > 10:
|
|
|
raise ValueError("num must be a positive integer")
|
|
|
self.styple = mold
|
|
|
self.edgeinfo = 1 # 是否显示详情
|
|
|
self.nodes_num = num # 节点个数
|
|
|
self.ang = 360 / self.nodes_num # 圆心顶点角度
|
|
|
self.R = 300 # 外接圆半径
|
|
|
self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离
|
|
|
self.canvas_len = int(2 * self.R + 80) # 画布边长
|
|
|
self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标
|
|
|
self.drop = []
|
|
|
self.index = 0
|
|
|
if source == 3 or source == 2: # 指定删除
|
|
|
# name = [str(i) for i in range(1, num + 2)]
|
|
|
# print(name)
|
|
|
if source == 2:
|
|
|
NAME.pop()
|
|
|
else:
|
|
|
# print(Specify_Nodes)
|
|
|
NAME.remove(Specify_Nodes)
|
|
|
self.name = NAME
|
|
|
self.coordinate = self.coord_creat() # 纯节点坐标
|
|
|
self.Nodes = self.nodes_creat() # 创建节点列表
|
|
|
# print(self.Nodes)
|
|
|
self.Edges = self.edges_delete(source) # 创建第一条连接
|
|
|
else:
|
|
|
if source == 1:
|
|
|
name = str(int(NAME[-1])+1)
|
|
|
# print(name)
|
|
|
NAME.append(name)
|
|
|
else:
|
|
|
NAME = [str(i) for i in range(1, num + 1)]
|
|
|
# print(NAME)
|
|
|
self.name = NAME
|
|
|
self.coordinate = self.coord_creat() # 纯节点坐标
|
|
|
self.Nodes = self.nodes_creat() # 创建节点列表
|
|
|
# print(self.Nodes)
|
|
|
self.Edges = self.edges_creat(source) # 创建第一条连接
|
|
|
self.edge_add(2, source) # 创建第2条连接
|
|
|
self.edge_add(3, source) # 创建第3条连接
|
|
|
|
|
|
def nodes_creat(self, n_sum=None):
|
|
|
if n_sum == None:
|
|
|
n_sum = self.nodes_num
|
|
|
nodes = [] # 初始化node表
|
|
|
# 设置画布中心点坐标x0,y0
|
|
|
x0 = self.center[0]
|
|
|
y0 = self.center[1]
|
|
|
for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标
|
|
|
rad = math.radians(i * self.ang) # 计算第i个点的弧度
|
|
|
x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标
|
|
|
y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标
|
|
|
# name = '' + str(i + 1) # 给第i个顶点命名
|
|
|
name = '' + self.name[i]
|
|
|
mark = 0 # 节点为未标记
|
|
|
dominator = 1 # 设置为必经节点
|
|
|
nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中
|
|
|
return nodes
|
|
|
|
|
|
def edges_delete(self, source):
|
|
|
edges = [] # 初始化所有链接集合
|
|
|
ser = 0
|
|
|
# print(str(int(Specify_Nodes) + 1))
|
|
|
for list_edge in Edges_list:
|
|
|
# print(list_edge)
|
|
|
edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6],
|
|
|
list_edge[7], list_edge[8]]) # 将连接加入到边的集合中
|
|
|
nox = [int(num) for num in edges[ser][5].split('-')]
|
|
|
# print(nox)
|
|
|
if len(nox) == 2:
|
|
|
ss = edges[ser][5]
|
|
|
else:
|
|
|
ss = edges[ser][5][:-2]
|
|
|
# print(ss)
|
|
|
if source == 2:
|
|
|
if str(len(self.Nodes)+1) in ss:
|
|
|
# print(edges[ser])
|
|
|
edges.pop()
|
|
|
ser -= 1
|
|
|
else:
|
|
|
if Specify_Nodes in ss:
|
|
|
# print(edges[ser])
|
|
|
edges.pop()
|
|
|
ser -= 1
|
|
|
ser += 1
|
|
|
return edges
|
|
|
|
|
|
def edges_creat(self, source):
|
|
|
edges = [] # 初始化所有链接集合
|
|
|
ser = 0 # 初始化链接对象编号
|
|
|
nodes = self.Nodes.copy() # 复制节点对象
|
|
|
nodes1 = self.Nodes.copy() # 复制节点对象
|
|
|
# print(nodes1)
|
|
|
self.drop = []
|
|
|
for node in nodes: # 遍历节点对象
|
|
|
if node in nodes1: # 删除nodes1中当期遍历的节点信息
|
|
|
nodes1.remove(node)
|
|
|
# print(nodes1)
|
|
|
for node1 in nodes1: # 遍历删除后的节点信息
|
|
|
n1 = node[0] # 节点对象编号1
|
|
|
n2 = node1[0] # 节点对象编号2
|
|
|
if self.styple == 1:
|
|
|
dist_c = random.randint(1, 99) # 随机生成距离成本
|
|
|
time_c = random.randint(1, 99) # 随机生成链接的时间
|
|
|
else:
|
|
|
if self.drop.count(n2) > 3 or self.drop.count(n1) > 3:
|
|
|
dist_c = random.randint(101, 120) # 随机生成距离成本
|
|
|
time_c = random.randint(101, 120) # 随机生成链接的时间
|
|
|
else:
|
|
|
dist_c = random.randint(10, 120) # 随机生成距离成本
|
|
|
time_c = random.randint(10, 120) # 随机生成链接的时间
|
|
|
seq = 1 # 链接序号
|
|
|
mark = False # 连接是否标记
|
|
|
tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签
|
|
|
enable = 1 # 连接是否可用
|
|
|
if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示
|
|
|
enable = 0
|
|
|
else:
|
|
|
self.drop.append(n1)
|
|
|
self.drop.append(n2)
|
|
|
if source == 1:
|
|
|
# print(n2, len(self.Nodes))
|
|
|
if n2 == len(self.Nodes)-1:
|
|
|
# print('进来了')
|
|
|
edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中
|
|
|
else:
|
|
|
# print(self.index)
|
|
|
edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2],
|
|
|
Edges_list[self.index][3], Edges_list[self.index][4],
|
|
|
Edges_list[self.index][5],Edges_list[self.index][6],
|
|
|
Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中
|
|
|
self.index += 1
|
|
|
else:
|
|
|
edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中
|
|
|
ser += 1 # 计数加一
|
|
|
return edges
|
|
|
|
|
|
def edge_add(self, no, source):
|
|
|
nodes = self.Nodes.copy() # 复制节点对象
|
|
|
nodes1 = self.Nodes.copy() # 复制节点对象
|
|
|
for node in nodes: # 遍历节点对象
|
|
|
if node in nodes1: # 删除nodes1中当期遍历的节点信息
|
|
|
nodes1.remove(node)
|
|
|
for node1 in nodes1: # 遍历删除后的节点信息
|
|
|
ser = len(self.Edges) # 链接序号
|
|
|
n1 = node[0] # 节点对象编号1
|
|
|
n2 = node1[0] # 节点对象编号2
|
|
|
show = 0 # 设置为不显示
|
|
|
mark = False # 连接是否标记
|
|
|
tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签
|
|
|
dist_c = random.randint(30, 100) # 距离成本
|
|
|
time_c = random.randint(1, 30) # 链接的时间
|
|
|
enable = no # 连接是否可用
|
|
|
if source == 1:
|
|
|
if n2 == len(self.Nodes)-1:
|
|
|
# print('进来了')
|
|
|
self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中
|
|
|
else:
|
|
|
# print(no ,self.index)
|
|
|
self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2],
|
|
|
Edges_list[self.index][3], Edges_list[self.index][4],
|
|
|
Edges_list[self.index][5], Edges_list[self.index][6],
|
|
|
Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中
|
|
|
|
|
|
self.index += 1
|
|
|
else:
|
|
|
self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable])
|
|
|
|
|
|
def coord_creat(self):
|
|
|
'''返回每个节点的坐标'''
|
|
|
coordinate = []
|
|
|
x0 = self.center[0]
|
|
|
y0 = self.center[1]
|
|
|
for i in range(self.nodes_num):
|
|
|
rad = math.radians(i * self.ang)
|
|
|
x = int(math.cos(rad) * self.R)
|
|
|
y = int(math.sin(rad) * self.R)
|
|
|
coordinate.append((x0 + x, y0 + y))
|
|
|
return coordinate
|
|
|
|
|
|
def node_co(self, key): # 通过节点编号返回编号的坐标
|
|
|
"""通过节点编号返回编号的坐标"""
|
|
|
nodes = self.Nodes
|
|
|
for n in nodes:
|
|
|
if n[1] == str(key + 1):
|
|
|
return n[3]
|
|
|
|
|
|
def node_no(self, key): # 通过节点编号返回编号的坐标
|
|
|
"""通过节点编号返回编号的坐标"""
|
|
|
nodes = self.Nodes
|
|
|
for n in nodes:
|
|
|
if n[1] == str(key + 1):
|
|
|
return n[0]
|
|
|
def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标
|
|
|
result = []
|
|
|
for i in range(n):
|
|
|
for j in range(i + 1, n):
|
|
|
result.append((i, j))
|
|
|
return result
|
|
|
|
|
|
# G = Graph()
|
|
|
# print(1)
|
|
|
nodes = 6 # 设置初始节点数量
|
|
|
d = Data(0, nodes, 0)
|
|
|
|
|
|
class Graph:
|
|
|
# Graph定义
|
|
|
def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息
|
|
|
if d.edgeinfo == 0: # 若设置为不显示则直接返回
|
|
|
return
|
|
|
# print(n1)
|
|
|
# print(n2)
|
|
|
x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标
|
|
|
y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标
|
|
|
if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示
|
|
|
reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度
|
|
|
ang = round(math.degrees(reat)) + 90 # 根据弧度转角度
|
|
|
if ang > 89: # 斜度大于90度调整坐标
|
|
|
text_x, text_y = x + 15, y
|
|
|
else: # 斜度大于90度调整坐标
|
|
|
text_x, text_y = x, y + 15
|
|
|
else: # 斜率为0度时的显示
|
|
|
ang = round(math.degrees(0)) # 根据弧度转角度
|
|
|
text_x, text_y = x - 30, y + 10
|
|
|
# print(info.split(' ')[-1])
|
|
|
cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字
|
|
|
|
|
|
def draw(self):
|
|
|
cv.delete('all') # 清空画布
|
|
|
cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW)
|
|
|
dis, tim = self.edges_show(cv, d.Edges) # 绘制边
|
|
|
self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息
|
|
|
self.nodes_show(cv) # 绘制点
|
|
|
cv.update() # 更新画布
|
|
|
|
|
|
|
|
|
def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int):
|
|
|
labs = ["节点数目", "时间", "距离(成本)"]
|
|
|
x0 = d.canvas_len // 3
|
|
|
for i in range(len(labs)):
|
|
|
lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12))
|
|
|
if i == 0:
|
|
|
lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12))
|
|
|
elif i == 1:
|
|
|
lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12))
|
|
|
else:
|
|
|
lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(x=10 + x0 * i, y=10, width=80, height=30)
|
|
|
lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30)
|
|
|
lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12))
|
|
|
lab1.place(x=170 + x0, y=10, width=50, height=30)
|
|
|
lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12))
|
|
|
lab1.place(x=170 + x0 * 2, y=10, width=50, height=30)
|
|
|
|
|
|
def nodes_show(self, cv: tk.Canvas):
|
|
|
COL = NODE # 初始化节点颜色
|
|
|
for i in d.Nodes: # 遍历每一个节点
|
|
|
x, y = i[3] # 记录该节点坐标
|
|
|
if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色
|
|
|
COL = RED
|
|
|
elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色
|
|
|
COL = ZERO
|
|
|
elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能)
|
|
|
COL = 'gold'
|
|
|
else: # 否则则使用初始颜色
|
|
|
COL = NODE
|
|
|
if i[4] == 1: # 如果是必经节点,则加黑色边框
|
|
|
w = 1
|
|
|
else: # 如果是非必经节点,则没有边框
|
|
|
w = 0
|
|
|
# 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点
|
|
|
cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3')
|
|
|
cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18))
|
|
|
cv.update()
|
|
|
|
|
|
def edges_show(self, cv: tk.Canvas, edges: d.Edges):
|
|
|
"""
|
|
|
显示链接
|
|
|
:param cv: tkcanvas对象
|
|
|
:return:
|
|
|
"""
|
|
|
distance = 0 # 初始化被标记的线的总路径
|
|
|
time1 = 0 # 初始化被标记的线的总时间
|
|
|
for edge in edges:
|
|
|
node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1
|
|
|
node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2
|
|
|
# print(node_1,node_2)
|
|
|
if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线
|
|
|
self.more_edge_show(2, edge)
|
|
|
elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线
|
|
|
self.more_edge_show(3, edge)
|
|
|
if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在
|
|
|
self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2)
|
|
|
point = [node_1, node_2]
|
|
|
if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制
|
|
|
if MODE == 1:
|
|
|
cv.create_line(point, fill=RED, width=4)
|
|
|
elif MODE == 2:
|
|
|
cv.create_line(point, fill=BLUE, width=4)
|
|
|
else:
|
|
|
cv.create_line(point, fill=GREEN, width=4)
|
|
|
distance += edge[6] # 更新总路径
|
|
|
time1 += edge[7] # 更新总时间
|
|
|
elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记
|
|
|
cv.create_line(point, fill=LINE, width=2)
|
|
|
if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记
|
|
|
cv.create_line(point, fill=BLUE, width=5, dash=(4, 14))
|
|
|
return (distance, time1) # 返回标记路径的总时间与路程
|
|
|
|
|
|
# def node_edge(self, a, b): # 根据点的编号找边的编号
|
|
|
# if a > b:
|
|
|
# x = a
|
|
|
# a = b
|
|
|
# b = x
|
|
|
# return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1)
|
|
|
|
|
|
def node_edge(self, a, b): # 根据点的编号找边的编号
|
|
|
for edge in d.Edges:
|
|
|
if a == edge[1] and b == edge[2]:
|
|
|
return edge[0]
|
|
|
if b == edge[1] and a == edge[2]:
|
|
|
return edge[0]
|
|
|
|
|
|
def way_node_edge(self, a, b): # 根据点的编号找边的编号
|
|
|
way = []
|
|
|
for node in d.Nodes:
|
|
|
way.append(int(node[1]))
|
|
|
# print(way)
|
|
|
# print(a, b)
|
|
|
x, y = way[a]-1, way[b]-1
|
|
|
for edge in d.Edges:
|
|
|
if x == edge[1] and y == edge[2]:
|
|
|
return edge[0]
|
|
|
if y == edge[1] and x == edge[2]:
|
|
|
return edge[0]
|
|
|
|
|
|
def delete_all(self):
|
|
|
cv.delete('all') # 清空画布
|
|
|
cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW)
|
|
|
self.lab_show(frame_top, 0, 0, 0) # 显示边信息
|
|
|
W.user_way = [0]
|
|
|
window(2)
|
|
|
|
|
|
class Edge:
|
|
|
def __init__(self):
|
|
|
self.mark = []
|
|
|
# 记录已标记的节点数目(连接操作)
|
|
|
self.user_cur = 0
|
|
|
# 记录用户所在点
|
|
|
|
|
|
def node_edge(self, no, n1, n2): # 根据节点返回编号
|
|
|
if n1 > n2:
|
|
|
n = n1
|
|
|
n1 = n2
|
|
|
n2 = n
|
|
|
for e in d.Edges:
|
|
|
if e[1] == n1 and e[2] == n2 and e[8] == no:
|
|
|
return e[0]
|
|
|
return -1
|
|
|
def edge_del_all(self, mode):
|
|
|
# mode = 1:优先最短路径
|
|
|
# mode = 2:优先最短时间
|
|
|
# cur1 = node_edge()
|
|
|
if mode == 1:
|
|
|
for e in d.Edges:
|
|
|
if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1):
|
|
|
self.edge_merge_cost(e[1], e[2])
|
|
|
else:
|
|
|
for e in d.Edges:
|
|
|
if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1):
|
|
|
self.edge_merge_time(e[1], e[2])
|
|
|
G.draw()
|
|
|
|
|
|
def edge_merge_cost(self, n1, n2):
|
|
|
cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号
|
|
|
cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号
|
|
|
cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号
|
|
|
edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费
|
|
|
valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边
|
|
|
valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边
|
|
|
for idx, (edge, _) in enumerate(valid_edges):
|
|
|
d.Edges[edge][8] = idx + 1 # 设置边的优先级
|
|
|
d.Edges[edge][0] = edge # 确保边的索引正确
|
|
|
for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示
|
|
|
d.Edges[edge][3] = 0
|
|
|
def edge_merge_time(self, n1, n2):
|
|
|
cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号
|
|
|
cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号
|
|
|
cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号
|
|
|
edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费
|
|
|
valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边
|
|
|
valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边
|
|
|
for idx, (edge, _) in enumerate(valid_edges):
|
|
|
d.Edges[edge][8] = idx + 1 # 设置边的优先级
|
|
|
d.Edges[edge][0] = edge # 确保边的索引正确
|
|
|
for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示
|
|
|
d.Edges[edge][3] = 0
|
|
|
|
|
|
class Solve:
|
|
|
def __init__(self):
|
|
|
self.ans = [] # 记录答案
|
|
|
self.mutians = [] # 若有多个最短路,则保存在mutians中
|
|
|
self.way_sum = 0 # 记录遍历的路径数目
|
|
|
self.minn = float("inf") # 初始化最小值为正无穷
|
|
|
self.start = 0 # 起始点
|
|
|
self.end = 0 # 终点
|
|
|
self.mark = [] # 记录该点是否已经经过
|
|
|
self.user_cur = 0 # 记录用户所在点
|
|
|
self.cura = -1 # 记录用户选择的第一个点
|
|
|
self.curb = -1 # 记录用户选择的第二个点
|
|
|
self.con = 0 # 记录必经的节点数目
|
|
|
|
|
|
def node_greedy(self): # 贪心
|
|
|
self.start = time.time() # 记录开始时间
|
|
|
self.way_sum = 0
|
|
|
self.ans.clear() # 初始化答案列表
|
|
|
E.edge_del_all(1) # 两点间仅保留距离最短路径
|
|
|
for edge in d.Edges: # 清空标记
|
|
|
edge[4] = False
|
|
|
flag = [0] * len(d.Nodes) # 初始化已经过点
|
|
|
cur = 0 # 初始化起始点
|
|
|
flag[cur] = 1 # 将起始点标记为已经过
|
|
|
l = len(d.Nodes)
|
|
|
# print(l)
|
|
|
distances = [[math.inf for _ in range(l)] for _ in range(l)]
|
|
|
result = d.node_coordinate(l)
|
|
|
for j in d.Edges: # 每一步选择当前最短的链接加到路径中去
|
|
|
if j[0] >= len(result):
|
|
|
break
|
|
|
else:
|
|
|
if j[3] == 1:
|
|
|
distances[result[j[0]][0]][result[j[0]][1]] = j[6]
|
|
|
distances[result[j[0]][1]][result[j[0]][0]] = j[6]
|
|
|
# print(distances)
|
|
|
ways = self.greedy(distances)
|
|
|
self.ans = ways.copy()
|
|
|
# 如果找到的路径不合法,则表示贪心法无法求解得到合法路径
|
|
|
print(self.way_sum)
|
|
|
# for i in distances:
|
|
|
# print(i)
|
|
|
if len(ways) != l+1 :
|
|
|
tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径')
|
|
|
print(ways)
|
|
|
for way in range(len(ways) - 1): # 标记一条路径
|
|
|
d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True
|
|
|
self.ans.clear()
|
|
|
self.end = time.time() # 记录结束时间
|
|
|
return -1
|
|
|
for way in range(len(ways) - 1): # 标记一条路径
|
|
|
d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True
|
|
|
# print(ways[len(ways) - 1], i)
|
|
|
# d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线
|
|
|
# print(ways)
|
|
|
tk.messagebox.askokcancel(title='结果', message='贪心算法路径')
|
|
|
self.end = time.time() # 记录结束时间
|
|
|
def greedy(self,distances):
|
|
|
num_cities = len(distances)
|
|
|
visited = [False] * num_cities
|
|
|
path = [0] # 起始城市为0
|
|
|
visited[0] = True
|
|
|
for _ in range(num_cities - 1):
|
|
|
curr_city = path[-1]
|
|
|
min_distance = float('inf')
|
|
|
next_city = None
|
|
|
|
|
|
for city in range(num_cities):
|
|
|
if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0:
|
|
|
min_distance = distances[curr_city][city]
|
|
|
next_city = city
|
|
|
self.way_sum += 1 # 遍历路径的数目加一
|
|
|
|
|
|
if next_city is not None:
|
|
|
path.append(next_city)
|
|
|
visited[next_city] = True
|
|
|
if distances[path[-1]][0] != np.inf:
|
|
|
path.append(0) # 回到起始城市形成闭合路径
|
|
|
# print(path)
|
|
|
return path
|
|
|
|
|
|
def tsp_backtracking(self):
|
|
|
self.start = time.time() # 记录开始时间
|
|
|
self.way_sum = 0
|
|
|
self.ans.clear() # 初始化答案列表
|
|
|
E.edge_del_all(1) # 两点间仅保留距离最短路径
|
|
|
for edge in d.Edges: # 清空标记
|
|
|
edge[4] = False
|
|
|
flag = [0] * len(d.Nodes) # 初始化已经过点
|
|
|
cur = 0 # 初始化起始点
|
|
|
flag[cur] = 1 # 将起始点标记为已经过
|
|
|
l = len(d.Nodes)
|
|
|
# print(l)
|
|
|
distances = [[math.inf for _ in range(l)] for _ in range(l)]
|
|
|
result = d.node_coordinate(l)
|
|
|
for j in d.Edges: # 每一步选择当前最短的链接加到路径中去
|
|
|
if j[0] >= len(result):
|
|
|
break
|
|
|
else:
|
|
|
if j[3] == 1:
|
|
|
distances[result[j[0]][0]][result[j[0]][1]] = j[6]
|
|
|
distances[result[j[0]][1]][result[j[0]][0]] = j[6]
|
|
|
# print(distances)
|
|
|
ways = self.backtracking(distances)
|
|
|
print(self.way_sum)
|
|
|
|
|
|
if ways: # 判断路径是否存在
|
|
|
self.ans = ways.copy()
|
|
|
for way in range(len(ways) - 1): # 标记一条路径
|
|
|
d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True
|
|
|
tk.messagebox.askokcancel(title='结果', message='回溯算法路径')
|
|
|
self.end = time.time() # 记录结束时间
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径')
|
|
|
self.end = time.time() # 记录结束时间
|
|
|
return -1
|
|
|
|
|
|
def backtracking(self, distances):
|
|
|
num_cities = len(distances)
|
|
|
path = [0] # 起始城市为0
|
|
|
visited = [False] * num_cities
|
|
|
visited[0] = True
|
|
|
min_distance = float('inf')
|
|
|
shortest_path = None
|
|
|
|
|
|
def backtrack(curr_city, curr_distance, visited, path):
|
|
|
nonlocal min_distance, shortest_path
|
|
|
if len(path) == num_cities:
|
|
|
# 到达所有城市,更新最短路径
|
|
|
if curr_distance + distances[curr_city][0] < min_distance:
|
|
|
min_distance = curr_distance + distances[curr_city][0]
|
|
|
shortest_path = path + [0]
|
|
|
else:
|
|
|
for next_city in range(num_cities):
|
|
|
if not visited[next_city]:
|
|
|
# 选择下一个未访问的城市
|
|
|
visited[next_city] = True
|
|
|
path.append(next_city)
|
|
|
new_distance = curr_distance + distances[curr_city][next_city]
|
|
|
self.way_sum += 1 # 遍历路径的数目加一
|
|
|
# 剪枝条件:当前路径已经大于最短路径,不继续搜索
|
|
|
if new_distance < min_distance:
|
|
|
backtrack(next_city, new_distance, visited, path)
|
|
|
# 回溯
|
|
|
visited[next_city] = False
|
|
|
path.pop()
|
|
|
|
|
|
backtrack(0, 0, visited, path)
|
|
|
return shortest_path
|
|
|
|
|
|
def auto_solve(self, number):
|
|
|
global MODE, MARK_NODE
|
|
|
MARK_NODE = 0
|
|
|
MODE = 2
|
|
|
if number == 1: # 回溯
|
|
|
self.tsp_backtracking()
|
|
|
elif number == 2:
|
|
|
self.node_greedy() # 贪心
|
|
|
else: # 深度优先算法
|
|
|
self.node_dfs()
|
|
|
window(1)
|
|
|
G.draw()
|
|
|
|
|
|
def node_dfs(self): # 遍历 深度优先算法
|
|
|
self.start = time.time() # 记录开始时间
|
|
|
self.way_sum = 0
|
|
|
self.ans.clear() # 初始化答案列表
|
|
|
E.edge_del_all(1) # 两点间仅保留距离最短路径
|
|
|
for edge in d.Edges: # 清空标记
|
|
|
edge[4] = False
|
|
|
i = 0 # 起始点
|
|
|
flag = [0] * len(d.Nodes) # 初始化已经过点
|
|
|
cur = i # 初始化起始点
|
|
|
flag[cur] = 1 # 将起始点标记为已经过
|
|
|
l = len(d.Nodes)
|
|
|
distances = [[math.inf for _ in range(l)] for _ in range(l)]
|
|
|
result = d.node_coordinate(l)
|
|
|
for j in d.Edges: # 每一步选择当前最短的链接加到路径中去
|
|
|
if j[0] >= len(result):
|
|
|
break
|
|
|
else:
|
|
|
if j[3] == 1:
|
|
|
distances[result[j[0]][0]][result[j[0]][1]] = j[6]
|
|
|
distances[result[j[0]][1]][result[j[0]][0]] = j[6]
|
|
|
cost, ways = self.dfs(distances)
|
|
|
print(self.way_sum)
|
|
|
self.ans = ways.copy()
|
|
|
if len(ways) != l + 1:
|
|
|
tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径')
|
|
|
# print(ways)
|
|
|
for way in range(len(ways) - 1): # 标记一条路径
|
|
|
d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True
|
|
|
self.end = time.time() # 记录结束时间
|
|
|
self.ans.clear()
|
|
|
return -1
|
|
|
for way in range(len(ways) - 1): # 标记一条路径
|
|
|
d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True
|
|
|
tk.messagebox.askokcancel(title='结果', message='dfs算法路径')
|
|
|
self.end = time.time() # 记录结束时间
|
|
|
|
|
|
|
|
|
def dfs_tsp(self,graph, start, current, path,
|
|
|
visited, cost, min_cost, min_path):
|
|
|
if len(path) == len(graph) and graph[
|
|
|
current][start] != np.inf:
|
|
|
path.append(start)
|
|
|
cost += graph[current][start]
|
|
|
if cost < min_cost[0]:
|
|
|
min_cost[0] = cost
|
|
|
min_path[0] = path.copy()
|
|
|
path.pop()
|
|
|
cost -= graph[current][start]
|
|
|
return
|
|
|
for next_node in range(len(graph)):
|
|
|
if graph[current][next_node] != np.inf and \
|
|
|
not visited[next_node]:
|
|
|
visited[next_node] = True
|
|
|
path.append(next_node)
|
|
|
cost += graph[current][next_node]
|
|
|
self.way_sum += 1 # 遍历路径的数目加一
|
|
|
self.dfs_tsp(graph, start, next_node, path,
|
|
|
visited, cost, min_cost, min_path)
|
|
|
visited[next_node] = False
|
|
|
path.pop()
|
|
|
cost -= graph[current][next_node]
|
|
|
def dfs(self, graph):
|
|
|
n = len(graph)
|
|
|
min_cost = [float('inf')]
|
|
|
min_path = [[]]
|
|
|
|
|
|
for start_node in range(n):
|
|
|
visited = [False] * n
|
|
|
path = [start_node]
|
|
|
cost = 0
|
|
|
visited[start_node] = True
|
|
|
self.dfs_tsp(graph, start_node, start_node, path,
|
|
|
visited, cost, min_cost, min_path)
|
|
|
return min_cost[0], min_path[0]
|
|
|
|
|
|
def find_hamiltonian_cycles(self,start, distances):
|
|
|
n = len(distances)
|
|
|
path = [start]
|
|
|
cycles = []
|
|
|
|
|
|
def is_valid(node, position):
|
|
|
if distances[path[position - 1]][node] == np.inf:
|
|
|
return False
|
|
|
if node in path:
|
|
|
return False
|
|
|
return True
|
|
|
def find_paths(node):
|
|
|
for next_node in range(n):
|
|
|
if is_valid(next_node, len(path)):
|
|
|
path.append(next_node)
|
|
|
if len(path) < n:
|
|
|
find_paths(next_node)
|
|
|
elif len(path) == n and distances[path[-1]][start] != np.inf:
|
|
|
cycles.append(path + [start])
|
|
|
path.pop()
|
|
|
find_paths(start)
|
|
|
return cycles
|
|
|
def check(self):
|
|
|
l = len(d.Nodes)
|
|
|
distances = [[math.inf for _ in range(l)] for _ in range(l)]
|
|
|
result = d.node_coordinate(l)
|
|
|
for j in d.Edges: # 将路径中的
|
|
|
if j[0] >= len(result):
|
|
|
break
|
|
|
else:
|
|
|
if j[6] < 99 and j[7] < 99:
|
|
|
distances[result[j[0]][0]][result[j[0]][1]] = j[6]
|
|
|
distances[result[j[0]][1]][result[j[0]][0]] = j[6]
|
|
|
all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances))
|
|
|
l = len(all_hamiltonian_cycles)
|
|
|
if l:
|
|
|
for cycles in all_hamiltonian_cycles:
|
|
|
dis_ans = 0
|
|
|
for i in range(d.nodes_num):
|
|
|
dis_ans += d.Edges[G.way_node_edge(cycles[i], cycles[i + 1])][6]
|
|
|
print(cycles,dis_ans)
|
|
|
if all_hamiltonian_cycles:
|
|
|
tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n')
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='结果', message='该图不能完全互通')
|
|
|
|
|
|
def node_edge(n1, n2, no=1): # 根据点的编号找边的编号
|
|
|
if n1 > n2: # 为了防止充分编号,让n1小于n2
|
|
|
n1, n2 = n2, n1
|
|
|
for e in d.Edges: # 遍历所有路径,找到符合要求的返回
|
|
|
if e[1] == n1 and e[2] == n2 and e[8] == no:
|
|
|
return e[0]
|
|
|
return -1 # 若找不到,则返回-1
|
|
|
|
|
|
class Ways():
|
|
|
def __init__(self):
|
|
|
self.user_way = [0] # 记录用户路径
|
|
|
self.user_cur = 0 # 记录用户所在点
|
|
|
self.user_flag = set() # 记录用户已经过的点
|
|
|
def auto_ways(self): # 标记用户路径
|
|
|
for w in d.Edges: # 清空路径标记
|
|
|
w[4] = 0
|
|
|
for N in d.Nodes: # 清空节点标记
|
|
|
N[2] = 0
|
|
|
for w in self.user_way: # 标记用户已选的点
|
|
|
d.Nodes[w][2] = 5
|
|
|
# 标记用户经过的路径
|
|
|
for w in range(len(self.user_way) - 1):
|
|
|
d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1
|
|
|
# 如果已经访问所有节点,自动回到初始节点
|
|
|
if len(set(self.user_way)) == len(d.Nodes):
|
|
|
d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1
|
|
|
G.draw() # 按照标记重新绘制图形
|
|
|
|
|
|
def check_user_best(self): # 检查用户路径是不是最佳路径
|
|
|
s.node_dfs()
|
|
|
if not len(s.ans):
|
|
|
return -1, 0
|
|
|
# 长度不一样肯定不匹配
|
|
|
if not len(self.user_way) == len(s.ans):
|
|
|
return 0, 0
|
|
|
dis_user = 0
|
|
|
dis_ans = 0
|
|
|
num = len(self.user_way)
|
|
|
for i in range(num - 1):
|
|
|
dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6]
|
|
|
dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6]
|
|
|
return dis_ans, dis_user
|
|
|
|
|
|
def show_best_ans(self, top, best, user):
|
|
|
lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20))
|
|
|
b_way = ""
|
|
|
# print(s.ans)
|
|
|
for a in s.ans:
|
|
|
b_way += d.Nodes[a][1]
|
|
|
b_way += "->"
|
|
|
b_way += d.Nodes[0][1]
|
|
|
y0 = 0.05
|
|
|
lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left')
|
|
|
lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30)
|
|
|
lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100)
|
|
|
|
|
|
lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK,
|
|
|
font=('微软雅黑', 12), wraplength=200, justify='left')
|
|
|
lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100)
|
|
|
|
|
|
def show_BEST(self):
|
|
|
best, user = self.check_user_best() # 计算最优路径与用户路径的总花费
|
|
|
|
|
|
if not best:
|
|
|
lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.3, rely=0.8, width=140, height=30)
|
|
|
elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径
|
|
|
lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.3, rely=0.8, width=140, height=30)
|
|
|
else:
|
|
|
lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.3, rely=0.8, width=140, height=30)
|
|
|
# else: # 否则说明用户路径不是最短路径
|
|
|
# self.servey_path_display()
|
|
|
def servey_path_display(self):
|
|
|
lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.1, rely=0.75, width=80, height=30)
|
|
|
# self.auto_ways()
|
|
|
b_way = ""
|
|
|
for a in s.ans:
|
|
|
b_way += d.Nodes[a][1]
|
|
|
b_way += "->"
|
|
|
b_way = b_way[:-2]
|
|
|
lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab2.place(relx=0.1, rely=0.8, width=200, height=100)
|
|
|
|
|
|
def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点
|
|
|
s.node_greedy()# 计算贪心路径
|
|
|
if not len(s.ans):
|
|
|
return -2
|
|
|
# 路径长度不一样肯定不匹配,直接返回
|
|
|
if not len(self.user_way) == len(s.ans):
|
|
|
# self.auto_ways()
|
|
|
return 0
|
|
|
way = [(int(node[1])-1) for node in d.Nodes]
|
|
|
result1 = list(map(str, self.user_way))
|
|
|
result1 = ' '.join(result1)
|
|
|
ways = [way[an] for an in s.ans]
|
|
|
result2 = list(map(str, ways))
|
|
|
result3 = ' '.join(result2)
|
|
|
result2.reverse() # 倒序排列列表元素
|
|
|
result4 = ' '.join(result2)
|
|
|
if result4 == result1 or result3 == result1:
|
|
|
return -1 #-1表示是贪心路径
|
|
|
else:
|
|
|
x_1 = self.find_longest(result1, result4)
|
|
|
x_2 = self.find_longest(result1, result3)
|
|
|
return max(x_1, x_2)
|
|
|
|
|
|
def find_longest(self, list1, list2):
|
|
|
index = 0
|
|
|
while index < len(list1) and index < len(list2):
|
|
|
if list1[index] != list2[index]:
|
|
|
break
|
|
|
index += 1
|
|
|
return index
|
|
|
|
|
|
|
|
|
def show_greddy_ans(self, top):
|
|
|
lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20))
|
|
|
b_way = ""
|
|
|
for a in s.ans:
|
|
|
b_way += d.Nodes[a][1]
|
|
|
b_way += "->"
|
|
|
b_way += d.Nodes[0][1]
|
|
|
y0 = 0.05
|
|
|
lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left')
|
|
|
lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30)
|
|
|
lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100)
|
|
|
|
|
|
# 对用户路径进行标记与记录
|
|
|
def way_route_markings(self, event):
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 2
|
|
|
# E.newedge_del_all(1)
|
|
|
for w in frame_left.winfo_children():
|
|
|
w.destroy()
|
|
|
# 清除右侧组件
|
|
|
|
|
|
left_window()
|
|
|
self.user_way = [0]
|
|
|
# lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20))
|
|
|
# lab.place(relx=0.18, rely=0.02, width=200, height=30)
|
|
|
y0 = 0.1
|
|
|
global entry1,entry2,default_value
|
|
|
# 定义默认值变量
|
|
|
default_value = tk.StringVar()
|
|
|
default_value.set(self.user_way[-1]+1)
|
|
|
laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15))
|
|
|
laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30)
|
|
|
# 创建第一个输入框
|
|
|
entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5")
|
|
|
entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30)
|
|
|
labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15))
|
|
|
labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30)
|
|
|
# 创建第二个输入框
|
|
|
entry2 = tk.Entry(frame_left, bg="#F5F5F5")
|
|
|
entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30)
|
|
|
|
|
|
labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15))
|
|
|
labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30)
|
|
|
# 按钮
|
|
|
button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定
|
|
|
button.place(relx=0.55, rely=0.38 + y0)
|
|
|
button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销
|
|
|
button2.place(relx=0.18, rely=0.46 + y0)
|
|
|
button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径
|
|
|
button3.place(relx=0.5, rely=0.46 + y0)
|
|
|
button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径
|
|
|
button4.place(relx=0.18, rely=0.55 + y0)
|
|
|
button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径
|
|
|
button5.place(relx=0.18, rely=0.62 + y0)
|
|
|
|
|
|
self.user_path_display()
|
|
|
def extract_values(self):
|
|
|
global entry1, entry2, default_value
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 2
|
|
|
value1 = entry1.get() # 获取第一个输入框的值
|
|
|
value2 = entry2.get() # 获取第二个输入框的值
|
|
|
try:
|
|
|
value1 = int(value1) # 将值转换为整数
|
|
|
value2 = int(value2) # 将值转换为整数
|
|
|
print("值1:", value1)
|
|
|
print("值2:", value2)
|
|
|
# 判断两点是否连接
|
|
|
if abs(value2) > 10 and abs(value1) > 10:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!')
|
|
|
return
|
|
|
else:
|
|
|
E = 0
|
|
|
if value1 < value2:
|
|
|
str_route = f'{value1}-{value2}'
|
|
|
else:
|
|
|
str_route = f'{value2}-{value1}'
|
|
|
for item in d.Edges:
|
|
|
if str_route == item[5]:
|
|
|
if item[6] < 100 and item[7] < 100:
|
|
|
if d.Edges[G.node_edge(value1-1, value2-1)][4]:
|
|
|
E = 2
|
|
|
tk.messagebox.askokcancel(title='错误', message='城市已连接!')
|
|
|
else:
|
|
|
if self.user_way[-1] == (value1-1):
|
|
|
E = 1
|
|
|
global MODE
|
|
|
MODE = 1
|
|
|
self.user_way.append(value2 - 1)
|
|
|
default_value.set(value2)
|
|
|
d.Edges[G.node_edge(value1-1, value2-1)][4] = True
|
|
|
entry1.config(textvariable=default_value) # 更新entry1的默认值
|
|
|
entry2.delete(0, tk.END)
|
|
|
else:
|
|
|
E = 3
|
|
|
tk.messagebox.askokcancel(title='错误', message='未连贯!')
|
|
|
break
|
|
|
print(E)
|
|
|
if E == 1:
|
|
|
G.draw()
|
|
|
self.user_path_display()
|
|
|
elif E == 0:
|
|
|
tk.messagebox.askokcancel(title='错误', message='城市未连接!')
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!')
|
|
|
|
|
|
def extract_ligature(self, n1, n2):
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 2
|
|
|
no = G.way_node_edge(n1, n2)
|
|
|
flage = False
|
|
|
if d.Edges[no][4]:
|
|
|
tk.messagebox.askokcancel(title='错误', message='城市已连接!')
|
|
|
elif d.Edges[no][3] == 0:
|
|
|
tk.messagebox.askokcancel(title='错误', message='城市未连接!')
|
|
|
else:
|
|
|
if self.user_way[-1] == int(d.Nodes[n2][1]) - 1:
|
|
|
nx = n1
|
|
|
n1 = n2
|
|
|
n2 = nx
|
|
|
if self.user_way[-1] == int(d.Nodes[n1][1])-1:
|
|
|
global MODE
|
|
|
MODE = 1
|
|
|
self.user_way.append(int(d.Nodes[n2][1]) - 1)
|
|
|
d.Edges[no][4] = True
|
|
|
flage = True
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='错误', message='未连贯!')
|
|
|
if flage:
|
|
|
G.draw()
|
|
|
self.user_path_display()
|
|
|
|
|
|
def revoke(self):
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 2
|
|
|
try:
|
|
|
if len(self.user_way) > 1:
|
|
|
value1 = int(self.user_way[-1]) # 将值转换为整数
|
|
|
value2 = int(self.user_way[-2]) # 将值转换为整数
|
|
|
if value1 < value2:
|
|
|
str_route = f'{value1+1}-{value2+1}'
|
|
|
else:
|
|
|
str_route = f'{value2+1}-{value1+1}'
|
|
|
# print(str_route)
|
|
|
for item in d.Edges:
|
|
|
# print(item)
|
|
|
if str_route == item[5]:
|
|
|
# print(item)
|
|
|
global MODE
|
|
|
global entry1, default_value
|
|
|
MODE = 1
|
|
|
self.user_way.pop()
|
|
|
print(self.user_way)
|
|
|
d.Edges[G.node_edge(value1, value2)][4] = False
|
|
|
default_value.set(value2+1)
|
|
|
entry1.config(textvariable=default_value) # 更新entry1的默认值
|
|
|
break
|
|
|
G.draw()
|
|
|
self.user_path_display()
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='提示', message='未进行路径建设!')
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!')
|
|
|
|
|
|
def clear_all_paths(self):
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 2
|
|
|
try:
|
|
|
if len(self.user_way) > 1:
|
|
|
for i in range(len(self.user_way)-1):
|
|
|
value1 = int(self.user_way[-1]) # 将值转换为整数
|
|
|
value2 = int(self.user_way[-2]) # 将值转换为整数
|
|
|
if value1 < value2:
|
|
|
str_route = f'{value1 + 1}-{value2 + 1}'
|
|
|
else:
|
|
|
str_route = f'{value2 + 1}-{value1 + 1}'
|
|
|
# print(str_route)
|
|
|
for item in d.Edges:
|
|
|
# print(item)
|
|
|
if str_route == item[5]:
|
|
|
# print(item)
|
|
|
global MODE
|
|
|
global entry1, default_value
|
|
|
MODE = 3
|
|
|
self.user_way.pop()
|
|
|
print(self.user_way)
|
|
|
d.Edges[G.node_edge(value1, value2)][4] = False
|
|
|
default_value.set(value2 + 1)
|
|
|
entry1.config(textvariable=default_value) # 更新entry1的默认值
|
|
|
break
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='提示', message='未进行路径建设!')
|
|
|
for i in range(len(d.Edges)):
|
|
|
d.Edges[i][4] = False
|
|
|
G.draw()
|
|
|
self.user_path_display()
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!')
|
|
|
|
|
|
def greedy_path(self):
|
|
|
g = self.check_user_greey()
|
|
|
if g == -2:
|
|
|
# cvx.create_text(0, 0, text="不存在贪心路径")
|
|
|
lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.3, rely=0.8, width=180, height=30)
|
|
|
elif g == -1: # 如果用户路径是贪心路径则显示
|
|
|
# cvx.create_text(0, 0, text="用户路径是贪心路径")
|
|
|
lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.3, rely=0.8, width=180, height=30)
|
|
|
# elif g:
|
|
|
# self.servey_greedy_path()
|
|
|
else:
|
|
|
# print(self.user_way)
|
|
|
# print(self.user_way[0:g])
|
|
|
# cvx.create_text(0, 0, text="用户路径不是是贪心路径")
|
|
|
lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.3, rely=0.8, width=180, height=30)
|
|
|
|
|
|
def servey_greedy_path(self):
|
|
|
lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.1, rely=0.75, width=80, height=30)
|
|
|
# self.auto_ways()
|
|
|
g_way = ''
|
|
|
for a in s.ans:
|
|
|
g_way += d.Nodes[a][1]
|
|
|
g_way += "->"
|
|
|
g_way = g_way[:-2]
|
|
|
lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab2.place(relx=0.1, rely=0.8, width=200, height=100)
|
|
|
|
|
|
|
|
|
|
|
|
def shortest_path(self):
|
|
|
best, user = self.check_user_best() # 计算最优路径与用户路径的总花费
|
|
|
if not best:
|
|
|
lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.33, rely=0.8, width=180, height=30)
|
|
|
elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径
|
|
|
lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.33, rely=0.8, width=180, height=30)
|
|
|
else:
|
|
|
lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12))
|
|
|
lab1.place(relx=0.33, rely=0.8, width=180, height=30)
|
|
|
# if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径
|
|
|
# lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12))
|
|
|
# lab1.place(relx=0.1, rely=0.8, width=180, height=30)
|
|
|
# else: # 否则说明用户路径不是最短路径
|
|
|
# self.servey_path_display()
|
|
|
|
|
|
# 用户路径展示
|
|
|
def user_path_display(self):
|
|
|
lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15))
|
|
|
lab1.place(relx=0.15, rely=0.8, width=80, height=30)
|
|
|
# self.auto_ways()
|
|
|
way = ""
|
|
|
# d_way = []
|
|
|
# for i in d.Nodes:
|
|
|
# d_way.append(i[1])
|
|
|
for i in range(len(self.user_way)):
|
|
|
if len(self.user_way)-i > 1:
|
|
|
way += str(self.user_way[i]+1)
|
|
|
way += "->"
|
|
|
else:
|
|
|
way += str(self.user_way[i]+1)
|
|
|
lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab2.place(relx=0.18, rely=0.84, width=300, height=100)
|
|
|
|
|
|
def way_operate(self, event):
|
|
|
global MARK_NODE
|
|
|
for i in range(len(d.Edges)):
|
|
|
d.Edges[i][4] = False
|
|
|
MARK_NODE = 5
|
|
|
# E.newedge_del_all(1)
|
|
|
for w in frame_left.winfo_children():
|
|
|
w.destroy()
|
|
|
# 清除左侧组件
|
|
|
left_window()
|
|
|
self.user_way = [0]
|
|
|
y0 = 0.1
|
|
|
label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15))
|
|
|
label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30)
|
|
|
global entry3, entry4
|
|
|
# 创建第一个输入框
|
|
|
entry3 = tk.Entry(frame_left, bg='#F5F5F5')
|
|
|
entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30)
|
|
|
|
|
|
label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15))
|
|
|
label2.place(relx=0.15, rely=0.37 + y0, width=100, height=30)
|
|
|
# 创建第二个输入框
|
|
|
entry4 = tk.Entry(frame_left, bg='#F5F5F5')
|
|
|
entry4.place(relx=0.6, rely=0.37 + y0, width=50, height=30)
|
|
|
button1 = tk.Button(frame_left, text='完全图', relief="flat", bg='#00BFFF', fg=BLACK,
|
|
|
font=('微软雅黑', 15), command=lambda: self.complete_graph()) # 完全图
|
|
|
button1.place(relx=0.2, rely=0.43 + y0, width=100, height=30)
|
|
|
button2 = tk.Button(frame_left, text='可达判断', relief="flat", bg='#00BFFF', fg=BLACK,
|
|
|
font=('微软雅黑', 15), command=lambda: self.reachable_judgment()) # 节点可达性判断
|
|
|
button2.place(relx=0.6, rely=0.43 + y0, width=100, height=30)
|
|
|
|
|
|
|
|
|
label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14))
|
|
|
label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30)
|
|
|
# 按钮
|
|
|
button3 = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white',
|
|
|
command=lambda: self.way_add()) # 线路增加
|
|
|
button3.place(relx=0.2, rely=0.56 + y0)
|
|
|
button4 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white',
|
|
|
command=lambda: self.way_change()) # 线路修改
|
|
|
button4.place(relx=0.2, rely=0.66 + y0)
|
|
|
button5 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white',
|
|
|
command=lambda: self.way_delete()) # 线路删除
|
|
|
button5.place(relx=0.2, rely=0.76 + y0)
|
|
|
|
|
|
def is_reachable(self, graph, start, target, visited):
|
|
|
if start == target:
|
|
|
return True
|
|
|
visited.add(start)
|
|
|
for neighbor in graph[start]:
|
|
|
if neighbor not in visited:
|
|
|
if self.is_reachable(graph, neighbor, target, visited):
|
|
|
return True
|
|
|
return False
|
|
|
|
|
|
def complete_graph(self):
|
|
|
l = len(d.Nodes)
|
|
|
result = d.node_coordinate(l)
|
|
|
for index in range(len(result)):
|
|
|
if d.Edges[index][3] == 0:
|
|
|
dist_c = random.randint(10, 99) # 随机生成距离成本
|
|
|
time_c = random.randint(10, 99) # 随机生成链接的时间
|
|
|
d.Edges[index][3] = 1
|
|
|
d.Edges[index][6] = dist_c
|
|
|
d.Edges[index][7] = time_c
|
|
|
G.draw()
|
|
|
|
|
|
|
|
|
def reachable_judgment(self):
|
|
|
try:
|
|
|
a = int(entry3.get()) # 获取第一个输入框的值
|
|
|
b = int(entry4.get()) # 获取第二个输入框的值
|
|
|
l = len(d.Nodes)
|
|
|
index_list = []
|
|
|
for i in d.Nodes:
|
|
|
index_list.append(i[1])
|
|
|
dictionary = {i: [] for i in range(1, l + 1)}
|
|
|
result = d.node_coordinate(l)
|
|
|
# 生成无向图
|
|
|
for i in range(len(result)):
|
|
|
if d.Edges[i][3] == 1:
|
|
|
x = d.Edges[i][5].split('-')[0]
|
|
|
y = d.Edges[i][5].split('-')[-1]
|
|
|
dictionary[index_list.index(x)+1].append(index_list.index(y)+1)
|
|
|
dictionary[index_list.index(y)+1].append(index_list.index(x)+1)
|
|
|
start_node = index_list.index(str(a))+1
|
|
|
target_node = index_list.index(str(b))+1
|
|
|
visited_nodes = set()
|
|
|
print(dictionary)
|
|
|
flag = self.is_reachable(dictionary, start_node, target_node, visited_nodes)
|
|
|
if flag:
|
|
|
tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}可达')
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}不可达')
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='结果', message='请输入节点')
|
|
|
|
|
|
def way_add(self):
|
|
|
global entry3, entry4
|
|
|
value1 = entry3.get() # 获取第一个输入框的值
|
|
|
value2 = entry4.get() # 获取第二个输入框的值
|
|
|
try:
|
|
|
value1 = int(value1) # 将值转换为整数
|
|
|
value2 = int(value2) # 将值转换为整数
|
|
|
print("值1:", value1)
|
|
|
print("值2:", value2)
|
|
|
ways_list = [int(node[1]) for node in d.Nodes]
|
|
|
# 判断两点是否连接
|
|
|
if abs(value2) > 10 and abs(value1) > 10:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!')
|
|
|
return
|
|
|
else:
|
|
|
if value1 < value2:
|
|
|
str_route = f'{value1}-{value2}'
|
|
|
else:
|
|
|
str_route = f'{value2}-{value1}'
|
|
|
if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4:
|
|
|
print(d.drop)
|
|
|
tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!')
|
|
|
return
|
|
|
no = 0
|
|
|
for item in d.Edges:
|
|
|
if str_route == item[5]:
|
|
|
no = G.node_edge(value1 - 1, value2 - 1)
|
|
|
if d.Edges[no][3] == 1:
|
|
|
tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!')
|
|
|
return
|
|
|
top = creat_window(str_route+'路径参数配置') # 创建弹出窗口
|
|
|
distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小
|
|
|
time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长
|
|
|
|
|
|
def get_input(): # 创建
|
|
|
# 获取输入框的内容
|
|
|
try:
|
|
|
result1 = int(distance_entry.get())
|
|
|
result2 = int(time_entry.get())
|
|
|
print(result1,result2)
|
|
|
if 100 > result1 > 0 and 0 < result2 < 100:
|
|
|
d.Edges[no][3] = 1
|
|
|
d.Edges[no][6] = result1
|
|
|
d.Edges[no][7] = result2
|
|
|
top.destroy() # 关闭窗口
|
|
|
G.draw()
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!')
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!')
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!')
|
|
|
|
|
|
def way_change(self):
|
|
|
global entry3, entry4
|
|
|
value1 = entry3.get() # 获取第一个输入框的值
|
|
|
value2 = entry4.get() # 获取第二个输入框的值
|
|
|
try:
|
|
|
value1 = int(value1) # 将值转换为整数
|
|
|
value2 = int(value2) # 将值转换为整数
|
|
|
print("值1:", value1)
|
|
|
print("值2:", value2)
|
|
|
ways_list = [int(node[1]) for node in d.Nodes]
|
|
|
# 判断两点是否连接
|
|
|
if abs(value2) > 10 and abs(value1) > 10:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!')
|
|
|
return
|
|
|
else:
|
|
|
if value1 < value2:
|
|
|
str_route = f'{value1}-{value2}'
|
|
|
else:
|
|
|
str_route = f'{value2}-{value1}'
|
|
|
no = 0
|
|
|
for item in d.Edges:
|
|
|
if str_route == item[5]:
|
|
|
no = G.node_edge(value1 - 1, value2 - 1)
|
|
|
if d.Edges[no][3] == 0:
|
|
|
tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!')
|
|
|
return
|
|
|
top = creat_window(str_route + '路径参数修改') # 创建弹出窗口
|
|
|
distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小
|
|
|
time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长
|
|
|
|
|
|
def get_input(): # 创建
|
|
|
# 获取输入框的内容
|
|
|
try:
|
|
|
result1 = int(distance_entry.get())
|
|
|
result2 = int(time_entry.get())
|
|
|
print(result1, result2)
|
|
|
if 100 > result1 > 0 and 0 < result2 < 100:
|
|
|
d.Edges[no][6] = result1
|
|
|
d.Edges[no][7] = result2
|
|
|
top.destroy() # 关闭窗口
|
|
|
G.draw()
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!')
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!')
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!')
|
|
|
|
|
|
def way_delete(self):
|
|
|
global entry3, entry4
|
|
|
value1 = entry3.get() # 获取第一个输入框的值
|
|
|
value2 = entry4.get() # 获取第二个输入框的值
|
|
|
try:
|
|
|
value1 = int(value1) # 将值转换为整数
|
|
|
value2 = int(value2) # 将值转换为整数
|
|
|
print("值1:", value1)
|
|
|
print("值2:", value2)
|
|
|
ways_list = [int(node[1]) for node in d.Nodes]
|
|
|
# 判断两点是否连接
|
|
|
if abs(value2) > 10 and abs(value1) > 10:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!')
|
|
|
return
|
|
|
else:
|
|
|
if value1 < value2:
|
|
|
str_route = f'{value1}-{value2}'
|
|
|
else:
|
|
|
str_route = f'{value2}-{value1}'
|
|
|
no = 0
|
|
|
for item in d.Edges:
|
|
|
if str_route == item[5]:
|
|
|
no = G.node_edge(value1 - 1, value2 - 1)
|
|
|
if d.Edges[no][3] == 0:
|
|
|
tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!')
|
|
|
return
|
|
|
d.Edges[no][3] = 0
|
|
|
d.Edges[no][6] = 111
|
|
|
d.Edges[no][7] = 111
|
|
|
G.draw()
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!')
|
|
|
|
|
|
|
|
|
class Node:
|
|
|
# 标记节点按钮
|
|
|
def node_mark_display(self, event):
|
|
|
global MARK_NODE, Specify_Nodes
|
|
|
W.user_way = [0]
|
|
|
MARK_NODE = 1
|
|
|
for w in frame_left.winfo_children():
|
|
|
w.destroy()
|
|
|
# 清除右侧组件
|
|
|
left_window()
|
|
|
curx = -1
|
|
|
y0 = 0.1
|
|
|
for i in d.Nodes:
|
|
|
if i[2] == 1:
|
|
|
curx = i[0]
|
|
|
# lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20))
|
|
|
# lab.place(relx=0.18, rely=0.32, width=200, height=30)
|
|
|
labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20))
|
|
|
labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30)
|
|
|
lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20))
|
|
|
if curx == -1:
|
|
|
text = "未选择"
|
|
|
else:
|
|
|
text = f"{d.Nodes[curx][1]}"
|
|
|
Specify_Nodes = str(d.Nodes[curx][1])
|
|
|
lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20))
|
|
|
lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50)
|
|
|
lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50)
|
|
|
|
|
|
# 按钮
|
|
|
button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white',
|
|
|
command=lambda: self.node_del_exact()) # 删除当前节点
|
|
|
button.place(relx=0.2, rely=0.5 + y0)
|
|
|
button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white',
|
|
|
command=lambda: self.node_add()) # 增加一个节点
|
|
|
button2.place(relx=0.2, rely=0.6 + y0)
|
|
|
button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white',
|
|
|
command=lambda: self.node_del()) # 减少一个节点
|
|
|
button3.place(relx=0.2, rely=0.7 + y0)
|
|
|
|
|
|
def node_add(self):
|
|
|
global d,Edges_list
|
|
|
for i in range(len(d.Edges)):
|
|
|
d.Edges[i][4] = False
|
|
|
Edges_list = []
|
|
|
Edges_list = d.Edges
|
|
|
# print(len(Edges_list))
|
|
|
# 增加一个节点
|
|
|
nodes = len(d.Nodes) + 1
|
|
|
# 如果节点数目大于五,则将连接详细信息改为不显示
|
|
|
if nodes > 5:
|
|
|
d.edgeinfo = 0
|
|
|
if nodes > 10:
|
|
|
tk.messagebox.askokcancel(title='错误', message='已达到最大节点')
|
|
|
return
|
|
|
d = Data(1, nodes, d.styple)
|
|
|
# for i in d.Edges:
|
|
|
# print(i)
|
|
|
G.draw()
|
|
|
self.node_mark_display(1)
|
|
|
|
|
|
|
|
|
def node_del(self):
|
|
|
global d,Edges_list
|
|
|
for i in range(len(d.Edges)):
|
|
|
d.Edges[i][4] = False
|
|
|
Edges_list = []
|
|
|
Edges_list = d.Edges
|
|
|
# print(len(Edges_list))
|
|
|
if d.nodes_num <= 2:
|
|
|
tk.messagebox.askokcancel(title='错误', message='节点过少')
|
|
|
return
|
|
|
nodes = len(d.Nodes) - 1
|
|
|
if nodes < 6:
|
|
|
d.edgeinfo = 1
|
|
|
d = Data(2, nodes, d.styple)
|
|
|
# for i in d.Edges:
|
|
|
# print(i)
|
|
|
G.draw()
|
|
|
self.node_mark_display(1)
|
|
|
|
|
|
def node_del_exact(self):
|
|
|
global d,Edges_list
|
|
|
for i in range(len(d.Edges)):
|
|
|
d.Edges[i][4] = False
|
|
|
Edges_list = d.Edges
|
|
|
if len(Specify_Nodes) == 3:
|
|
|
tk.messagebox.askokcancel(title='错误', message='未选择节点')
|
|
|
return
|
|
|
if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告
|
|
|
tk.messagebox.askokcancel(title='错误', message='节点过少')
|
|
|
return
|
|
|
nodes = len(d.Nodes) - 1
|
|
|
if nodes < 6:
|
|
|
d.edgeinfo = 1
|
|
|
d = Data(3, nodes, d.styple)
|
|
|
# for i in d.Edges:
|
|
|
# print(i)
|
|
|
G.draw()
|
|
|
self.node_mark_display(1)
|
|
|
|
|
|
|
|
|
def refresh():
|
|
|
top = creat_window('路径生成') # 创建弹出窗口
|
|
|
distance_entry = create_input_box(top, "节点个数:(1-10)", 6) # 创建一个输入框,获取节点个数
|
|
|
# 创建一个下拉框,用于选择生成图类型
|
|
|
mode_combobox = create_dropdown_box(top, "生成图类型", ["TSP", "完全图"]) # 创建一个下拉框,用于选择生成图类型
|
|
|
|
|
|
def get_input(): # 创建
|
|
|
# 获取输入框的内容
|
|
|
try:
|
|
|
result1 = int(distance_entry.get())
|
|
|
# 从下拉框中获取池化类型
|
|
|
result2 = mode_combobox.get()
|
|
|
print(result1, result2)
|
|
|
flag = 0
|
|
|
if result2!=None:
|
|
|
if result2 == '完全图':
|
|
|
flag = 1
|
|
|
if 10 > result1 > 0:
|
|
|
global d
|
|
|
if flag:
|
|
|
d = Data(0, result1, 1)
|
|
|
else:
|
|
|
d = Data(0, result1, 0)
|
|
|
top.destroy() # 关闭窗口
|
|
|
G.draw()
|
|
|
# for i in d.Edges:
|
|
|
# print(i)
|
|
|
else:
|
|
|
tk.messagebox.askokcancel(title='错误', message='节点数范围错误!')
|
|
|
except ValueError:
|
|
|
tk.messagebox.askokcancel(title='错误', message='请输入正确的节点数')
|
|
|
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def graph_init():
|
|
|
node = random.randint(4, 10)
|
|
|
global d, MODE, MARK_NODE
|
|
|
MARK_NODE = 0
|
|
|
MODE = 0
|
|
|
d = Data(0, node,0)
|
|
|
print(node)
|
|
|
W.user_way = [0]
|
|
|
G.draw()
|
|
|
window(2)
|
|
|
|
|
|
def empty():
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 0
|
|
|
G.delete_all()
|
|
|
|
|
|
def auto_opera_mu(menu: tk.Menu):
|
|
|
"""TSP问题自动生成与自动求解相关"""
|
|
|
menu_node = Menu(menu, tearoff=False)
|
|
|
menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init())
|
|
|
menu_node.add_command(label="重新生成", command=lambda: refresh())
|
|
|
menu_node.add_command(label="清空", command=lambda: empty())
|
|
|
menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3))
|
|
|
menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2))
|
|
|
menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1))
|
|
|
menu_node.add_command(label="检查是否存在路径", command=lambda: s.check())
|
|
|
# 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定
|
|
|
menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node)
|
|
|
|
|
|
# 创建弹出窗口
|
|
|
def creat_window(title):
|
|
|
top = tk.Toplevel(root)
|
|
|
top.geometry("300x350")
|
|
|
top.title(title)
|
|
|
return top
|
|
|
|
|
|
# 输入框
|
|
|
def create_input_box(top, text, value):
|
|
|
box_label = tk.Label(top, text=text)
|
|
|
box_label.pack(padx=10, pady=10)
|
|
|
box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3
|
|
|
box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象
|
|
|
box_size_entry.pack(padx=20, pady=20)
|
|
|
return box_size_entry
|
|
|
|
|
|
# 下拉框
|
|
|
def create_dropdown_box(top, text, listvalues):
|
|
|
# 创建一个下拉框,用于选择
|
|
|
box_mode_label = tk.Label(top, text=text)
|
|
|
box_mode_label.pack(padx=10, pady=10)
|
|
|
box_mode_combobox = ttk.Combobox(top)
|
|
|
box_mode_combobox["values"] = listvalues
|
|
|
box_mode_combobox.pack(padx=20, pady=15)
|
|
|
return box_mode_combobox
|
|
|
|
|
|
def dir2(x, y): # 计算两点间距离
|
|
|
return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1])
|
|
|
|
|
|
def left1(event):
|
|
|
global entry1, entry2, default_value, CLICK, entry3, entry4
|
|
|
# 查找鼠标左键按下时位置是否在某个节点内
|
|
|
n = -1
|
|
|
for node in d.Nodes:
|
|
|
if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20:
|
|
|
n = node[0]
|
|
|
# n为点击的节点,若没有则为-1
|
|
|
m = mode.get() # 查看现在操作类型
|
|
|
if m == 1 and not n == -1:
|
|
|
# 节点操作
|
|
|
# N.node_mark(n)
|
|
|
if n >= len(d.Nodes):
|
|
|
# 弹出对话框
|
|
|
tk.messagebox.askokcancel(title='错误', message='该节点不存在')
|
|
|
return
|
|
|
for node in d.Nodes:
|
|
|
if node[0] == n:
|
|
|
node[2] = 1
|
|
|
else:
|
|
|
node[2] = 0
|
|
|
G.draw()
|
|
|
if MARK_NODE == 1: # 限制节点操作
|
|
|
N.node_mark_display(1)
|
|
|
elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作
|
|
|
if CLICK[2] == 0:
|
|
|
CLICK[0] = n
|
|
|
default_value.set(d.Nodes[n][1])
|
|
|
entry1.config(textvariable=default_value) # 更新entry1的默认值
|
|
|
elif CLICK[2] == 1:
|
|
|
CLICK[1] = n
|
|
|
W.extract_ligature(CLICK[0], CLICK[1])
|
|
|
CLICK[2] = -1
|
|
|
CLICK[2] += 1
|
|
|
elif MARK_NODE == 5: # 若是MARK_NODE==5,则可以进行节点连接操作
|
|
|
if CLICK[2] == 0:
|
|
|
CLICK[0] = n
|
|
|
entry3.delete(0, tk.END)
|
|
|
# 设置输入框的值为 节点
|
|
|
entry3.insert(0, d.Nodes[n][1])
|
|
|
elif CLICK[2] == 1:
|
|
|
CLICK[1] = n
|
|
|
# W.extract_ligature(CLICK[0], CLICK[1])
|
|
|
entry4.delete(0, tk.END)
|
|
|
# 设置输入框的值为 节点
|
|
|
entry4.insert(0, d.Nodes[n][1])
|
|
|
CLICK[2] = -1
|
|
|
CLICK[2] += 1
|
|
|
print(n)
|
|
|
|
|
|
def element_reading():
|
|
|
global List_Image
|
|
|
folder_path = 'K:/work/traveler/background/'
|
|
|
image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png',
|
|
|
'4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png',
|
|
|
'7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png',
|
|
|
'10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png',
|
|
|
'线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png',
|
|
|
'矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png', '完全图.png', '可达判断.png']
|
|
|
for path in image_path[:30]: # 1-10球加载
|
|
|
List_Image.append(element(folder_path+path, 50, 50))
|
|
|
for path in image_path[30:36]: # 节点操作与连线操作功能按钮
|
|
|
List_Image.append(element(folder_path + path, 280, 50))
|
|
|
List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销
|
|
|
List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径
|
|
|
List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认
|
|
|
List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9
|
|
|
List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景
|
|
|
List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形
|
|
|
List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径
|
|
|
List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径
|
|
|
List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形
|
|
|
List_Image.append(element(folder_path + image_path[45], 140, 50)) # 完全图
|
|
|
List_Image.append(element(folder_path + image_path[46], 140, 50)) # 可达判断
|
|
|
|
|
|
def element(path, width, height):
|
|
|
# 加载图元对应的图片文件
|
|
|
img = Image.open(path)
|
|
|
# 使用resize方法调整图片
|
|
|
img = img.resize((width, height))
|
|
|
# 把Image对象转换成PhotoImage对象
|
|
|
img = ImageTk.PhotoImage(img)
|
|
|
# 保存图片的引用,防止被垃圾回收
|
|
|
root.img = img
|
|
|
return img
|
|
|
|
|
|
def window(event):
|
|
|
global MARK_NODE
|
|
|
MARK_NODE = 0
|
|
|
for w in frame_left.winfo_children():
|
|
|
w.destroy()
|
|
|
# 清除右侧组件
|
|
|
left_window()
|
|
|
Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16))
|
|
|
Label_1.place(x=50, y=330, width=180, height=40)
|
|
|
Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16))
|
|
|
Label_2.place(x=50, y=450, width=180, height=40)
|
|
|
Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16))
|
|
|
Label_3.place(x=50, y=570, width=140, height=40)
|
|
|
if event == 2:
|
|
|
lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab1.place(x=80, y=370, width=300, height=60)
|
|
|
lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab2.place(x=80, y=490, width=300, height=60)
|
|
|
lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab3.place(x=80, y=610, width=300, height=140)
|
|
|
else:
|
|
|
lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab1.place(x=80, y=370, width=300, height=60)
|
|
|
t = round(s.end - s.start, 4)
|
|
|
lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab2.place(x=80, y=490, width=300, height=60)
|
|
|
g_way = ''
|
|
|
if len(s.ans) > 0:
|
|
|
for a in s.ans:
|
|
|
g_way += d.Nodes[a][1]
|
|
|
g_way += "->"
|
|
|
g_way = g_way[:-2]
|
|
|
else:
|
|
|
g_way='1'
|
|
|
lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200,
|
|
|
justify='left')
|
|
|
lab3.place(x=80, y=610, width=300, height=140)
|
|
|
|
|
|
def left_window():
|
|
|
background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组
|
|
|
background_button.place(x=50, y=20, width=380, height=100)
|
|
|
background_button.bind("<Button-1>", window)
|
|
|
# # 创建 Label 组件来显示背景图片
|
|
|
background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景
|
|
|
background_on.place(x=50, y=140, width=380, height=140)
|
|
|
|
|
|
if MARK_NODE == 1:
|
|
|
background_color = [BLACK, '#DCDCDC', '#DCDCDC']
|
|
|
elif MARK_NODE == 5:
|
|
|
background_color = ['#DCDCDC', BLACK, '#DCDCDC']
|
|
|
elif MARK_NODE == 2:
|
|
|
background_color = ['#DCDCDC', '#DCDCDC', BLACK]
|
|
|
else:
|
|
|
background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC']
|
|
|
Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15))
|
|
|
Label_button1.place(x=60, y=180, width=80, height=50)
|
|
|
Label_button1.bind("<Button-1>", N.node_mark_display)
|
|
|
Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15))
|
|
|
Label_button2.place(x=200, y=180, width=80, height=50)
|
|
|
Label_button2.bind("<Button-1>", W.way_operate)
|
|
|
Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15))
|
|
|
Label_button3.place(x=340, y=180, width=80, height=50)
|
|
|
Label_button3.bind("<Button-1>", W.way_route_markings)
|
|
|
|
|
|
background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景
|
|
|
background_below.place(x=50, y=300, width=380, height=530)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
root = Tk() # 设置主界面
|
|
|
root.title("旅行商问题") # 设置标题
|
|
|
root.geometry("1400x900") # 设置大小
|
|
|
|
|
|
root.resizable(0, 0) # 设置不能调整显示边框
|
|
|
element_reading()
|
|
|
frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0)
|
|
|
frame_top.place(x=0, y=0) # 绘制信息输出栏
|
|
|
|
|
|
frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏
|
|
|
frame_left.place(x=0, y=50) # 左边栏
|
|
|
|
|
|
cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0)
|
|
|
cv.place(x=500, y=50) # 放置绘图Canvas画布
|
|
|
|
|
|
cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景
|
|
|
|
|
|
G = Graph()
|
|
|
mode = tk.IntVar(value=1)
|
|
|
main_menu = tk.Menu(root)
|
|
|
root.config(menu=main_menu)
|
|
|
cv.bind('<Button-1>', left1)
|
|
|
E = Edge()
|
|
|
W = Ways()
|
|
|
s = Solve()
|
|
|
N = Node()
|
|
|
auto_opera_mu(main_menu)
|
|
|
window(2)
|
|
|
G.lab_show(frame_top, 0, 0, 0) # 显示边信息
|
|
|
root.mainloop()
|