好友
阅读权限 10
听众
最后登录 1970-1-1
import tkinter as tk
from tkinter import messagebox, ttk
from datetime import datetime, date
import re
import json
import os
DATA_FILE = "daily_queue.json" # 数据文件路径
class QueueSystem(tk.Tk):
def __init__(self):
super().__init__()
self.title("☯️魏氏奇门轩智能排号系统☯")
self.geometry("500x400")
self.resizable(False, False)
# 初始化数据
self.queue = []
self.current_number = 0
# 界面初始化流程优化:数据加载 -> 界面创建 -> 样式配置 -> 状态检查
self._load_daily_data()
self.init_ui()
self.style_config()
self.center_window()
self.update_time()
self._check_button_state()
def init_ui(self):
"""创建界面组件"""
# 时间显示
self.time_label = tk.Label(self, font=('微软雅黑', 12, 'italic'), fg='#666')
self.time_label.pack(pady=5)
# 输入区域
input_frame = ttk.Frame(self, padding=(10, 5))
input_frame.pack(fill=tk.X, padx=10)
# 手机号输入(带验证)
ttk.Label(input_frame, text="问策者手机号:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.phone_var = tk.StringVar()
self.phone_entry = ttk.Entry(
input_frame,
textvariable=self.phone_var,
width=20,
valIDA te='key',
validatecommand=(self.register(self.validate_phone), '%P')
)
self.phone_entry.grid(row=0, column=1, pady=5, padx=5, sticky=tk.W)
# 人数选择(严格数字验证)
ttk.Label(input_frame, text="排号测算人数:").grid(row=0, column=2, sticky=tk.W, padx=5)
self.people_var = tk.IntVar(value=1)
self.people_spinbox = ttk.Spinbox(
input_frame,
from_=1,
to=9,
textvariable=self.people_var,
width=5,
wrap=True,
validate='key',
validatecommand=(self.register(self.validate_people), '%P')
)
self.people_spinbox.grid(row=0, column=3, pady=5, padx=5, sticky=tk.W)
# 按钮组
button_frame = ttk.Frame(input_frame, padding=(5, 0))
button_frame.grid(row=1, columnspan=4, pady=10, sticky=tk.W)
# 排号按钮
self.submit_btn = ttk.Button(
button_frame,
text="立即排号",
style='TButton',
command=self.submit_queue,
padding=(12, 6)
)
self.submit_btn.pack(side=tk.LEFT, padx=8)
# 刷新按钮(带动画)
self.refresh_btn = ttk.Button(
button_frame,
text="🔄 刷新",
style='Refresh.TButton',
command=self.refresh_queue,
padding=(8, 6),
width=8
)
self.refresh_btn.pack(side=tk.LEFT, padx=8)
# 叫号按钮
self.call_button = ttk.Button(
button_frame,
text="▶ 立即叫号",
style='Success.TButton',
command=self.call_next,
padding=(12, 6),
state='disabled'
)
self.call_button.pack(side=tk.LEFT, padx=8)
# 队列显示
queue_frame = ttk.Frame(
self,
padding=10,
relief='solid',
borderwidth=1,
style='Border.TFrame'
)
queue_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.queue_list = tk.Listbox(
queue_frame,
width=60,
height=10,
font=('微软雅黑', 10),
activestyle='none',
selectbackground='#E0E0E0'
)
self.queue_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
ttk.Scrollbar(queue_frame, orient='vertical', command=self.queue_list.yview).pack(side=tk.RIGHT, fill=tk.Y)
def style_config(self):
"""统一配置界面样式"""
style = ttk.Style()
# 基础按钮样式
style.configure('TButton',
foreground='#0078D4',
font=('微软雅黑', 10, 'bold'),
padding=6,
relief='flat'
)
# 成功按钮样式(叫号按钮)
style.configure('Success.TButton',
foreground='#22C55E',
font=('微软雅黑', 11, 'bold'),
padding=(12, 6),
relief='raised',
borderwidth=2,
background='#EBF5EC'
)
style.map('Success.TButton',
relief=[('pressed', 'sunken'), ('active', 'raised')],
background=[('active', '#E8F5E9')]
)
# 刷新按钮样式
style.configure('Refresh.TButton',
foreground='#666',
font=('微软雅黑', 10, 'bold'),
padding=6,
)
# 边框框架样式
style.configure('Border.TFrame',
borderwidth=1,
relief='solid',
lightcolor='#E0E0E0',
darkcolor='#E0E0E0'
)
style.layout('Border.TFrame', [
('Border.TFrame.border', {
'sticky': 'nswe',
'border': 1,
'children': [('Border.TFrame.padding', {'sticky': 'nswe'})]
})
])
def _load_daily_data(self):
"""加载当天数据,跨天自动重置"""
today = date.today().isoformat()
try:
with open(DATA_FILE, 'r') as f:
data = json.load(f)
if data.get('date') == today:
self.queue = data['queue']
self.current_number = data.get('current_number', 0)
else:
self._reset_daily_data() # 跨天重置
except (FileNotFoundError, json.JSONDecodeError):
self._reset_daily_data() # 初始化或文件损坏时重置
def _reset_daily_data(self):
"""重置当天数据(跨天/初始化/文件错误时调用)"""
self.queue = []
self.current_number = 0
self._save_daily_data()
def _save_daily_data(self):
"""保存当前数据到文件"""
try:
today = date.today().isoformat()
data = {
'date': today,
'queue': self.queue,
'current_number': self.current_number
}
with open(DATA_FILE, 'w') as f:
json.dump(data, f, indent=2)
except Exception as e:
messagebox.showerror("数据保存错误", f"保存数据时发生错误:{str(e)}", parent=self)
def validate_phone(self, new_value):
"""手机号验证:11位数字,允许空值(输入过程中)"""
return len(new_value) <= 11 and new_value.isdigit() or new_value == ''
def validate_people(self, new_value):
"""人数验证:1-9的数字,允许删除后自动回退"""
return not new_value or (new_value.isdigit() and 1 <= int(new_value) <= 9)
def submit_queue(self):
"""处理排号逻辑"""
phone = self.phone_var.get().strip()
people = self.people_var.get()
if not self._validate_phone_format(phone):
return
if not self._validate_people_count(people):
return
existing_people = sum(q['people'] for q in self.queue if q['status'] == '等待中')
remaining = people
while remaining > 0:
group = min(remaining, 2) # 每组最多2人(业务规则)
self.current_number += 1
self.queue.append({
'number': f'V{self.current_number:03d}',
'phone': phone,
'people': group,
'total': existing_people,
'status': '等待中',
'time': datetime.now().strftime('%H:%M')
})
existing_people += group
remaining -= group
# 非最后一组且人数>0时询问是否继续
if remaining > 0 and not self._ask_continue(phone, remaining):
break
self._save_daily_data()
self._update_queue_display()
self._adjust_window_size()
self.clear_input_fields()
self._check_button_state()
def _validate_phone_format(self, phone):
"""验证手机号格式并显示错误"""
if not re.fullmatch(r'^\d{11}$', phone):
messagebox.showerror("号码错误", f"❌ 请输入11位有效手机号(当前:{phone})", parent=self)
return False
return True
def _validate_people_count(self, people):
"""验证人数并显示错误"""
if not (1 <= people <= 9):
messagebox.showerror("人数错误", f"👥 请选择1-9人(当前:{people}人)", parent=self)
return False
return True
def call_next(self):
"""处理叫号逻辑"""
waiting_queue = [q for q in self.queue if q['status'] == '等待中']
if not waiting_queue:
messagebox.showwarning("队列已空", "📣 目前没有等待的号码", parent=self)
return
current_group = waiting_queue[0]
current_group['status'] = '已叫号'
current_group['call_time'] = datetime.now().strftime('%H:%M')
# 更新后续队列的等待人数
current_total = 0
for item in self.queue:
if item['status'] == '等待中':
item['total'] = current_total
current_total += item['people']
self._save_daily_data()
self._show_call_notification(current_group)
self._update_queue_display()
self._adjust_window_size()
self._check_button_state()
def _update_queue_display(self):
"""更新队列显示"""
self.queue_list.delete(0, tk.END)
for idx, item in enumerate(self.queue, 1):
bg_color = '#F8F9FA' if idx % 2 else '#FFFFFF'
if item['status'] == '已叫号':
bg_color = '#E9ECEF'
status_prefix = '✅' if item['status'] == '已叫号' else '⌛'
time_display = item.get('call_time', item['time'])
queue_text = (
f"{status_prefix} {item['number']} {item['phone']} "
f"👥{item['people']}人 前面:{item['total']}人 "
f"⏰ {time_display}"
)
self.queue_list.insert(tk.END, queue_text)
self.queue_list.itemconfig(idx-1, bg=bg_color)
def _adjust_window_size(self):
"""根据队列长度自动调整窗口高度"""
item_height = 28
required_height = 400 + (len(self.queue) - 10) * item_height
self.geometry(f"500x{max(400, required_height)}+{self.winfo_x()}+{self.winfo_y()}")
def update_time(self):
"""实时更新时间显示,跨天自动重置数据"""
now = datetime.now()
self.time_label.config(text=f"📅 {now.strftime('%Y-%m-%d')} {now.strftime('%H:%M:%S')}")
# 跨天自动重置(每天0点后首次检测到日期变化时重置)
if date.today() != date.fromisoformat(self._get_last_save_date()):
self._reset_daily_data()
messagebox.showinfo("每日重置", "🌅 新的一天开始,今日数据已重置", parent=self)
self.after(1000, self.update_time)
def _get_last_save_date(self):
"""获取最后保存的日期(避免文件不存在时错误)"""
try:
with open(DATA_FILE, 'r') as f:
return json.load(f).get('date', None)
except:
return None
def refresh_queue(self):
"""刷新队列数据(带动画效果)"""
self._load_daily_data()
self._update_queue_display()
self._adjust_window_size()
self._check_button_state()
# 动画效果:交替显示刷新图标
self.refresh_btn.config(text="⟳")
self.after(100, lambda: self.refresh_btn.config(text="🔄"))
self.after(200, lambda: self.refresh_btn.config(text="⟳"))
self.after(300, lambda: self.refresh_btn.config(text="🔄"))
def _check_button_state(self):
"""更新按钮可用状态"""
has_waiting = any(item['status'] == '等待中' for item in self.queue)
self.call_button.config(state='normal' if has_waiting else 'disabled')
def clear_input_fields(self):
"""清空输入框并聚焦"""
self.phone_var.set('')
self.people_var.set(1)
self.phone_entry.focus_set()
def _ask_continue(self, phone, remaining):
"""询问是否继续使用当前手机号排号"""
return messagebox.askyesno(
"继续排号",
f"ℹ️ 剩余{remaining}人需要排号\n是否继续使用当前手机号?\n📱 {phone[-4:]}*******",
icon='question',
parent=self
)
def _show_call_notification(self, item):
"""显示叫号通知"""
messagebox.showinfo(
"号码已叫",
f"📣 请 {item['number']} 到前台\n"
f"⏰ 取号时间:{item['time']}\n"
f"📞 手机号:{item['phone']}",
parent=self,
icon='info'
)
def center_window(self):
"""窗口居中显示"""
self.update_idletasks()
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x = (screen_width - 500) // 2
y = (screen_height - self.winfo_height()) // 2
self.geometry(f"500x{self.winfo_height()}+{x}+{y}")
if __name__ == "__main__":
app = QueueSystem()
app.mainloop()
这是代码,我想将它打包成可执行的exe文件,小白不懂,尝试多次,不成功,请大神指点一下