import tkinter as tk
from PIL import Image, ImageTk
# ==================== 配置文件路径 ====================
BACKGROUND_PATH = "1.jpg"
BASE_STATION_PATH = "base_station.png"
PHONE_ICON_PATH = "phone.png"
# ==================== 坐标定义 ====================
BS_X, BS_Y = 400, 300 # 基站逻辑坐标
# ==================== 固定图标大小 ====================
FIXED_BS_SIZE = (32, 32)
FIXED_PHONE_SIZE = (24, 24)
# ==================== 全局变量 ====================
zoom_factor = 1.0
img_x, img_y = 0, 0
drag_start = None
bg_image_tk = None
bs_icon_tk = None
phone_icon_tk = None
canvas_images = [] # 存储所有动态图标的 canvas ID
phones = {} # 手机字典:{ phone_id: { 'x': x, 'y': y } }
# ==================== 创建主窗口 ====================
root = tk.Tk()
root.title("多手机管理系统")
root.geometry("1200x900")
left_frame = tk.Frame(root)
left_frame.pack(side="left", fill="both", expand=True)
right_frame = tk.Frame(root, width=300, bg="lightgray")
right_frame.pack(side="right", fill="y")
canvas = tk.Canvas(left_frame, bg="black")
canvas.pack(fill="both", expand=True)
# ==================== 状态栏 ====================
status_label = tk.Label(right_frame, text="就绪", fg="green", bg="lightgray", wraplength=280)
status_label.pack(pady=10)
def update_status(text, color="black"):
status_label.config(text=text, fg=color)
# ==================== 加载图像 ====================
try:
bg_image = Image.open(BACKGROUND_PATH)
except FileNotFoundError:
print(f"[警告] {BACKGROUND_PATH} 不存在,使用蓝色占位图")
bg_image = Image.new("RGB", (800, 600), "blue")
# 加载基站图标
try:
bs_raw = Image.open(BASE_STATION_PATH).convert("RGBA")
bs_resized = bs_raw.resize(FIXED_BS_SIZE, Image.Resampling.LANCZOS)
bs_icon_tk = ImageTk.PhotoImage(bs_resized)
except FileNotFoundError:
print(f"[警告] {BASE_STATION_PATH} 不存在,生成白色圆形")
bs_img = Image.new("RGBA", FIXED_BS_SIZE, (0, 0, 0, 0))
from PIL import ImageDraw
draw = ImageDraw.Draw(bs_img)
draw.ellipse((2, 2, 30, 30), fill=(255, 255, 255, 200), outline="gray", width=3)
bs_icon_tk = ImageTk.PhotoImage(bs_img)
# 加载手机图标
try:
phone_raw = Image.open(PHONE_ICON_PATH).convert("RGBA")
phone_resized = phone_raw.resize(FIXED_PHONE_SIZE, Image.Resampling.LANCZOS)
phone_icon_tk = ImageTk.PhotoImage(phone_resized)
except FileNotFoundError:
print(f"[警告] {PHONE_ICON_PATH} 不存在,生成绿色圆形")
phone_img = Image.new("RGBA", FIXED_PHONE_SIZE, (0, 0, 0, 0))
from PIL import ImageDraw
draw = ImageDraw.Draw(phone_img)
draw.ellipse((2, 2, 22, 22), fill=(0, 200, 0, 180), outline="white", width=2)
phone_icon_tk = ImageTk.PhotoImage(phone_img)
# ==================== 渲染函数 ====================
def render_background():
global bg_image_tk
w, h = bg_image.size
new_size = (int(w * zoom_factor), int(h * zoom_factor))
resized_bg = bg_image.resize(new_size, Image.Resampling.LANCZOS)
bg_image_tk = ImageTk.PhotoImage(resized_bg)
canvas.delete("background")
canvas.create_image(img_x, img_y, anchor="nw", image=bg_image_tk, tags="background")
def draw_icons():
global canvas_images
for img_id in canvas_images:
canvas.delete(img_id)
canvas_images.clear()
# --- 绘制基站 ---
screen_bs_x = img_x + int(BS_X * zoom_factor)
screen_bs_y = img_y + int(BS_Y * zoom_factor)
bs_id = canvas.create_image(screen_bs_x, screen_bs_y, image=bs_icon_tk, anchor="center", tags="icon")
canvas_images.append(bs_id)
# --- 绘制所有手机 ---
for pid, data in phones.items():
x, y = data['x'], data['y']
sx = img_x + int(x * zoom_factor)
sy = img_y + int(y * zoom_factor)
phone_id = canvas.create_image(sx, sy, image=phone_icon_tk, anchor="center", tags=("phone", pid))
canvas_images.append(phone_id)
# 可选:添加标签文字
label_id = canvas.create_text(sx, sy + 15, text=str(pid), fill="white", font=("Arial", 9), tags=("label", pid))
canvas_images.append(label_id)
# 显示信息
canvas.create_text(10, 10, anchor="nw", text=f"缩放: {zoom_factor:.2f}x", fill="yellow", font=("Arial", 12), tags="info")
canvas.create_text(10, 30, anchor="nw", text=f"设备数: {len(phones)}", fill="cyan", font=("Arial", 12), tags="info")
def redraw():
render_background()
draw_icons()
# ==================== UI 控件区 ====================
tk.Label(right_frame, text="📱 多手机管理", font=("Arial", 16), bg="lightgray").pack(pady=10)
# 手机 ID 输入
tk.Label(right_frame, text="手机ID:", bg="lightgray").pack(pady=5)
entry_id = tk.Entry(right_frame, width=25, font=("Arial", 11))
entry_id.pack(pady=2)
# X 坐标
tk.Label(right_frame, text="X 坐标:", bg="lightgray").pack(pady=5)
entry_x = tk.Entry(right_frame, width=25, font=("Arial", 11))
entry_x.pack(pady=2)
# Y 坐标
tk.Label(right_frame, text="Y 坐标:", bg="lightgray").pack(pady=5)
entry_y = tk.Entry(right_frame, width=25, font=("Arial", 11))
entry_y.pack(pady=2)
# 添加或更新手机
def on_add_update():
try:
pid = entry_id.get().strip()
if not pid:
raise ValueError("ID 不能为空")
x = int(entry_x.get())
y = int(entry_y.get())
except ValueError as e:
update_status(f"❌ 输入错误:{e}", "red")
return
w, h = bg_image.size
if not (0 <= x < w):
update_status(f"❌ X 超出范围 [0, {w})", "red")
return
if not (0 <= y < h):
update_status(f"❌ Y 超出范围 [0, {h})", "red")
return
phones[pid] = {'x': x, 'y': y}
update_phone_list()
redraw()
update_status(f"✅ 已添加/更新 {pid}", "green")
# 删除手机
def on_delete():
pid = entry_id.get().strip()
if pid in phones:
del phones[pid]
update_phone_list()
redraw()
update_status(f"🗑️ 已删除 {pid}", "orange")
else:
update_status(f"❌ 找不到手机 {pid}", "red")
# 清空所有手机
def on_clear_all():
if phones:
phones.clear()
update_phone_list()
redraw()
update_status("🧹 已清空所有设备", "orange")
else:
update_status("ℹ️ 无设备可清除", "gray")
# 列表框显示当前手机
phone_listbox = tk.Listbox(right_frame, height=8, width=30, font=("Arial", 10))
phone_listbox.pack(pady=10)
def update_phone_list():
phone_listbox.delete(0, tk.END)
for pid in phones:
phone_listbox.insert(tk.END, f"{pid}")
def on_list_select(event):
selection = phone_listbox.curselection()
if selection:
index = selection[0]
pid = phone_listbox.get(index).split()[0] # 获取 ID
data = phones[pid]
entry_id.delete(0, tk.END)
entry_id.insert(0, pid)
entry_x.delete(0, tk.END)
entry_x.insert(0, str(data['x']))
entry_y.delete(0, tk.END)
entry_y.insert(0, str(data['y']))
phone_listbox.bind("<<ListboxSelect>>", on_list_select)
# 按钮区域
btn_frame = tk.Frame(right_frame, bg="lightgray")
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="➕ 添加/更新", bg="#4CAF50", fg="white", command=on_add_update).grid(row=0, column=0, padx=5, pady=2)
tk.Button(btn_frame, text="➖ 删除", bg="#F44336", fg="white", command=on_delete).grid(row=0, column=1, padx=5, pady=2)
tk.Button(btn_frame, text="🧹 清空全部", bg="#FF9800", fg="white", command=on_clear_all).grid(row=1, column=0, columnspan=2, pady=5)
# ==================== 鼠标事件(缩放 & 拖拽)====================
def on_scroll(event):
global zoom_factor
if event.delta > 0 or getattr(event, 'num', None) == 4:
zoom_factor *= 1.1
else:
zoom_factor /= 1.1
zoom_factor = max(0.1, min(zoom_factor, 10))
redraw()
def on_drag_start(event):
global drag_start
drag_start = (event.x, event.y)
def on_drag_motion(event):
global drag_start, img_x, img_y
if drag_start:
dx = event.x - drag_start[0]
dy = event.y - drag_start[1]
img_x += dx
img_y += dy
drag_start = (event.x, event.y)
redraw()
def on_drag_release(event):
global drag_start
drag_start = None
# 绑定事件
canvas.bind("<MouseWheel>", on_scroll)
canvas.bind("<Button-4>", on_scroll)
canvas.bind("<Button-5>", on_scroll)
canvas.bind("<ButtonPress-1>", on_drag_start)
canvas.bind("<B1-Motion>", on_drag_motion)
canvas.bind("<ButtonRelease-1>", on_drag_release)
# 初始化
update_phone_list()
redraw()
# 启动主循环
root.mainloop()
该代码是针对基站用户移动的模拟,能对其进行模块化分析,并告知想添加新功能该在哪个区域?
最新发布