10,171
社区成员
发帖
与我相关
我的任务
分享在完成数据结构课设(生成校园导航系统)过程中,发现电脑里同时有3.13和3.12版本的python,并遇到问题:
使用3.12作为解释器可以跑出代码,但是想进一步使用pillow库遇到困难(+选项无法使用,即无法加载新的库)
使用3.13作为解释器显示无法打开Tcl,应该是Tkinter出错,可以使用+,但是找不到tkinter库
如有大佬解答,鄙人感激万分,也希望您可以理解我在这方面知识的欠缺。
如果可以,也请指出相关背景知识,以供鄙人查缺补漏
附录(源码):
#主要依靠AI生成,欢迎提出批评建议
import tkinter as tk
import heapq
from typing import Dict, List, Tuple, Optional
# 定义地点和位置
LOCATIONS = {
"": (600, 80),
}
# 初始图
EDGES = {
"": { "": 320},
}
# 地点详细信息
LOCATIONS_INFO = {
": "",
}
# Dijkstra算法实现
def dijkstra(graph: Dict[str, Dict[str, float]], start: str, end: str) -> Tuple[float, list]:
queue = [(0, start)]
distances: Dict[str, float] = {node: float('inf') for node in graph}
distances[start] = 0
previous_nodes: Dict[str, Optional[str]] = {node: None for node in graph}
while queue:
current_distance, current_node = heapq.heappop(queue)
if current_node == end:
break # 找到目标节点,提前结束
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node].items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
previous_nodes[neighbor] = current_node
heapq.heappush(queue, (distance, neighbor))
path = []
current_node = end
while current_node is not None:
path.append(current_node)
current_node = previous_nodes[current_node]
path.reverse()
return distances[end], path
class CampusNavigator(tk.Tk):
def __init__(self):
super().__init__()
self.title("扬子津校园导航") # 设置窗口标题
self.geometry("800x600")# 设置窗口大小
self.canvas = tk.Canvas(self, bg="white")# 创建一个白色背景画布,并填充整个窗口。
self.canvas.pack(fill=tk.BOTH, expand=True)
# 初始化变量:缩放级别、偏移量、信息文本。
self.zoom_level = 1.0
self.offset_x = 0
self.offset_y = 0
self.info_text = None
# 起始和目标地点的输入框
self.start_entry = tk.Entry(self)
self.start_entry.pack(pady=10)
self.start_entry.insert(0, "请输入起点,如:西门")
self.end_entry = tk.Entry(self)
self.end_entry.pack(pady=10)
self.end_entry.insert(0, "请输入终点,如:东操场")
# 距离设置输入框
self.distance_entry = tk.Entry(self)
self.distance_entry.pack(pady=10)
self.distance_entry.insert(0, "请输入距离(m)如:200")
self.set_distance_button = tk.Button(self, text="设置距离", command=self.set_distance)
self.set_distance_button.pack(pady=5)
# 设置计算最短路径选项按钮
self.calc_button = tk.Button(self, text="计算最短路径", command=self.calculate_shortest_path)
self.calc_button.pack(pady=10)
self.last_path_lines = [] # 用于保存上一次路径的线条
# 绑定鼠标事件(拖拽、放缩的开始和终止)
self.canvas.bind("<ButtonPress-1>", self.start_drag)
self.canvas.bind("<B1-Motion>", self.drag)
self.canvas.bind("<ButtonRelease-1>", self.end_drag)
self.canvas.bind("<MouseWheel>", self.zoom)
self.canvas.bind("<Motion>", self.on_mouse_move)
# 绘制地图(循环调用以实现实时更新)
self.draw_map()
def draw_map(self):
self.canvas.delete("all")# 清除画布上的全部内容
# 绘制建筑物
for location, (x, y) in LOCATIONS.items(): #使用 坐标*放大倍数+偏移量 的公式来实现拖拽、放缩功能
scaled_x = x * self.zoom_level + self.offset_x
scaled_y = y * self.zoom_level + self.offset_y
self.canvas.create_rectangle(scaled_x - 5, scaled_y - 5,
scaled_x + 5, scaled_y + 5,
fill="blue", tags=location)
self.canvas.create_text(scaled_x, scaled_y - 10, text=location, fill="black", tags="label")
# 绘制连接线和距离
for loc1, edges in EDGES.items():
for loc2, distance in edges.items():
x1, y1 = LOCATIONS[loc1]
x2, y2 = LOCATIONS[loc2]
# 绘制连接线
self.canvas.create_line(x1 * self.zoom_level + self.offset_x,
y1 * self.zoom_level + self.offset_y,
x2 * self.zoom_level + self.offset_x,
y2 * self.zoom_level + self.offset_y,
fill="lightgray")
# 计算中点位置(为了显示距离)
mid_x = (x1 + x2) / 2 * self.zoom_level + self.offset_x
mid_y = (y1 + y2) / 2 * self.zoom_level + self.offset_y
# 在中点位置显示距离
self.canvas.create_text(mid_x, mid_y, text=str(distance), fill="purple")
def set_distance(self):
start_location = self.start_entry.get().strip() #从输入框获取起点、终点和距离。
end_location = self.end_entry.get().strip()
distance = self.distance_entry.get().strip()
if start_location in LOCATIONS and end_location in LOCATIONS:# 检查起点和终点是否有效,并尝试将距离转换为浮点数。
try:
distance_value = float(distance)# 如果距离有效,更新 EDGES 字典中的距离
if distance_value == 0:
# 如果设置为0,则删除该线路
if end_location in EDGES[start_location]:
del EDGES[start_location][end_location]
if start_location in EDGES[end_location]:
del EDGES[end_location][start_location]
self.show_message(f"已删除从 {start_location} 到 {end_location} 的线路。") # 显示操作结果信息
else:
# 更新图的距离
EDGES[start_location][end_location] = distance_value
EDGES[end_location][start_location] = distance_value # 由于是无向图,反向也要更新
self.show_message(f"已设置从 {start_location} 到 {end_location} 的距离为 {distance_value}。")
except ValueError:
self.show_message("请输入有效的距离!")
else:
self.show_message("起点或终点无效!")
# 重新绘制地图以更新状态
self.draw_map()
def calculate_shortest_path(self):
start_location = self.start_entry.get().strip()
end_location = self.end_entry.get().strip()
if start_location in LOCATIONS and end_location in LOCATIONS:
# 获取最短路径和距离
distance, path = dijkstra(EDGES, start_location, end_location)
# 恢复上一次路径的颜色
for line in self.last_path_lines:
self.canvas.itemconfig(line, fill="lightgray")
# 绘制新的最短路径并保存线条
self.last_path_lines = []
for i in range(len(path) - 1):
loc1 = path[i]
loc2 = path[i + 1]
x1, y1 = LOCATIONS[loc1]
x2, y2 = LOCATIONS[loc2]
line = self.canvas.create_line(x1 * self.zoom_level + self.offset_x,
y1 * self.zoom_level + self.offset_y,
x2 * self.zoom_level + self.offset_x,
y2 * self.zoom_level + self.offset_y,
fill="red") # 设为红色
self.last_path_lines.append(line) # 保存新的线条
result_text = f"从 {start_location} 到 {end_location} 的最短路径为:{' -> '.join(path)}\n距离: {distance} 单位"
if hasattr(self, 'result_label'):
self.result_label.destroy()
self.result_label = tk.Label(self, text=result_text, bg="white")
self.result_label.pack(pady=10)
else:
# 处理无效的地点名
if hasattr(self, 'result_label'):
self.result_label.destroy()
self.result_label = tk.Label(self, text="请输入有效的地点名!", bg="white")
self.result_label.pack(pady=10)
def show_message(self, message: str):
if hasattr(self, 'message_label'):
self.message_label.destroy()
self.message_label = tk.Label(self, text=message, bg="white")
self.message_label.pack(pady=5)
def on_mouse_move(self, event):
closest_location = None #赋初值
closest_distance = float('inf')
for loc, (x, y) in LOCATIONS.items(): #定义鼠标悬停的距离,以便显示用户指向的地点信息
scaled_x = x * self.zoom_level + self.offset_x
scaled_y = y * self.zoom_level + self.offset_y
distance = ((event.x - scaled_x) ** 2 + (event.y - scaled_y) ** 2) ** 0.5
if distance < 15: # 设定鼠标悬停的距离阈值
closest_location = loc
break
if closest_location:
self.display_info(closest_location)
else:
self.clear_info() #防止地点信息始终显示
def display_info(self, location: str):
if self.info_text:
self.clear_info()
# 获取地点的详细信息(包括内容和位置)
info = LOCATIONS_INFO.get(location, "无详细信息")
x, y = LOCATIONS[location]
text_x = x * self.zoom_level + self.offset_x #保证始终显示在地点旁边
text_y = y * self.zoom_level + self.offset_y
# 在画布上显示地点信息
self.info_text = self.canvas.create_text(text_x, text_y + 15, text=info, fill="purple")
def clear_info(self):
if self.info_text:
self.canvas.delete(self.info_text)
self.info_text = None # 重置信息文本变量
def start_drag(self, event):
self.last_x = event.x
self.last_y = event.y
def drag(self, event):
dx = event.x - self.last_x
dy = event.y - self.last_y
self.offset_x += dx
self.offset_y += dy
self.last_x = event.x
self.last_y = event.y
self.draw_map()
def end_drag(self, event):
pass # 释放鼠标时可以在这里添加任何需要的操作
###########################################################
def zoom(self, event):
if event.delta > 0:
self.zoom_level *= 1.1
else:
self.zoom_level /= 1.1
self.draw_map()
if __name__ == "__main__":
app = CampusNavigator() #创建类的实例
app.mainloop() #进入应用的主循环,等待用户触发事件