news 2026/4/15 12:04:50

大哥大点餐系统——Python Tkinter 桌面点餐管理系统(完整源码分享)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大哥大点餐系统——Python Tkinter 桌面点餐管理系统(完整源码分享)

大哥大饭店点餐系统**——Python Tkinter 桌面点餐管理系统(完整源码分享)**

项目简介:一个基于 Python + Tkinter 的轻量级桌面点餐系统,适合中小型饭店使用。实现了顾客点餐(支持辣度、备注)、管理员登录、菜品管理、订单持久化、营业数据统计与 Matplotlib 可视化展示。全程使用 JSON 文件存储数据,无需数据库,纯本地运行,开箱即用。

技术栈:Python 3.x、Tkinter、JSON、Matplotlib

开发日期:2025年12月

作者:kblj5555


前言

在学习 Python 编程的过程中,学校的课程设计要求我们做一个GUI项目,因为家里是开饭店的,所以我想做一个贴近自己家庭生活的实战项目,先用简单的做一个整体构思框架等,并在以后有机会的情况下做一个能用于自己家的小项目。于是我决定开发一个简单实用的大哥大饭店点餐系统

本系统完全使用 Python 标准库 Tkinter 实现界面,数据用 JSON 文件持久化,统计模块嵌入 Matplotlib 实现图表可视化。整个项目只有 4 个核心 py 文件 + 3 个 JSON 数据文件,结构清晰,易于二次开发。

本系统开发阶段由于时间紧张所以无法展现完整功能的作品,且大量借助了AI的力量,所以后续代码的结构不是很完整,且若大家代码有什么看不懂的且没有注释,希望大家借助AI来帮助自己,在这里感谢Tare CN、Gork、豆包对我的帮助。

主要功能:

  • 顾客:桌号选择、浏览菜品、双击点餐(可选辣度 + 备注)、订单管理、确认下单
  • 管理员:登录验证、创建管理员账号、菜品管理(框架)、查看订单、营业额统计(按天/按星期 + 图表)
  • 数据持久化:菜单、用户信息、营业数据全部本地保存,重启不丢失

下面分享整个开发思路、实现过程和完整源码,希望对正在学习 Tkinter 的同学有所帮助!


整体规划

项目结构

text

大哥大饭店点餐系统/ ├── main.py # 程序入口、主界面、桌号选择、管理者登录 ├── order.py # 顾客点餐核心模块 ├── guanli.py # 管理员管理模块(菜品管理、统计入口) ├── shuju.py # 营业数据统计与图表可视化 ├── 用户信息.json # 管理员账号密码(默认有几个测试账号) ├── 菜单.json # 菜品信息(19道菜示例) ├── 营业数据.json # 历史订单记录 └── README.md # 项目说明(就是本文)

模块职责划分

  • main.py:系统启动、欢迎界面、1-9桌选择、管理者登录跳转
  • order.py:点餐主逻辑,双击点餐、辣度备注、订单增删、确认下单并保存
  • guanli.py:管理员界面框架,菜品管理基础功能,跳转统计
  • shuju.py:独立统计窗口,按天(每小时折线图)/按星期(每天柱状图),支持中文显示

数据存储设计

全部使用 JSON 文件,无需额外安装数据库:

  • 用户信息.json:存储管理员账号、密码、角色
  • 菜单.json:菜品列表(id、name、price、category)
  • 营业数据.json:订单数组(订单编号、时间、桌号、菜品详情、总金额)

具体实现

1. 主入口与桌号选择(main.py)

使用九宫格按钮实现1-9桌选择,点击后销毁主窗口并传入桌号打开点餐界面。同时提供“管理者登录”入口,支持创建新管理员账号。

2. 顾客点餐(order.py)

  • 双击菜品弹出辣度(清淡/微辣/中辣/爆辣)和备注输入框
  • 右侧实时显示已选菜品,支持删除
  • 总金额自动计算
  • 确认下单后生成唯一订单编号,追加写入 营业数据.json

3. 管理员管理(guanli.py)

根据顾客点餐(order.py)基础框架,扩展完整菜品增删改查(两者基本上是相同的),减少桌号功能。可跳转到统计界面。

4. 营业数据统计(shuju.py)

  • 读取所有历史订单
  • 支持“按天”查看24小时销售额(折线图)
  • 支持“按星期”查看7天销售额(柱状图)
  • 使用 FigureCanvasTkAgg 将 Matplotlib 图表嵌入 Tkinter 窗口
  • 支持中文显示(SimHei 字体)

加分点

  • 辣度和备注自定义
  • Matplotlib 完美嵌入 Tkinter
  • 相对路径统一,项目可直接复制运行
  • 管理员账号动态创建

完整代码

1. main.py(程序入口)

Python

importtkinterastfromtkinterimportmessageboxasinfoimportjson,osimportguanli,order currentdir=os.path.dirname(os.path.abspath(__file__))USER_INFO_FILE=os.path.join(currentdir,"用户信息.json")# 读取用户信息defread_user_info():"""从JSON文件中读取用户信息列表"""try:withopen(USER_INFO_FILE,'r',encoding='utf-8')asfile:returnjson.load(file)exceptFileNotFoundError:info.showerror("错误","用户信息文件未找到")return[]# 保存用户信息defsave_user_info(user_info_list):"""将用户信息列表保存到JSON文件中"""try:withopen(USER_INFO_FILE,'w',encoding='utf-8')asfile:json.dump(user_info_list,file,ensure_ascii=False,indent=4)returnTrueexceptExceptionase:info.showerror("错误",f"保存用户信息失败:{str(e)}")returnFalse# 登录验证defvalidate_login(username,password):"""验证用户名和密码是否正确 Args: username: 用户名 password: 密码 Returns: 验证成功返回用户信息字典,失败返回None """user_info_list=read_user_info()foruserinuser_info_list:ifuser["账号"]==usernameanduser["密码"]==password:returnuserreturnNone# 打开订单系统defopen_order(desk_number):"""打开订单系统 Args: desk_number: 桌号 """try:# 直接调用order模块的create_order_window函数order.create_order_window(str(desk_number))exceptExceptionase:info.showerror("错误",f"打开订单系统失败:{str(e)}")# 管理者登录窗口defmanager_login_window():"""打开管理者登录窗口"""deflogin():"""处理登录按钮点击事件"""username=username_entry.get()password=password_entry.get()user=validate_login(username,password)ifuseranduser["角色"]=="管理者":login_window.destroy()open_manager_menu()else:info.showerror("错误","账号或密码错误,或无管理者权限")defback_to_main():"""返回主界面"""login_window.destroy()main_window()# 返回主界面login_window=t.Tk()login_window.title("管理者登录")login_window.geometry("400x300")# 增大窗口大小login_window.resizable(False,False)t.Label(login_window,text="管理者登录",font=("宋体",14)).pack(pady=10)t.Label(login_window,text="账号:").pack(pady=5)username_entry=t.Entry(login_window)# 账号输入框username_entry.pack()t.Label(login_window,text="密码:").pack(pady=5)password_entry=t.Entry(login_window,show="*")# 密码输入框,密码以*显示password_entry.pack()button_frame=t.Frame(login_window)# 创建按钮框架button_frame.pack(pady=20)t.Button(button_frame,text="登录",command=login,width=15).pack(side="left",padx=10)t.Button(button_frame,text="返回主界面",command=back_to_main,width=15).pack(side="left",padx=10)# 管理者菜单defopen_manager_menu():"""打开管理者菜单窗口"""defcreate_new_account():"""创建新管理者账号"""defsave_new_account():"""保存新账号"""new_username=new_username_entry.get()new_password=new_password_entry.get()new_role="管理者"ifnotnew_usernameornotnew_password:info.showwarning("警告","账号和密码不能为空")returnuser_info_list=read_user_info()# 检查账号是否已存在foruserinuser_info_list:ifuser["账号"]==new_username:info.showwarning("警告","账号已存在")return# 添加新账号new_user={"账号":new_username,"密码":new_password,"角色":new_role}user_info_list.append(new_user)ifsave_user_info(user_info_list):info.showinfo("成功","新账号创建成功")account_window.destroy()manager_window.destroy()main_window()# 创建成功后返回主界面defback_to_manager_menu():"""返回管理者菜单"""account_window.destroy()account_window=t.Toplevel()account_window.title("创建新账号")account_window.geometry("300x250")account_window.resizable(False,False)t.Label(account_window,text="创建管理者账号",font=("宋体",14)).pack(pady=10)t.Label(account_window,text="新账号:").pack(pady=5)new_username_entry=t.Entry(account_window)# 新账号输入框new_username_entry.pack()t.Label(account_window,text="新密码:").pack(pady=5)new_password_entry=t.Entry(account_window,show="*")# 新密码输入框new_password_entry.pack()button_frame=t.Frame(account_window)# 按钮框架button_frame.pack(pady=20)t.Button(button_frame,text="创建",command=save_new_account,width=12).pack(side="left",padx=5)t.Button(button_frame,text="返回",command=back_to_manager_menu,width=12).pack(side="left",padx=5)defopen_management_system():"""打开管理系统"""try:manager_window.destroy()# 直接调用guanli模块的create_guanli_window函数guanli.create_guanli_window()exceptExceptionase:info.showerror("错误",f"打开管理系统失败:{str(e)}")defback_to_main():"""返回主界面"""manager_window.destroy()main_window()# 返回主界面manager_window=t.Tk()manager_window.title("管理者菜单")manager_window.geometry("400x300")manager_window.resizable(False,False)t.Label(manager_window,text="管理者菜单",font=("宋体",16)).pack(pady=20)t.Button(manager_window,text="创建新管理者账号",command=create_new_account,width=20,height=2).pack(pady=10)t.Button(manager_window,text="打开管理系统",command=open_management_system,width=20,height=2).pack(pady=10)t.Button(manager_window,text="返回主界面",command=back_to_main,width=20,height=2).pack(pady=10)# 主界面defmain_window():"""打开系统主界面"""window=t.Tk()window.title("大哥大饭店 - 登录系统")window.geometry("1000x800")window.resizable(False,False)# 欢迎语t.Label(window,text="欢迎来到大哥大饭店",font=("宋体",24),fg="red").pack(pady=30)# 桌号按钮区域t.Label(window,text="请选择桌号",font=("宋体",16)).pack(pady=10)# 九宫格按钮布局button_frame=t.Frame(window)# 创建按钮框架button_frame.pack(pady=20)# 创建1-9的桌号按钮foriinrange(1,10):row=(i-1)//3# 计算按钮所在行(0-2)col=(i-1)%3# 计算按钮所在列(0-2)# 创建桌号按钮,点击后关闭当前窗口并打开点餐系统,同时传递桌号参数btn=t.Button(button_frame,text=str(i),width=15,height=5,font=("宋体",14),command=lambdanum=i:[window.destroy(),open_order(num)])btn.grid(row=row,column=col,padx=15,pady=15)# 设置按钮位置和间距# 管理者登录按钮manager_btn=t.Button(window,text="管理者登录",command=lambda:[window.destroy(),manager_login_window()],width=20,height=2,font=("宋体",12))manager_btn.pack(pady=15)# 减小pady值,确保按钮可见window.mainloop()# 启动主界面循环# 启动程序if__name__=="__main__":main_window()# 启动程序主界面

2. order.py(顾客点餐核心)

Python

importtkinterastfromtkinterimportmessageboxasinfoimportjsonimportos,datetime,subprocess,sysimportmain,shuju# 基本初始数据# 订单信息ordered_items=[]total_price=0.0current_desk_number="未知桌号"# 当前桌号,默认为未知桌号# 初始化桌号函数definit_desk_number():"""从命令行参数获取并初始化桌号"""globalcurrent_desk_numberimportsysiflen(sys.argv)>1:current_desk_number=sys.argv[1]# 类集合# 辣度类classLadu:def__init__(self,name):self.name=namedef__str__(self):returnself.name# 菜单类classDish:def__init__(self,name,price,category="热菜",ladu=Ladu("微辣"),beizhu="无"):self.name=name self.price=price self.category=category self.ladu=ladu self.beizhu=beizhu# 菜单显示格式(和__init__同级缩进!)defdisplay_in_menu(self):returnf"{self.name}-{self.price}元 -{self.category}"# 菜品字符串表示,用于已选列表显示def__str__(self):returnf"{self.name}-{self.ladu}- 备注:{self.beizhu}-{self.price}元"# 菜品库menu_items={}# 函数集合# 保存菜单函数defsave_menu_to_json():# 保存内容到菜单文件globalmenu json_file=r"./菜单.json"try:menu_data=[]fora,(dish_name,dish_object)inenumerate(menu_items.items(),1):menu_data.append({"id":a,"name":dish_object.name,"price":dish_object.price,"category":dish_object.category})withopen(json_file,"w",encoding="utf-8")asfile:json.dump(menu_data,file,ensure_ascii=False,indent=4)print(f"保存了{len(menu_data)}菜品")exceptExceptionase:print(f"保存失败,原因为{str(e)}")# 保存营业额函数defsave_sales_data():"""将当前订单保存为营业数据到JSON文件"""globalordered_items,total_price sales_file_path=r"./营业数据.json"try:# 获取当前时间now=datetime.datetime.now()current_date=now.strftime("%Y%m%d")# YYYYMMDD格式的日期current_time=now.strftime("%Y-%m-%d %H:%M:%S")# 完整时间格式# 读取现有营业数据sales_data=[]ifos.path.exists(sales_file_path):try:withopen(sales_file_path,'r',encoding='utf-8')asfile:file_content=file.read().strip()iffile_content:sales_data=json.loads(file_content)exceptjson.JSONDecodeError:# 文件内容不是有效的JSON,创建一个新的空列表sales_data=[]# 生成订单编号(当天时间+编号)today_orders=0fororderinsales_data:if'订单编号'inorderandorder['订单编号'].startswith(current_date):today_orders+=1order_number=f"{current_date}{today_orders+1:03d}"# 生成订单编号# 准备订单详情order_details=[]fordishinordered_items:order_details.append({"菜品名":dish.name,"辣度":str(dish.ladu),"备注":dish.beizhu,"价格":dish.price})# 创建新订单数据new_order={"订单编号":order_number,"时间":current_time,"桌号":current_desk_number,# 使用当前桌号"具体内容":order_details,"总金额":total_price}# 添加新订单到营业数据sales_data.append(new_order)# 保存更新后的营业数据(确保使用UTF-8编码)withopen(sales_file_path,'w',encoding='utf-8',newline='')asfile:json.dump(sales_data,file,ensure_ascii=False,indent=4,default=str)print(f"营业数据保存成功:{order_number}")exceptExceptionase:print(f"保存营业数据失败:{str(e)}")info.showerror("错误",f"保存营业数据失败:{str(e)}")# 清空当前订单函数defclear_current_order():"""清空当前订单的所有菜品和总价"""globalordered_items,total_price ordered_items=[]# 清空已点菜品列表total_price=0.0# 清空总价selected_listbox.delete(0,"end")# 清空列表框显示# 读取菜单函数defmenu():# 从json文件加载菜单数据并传唤为菜单globalmenu_items json_path=r'./菜单.json'try:withopen(json_path,'r',encoding='utf-8')asfile:menu_data=json.load(file)# 将json数据转换为dish对象字典menu_items={}foriteminmenu_data:dish_name=item['name']dish_price=item['price']dish_category=item.get('category','热菜')menu_items[dish_name]=Dish(dish_name,dish_price,dish_category)print(f"共打印了{len(menu_items)}个菜品")exceptFileNotFoundError:print(f"JSON文件{json}为找到")# 备用方案menu_items={"麻辣香锅":Dish("麻辣香锅",58,"热菜"),"水煮鱼":Dish("水煮鱼",88,"热菜"),"回锅肉":Dish("回锅肉",42,"热菜"),"番茄炒蛋":Dish("番茄炒蛋",22,"热菜"),"鱼香肉丝":Dish("鱼香肉丝",38,"热菜"),}save_menu_to_json()# 加菜函数defadd_dish(dish_object):"""加菜函数说明 点击下单后弹出备注窗口 功能如下: 辣度选择 备注 取消下单 """"""弹出辣度选择窗口,让用户选择菜品的辣度和备注"""# 创建新的辣度选择窗口beizhu_window=t.Toplevel()beizhu_window.title(f"选择{dish_object.name}的辣度")beizhu_window.geometry("300x250")# 固定窗口大小# 辣度选择(单选按钮)ladu_var=t.StringVar(value="微辣")# 默认微辣t.Label(beizhu_window,text="选择辣度:").pack(pady=5)t.Radiobutton(beizhu_window,text="清淡",variable=ladu_var,value="清淡").pack()t.Radiobutton(beizhu_window,text="微辣",variable=ladu_var,value="微辣").pack()t.Radiobutton(beizhu_window,text="中辣",variable=ladu_var,value="中辣").pack()t.Radiobutton(beizhu_window,text="爆辣",variable=ladu_var,value="爆辣").pack()# 备注输入框t.Label(beizhu_window,text="备注(忌口等):").pack(pady=5)beizhu_var=t.StringVar(value="无")# 默认无备注beizhu_entry=t.Entry(beizhu_window,textvariable=beizhu_var)beizhu_entry.pack()# 确认添加按钮函数defxiadan():"""确认添加菜品到订单"""selected_ladu_name=ladu_var.get()# 获取选择的辣度名称selected_beizhu=beizhu_var.get()# 获取备注# 创建辣度对象selected_ladu=Ladu(selected_ladu_name)# 创建新的菜品对象(包含用户选择的辣度和备注)selected_dish=Dish(dish_object.name,dish_object.price,dish_object.category,selected_ladu,selected_beizhu)# 将菜品对象添加到订单ordered_items.append(selected_dish)globaltotal_price# 声明使用全局变量total_price+=dish_object.price# 更新总价# 更新右侧已选菜品列表selected_listbox.insert("end",str(selected_dish))# 关闭辣度选择窗口beizhu_window.destroy()# 显示成功信息info.showinfo("提示",f"{dish_object.name}已加入订单!")# 返回主界面函数defback():"""返回主界面,不添加菜品"""beizhu_window.destroy()# 创建按钮容器button_frame=t.Frame(beizhu_window)button_frame.pack(pady=10)# 添加按钮t.Button(button_frame,text="确认添加",command=xiadan).pack(side="left",padx=5)t.Button(button_frame,text="返回",command=back).pack(side="left",padx=5)# 删菜函数defdelete_dish():"""删除选中的已点菜品"""# 获取选中菜品的索引selected_index=selected_listbox.curselection()ifnotselected_index:# 如果没有选中任何菜品则显示提示info.showwarning("提示","先选中要删除的菜品!")returnindex=selected_index[0]# 从订单中删除菜品对象并更新总价deleted_dish=ordered_items.pop(index)globaltotal_price total_price-=deleted_dish.price# 更新总价# 从列表框显示中删除selected_listbox.delete(index)info.showinfo("提示",f"已删除{deleted_dish.name}")# 预览菜单函数defview_preview():"""显示当前订单的预览信息"""ifnotordered_items:# 如果没有点任何菜品则显示提示info.showwarning("提示","还没点任何菜品!")return# 组装预览内容preview_content="===== 订单预览 =====\n"fornumber,dish_objectinenumerate(ordered_items,1):preview_content+=f"{number}.{dish_object.name}-{dish_object.ladu}- 备注:{dish_object.beizhu}-{dish_object.price}元\n"preview_content+=f"\n总价:{total_price}元"# 显示弹窗info.showinfo("订单预览",preview_content)# 下单函数defxiadan_dish():"""确认当前订单并保存营业数据"""ifnotordered_items:info.showwarning("提示","没点菜品不能下单!")return# 组装订单信息order_content="===== 下单成功 =====\n"fornumber,dish_objectinenumerate(ordered_items,1):order_content+=f"{number}.{dish_object.name}-{dish_object.ladu}- 备注:{dish_object.beizhu}-{dish_object.price}元\n"order_content+=f"\n总价:{total_price}元\n\n坐等上菜!"info.showinfo("下单成功",order_content)# 保存营业数据到JSON文件save_sales_data()# 清空当前订单clear_current_order()# 用户/加菜功能函数集合调用函数defuser_function_buttons():"""创建底部的功能按钮区域"""bottom_container=t.Frame(order_window)# 创建按钮容器bottom_container.pack(pady=20)# 放置容器# 删除按钮t.Button(bottom_container,text="删除选中菜品",command=delete_dish,width=15).pack(side="left",padx=10)# 预览按钮t.Button(bottom_container,text="查看订单预览",command=view_preview,width=15).pack(side="left",padx=10)# 下单按钮t.Button(bottom_container,text="确认下单",command=xiadan_dish,width=15).pack(side="left",padx=10)# 返回主界面按钮t.Button(bottom_container,text="返回主界面",command=lambda:[order_window.destroy(),main.main_window()],width=15).pack(side="left",padx=10)# 加载菜单数据menu()# 初始化桌号init_desk_number()# 更新菜单函数defupdate_dish_list():"""更新菜品列表显示"""# 清空当前列表dish_listbox.delete(0,"end")# 重新填充菜品列表fordish_objectinmenu_items.values():dish_listbox.insert("end",dish_object.display_in_menu())# 双击点餐函数defdouble_click_order(event):"""双击菜品列表触发点餐功能"""# 获取选中菜品的索引selected_index=dish_listbox.curselection()ifselected_index:index=selected_index[0]# 获取菜品项文本dish_item=dish_listbox.get(index)# 解析菜品名dish_name=dish_item.split(" - ")[0]# 从菜单中获取菜品对象dish_object=menu_items[dish_name]# 调用添加菜品函数(传递菜品对象)add_dish(dish_object)# 主窗口创建函数defcreate_order_window(desk_number="未知桌号"):"""创建并显示订单窗口 Args: desk_number: 桌号,默认为"未知桌号" """globalcurrent_desk_number,order_window,dish_listbox,selected_listbox current_desk_number=desk_number# ========== 搭建界面 ==========# 1. 创建订单窗口order_window=t.Tk()# 创建主窗口order_window.title(f"大哥大饭店 - 桌号{current_desk_number}")# 设置窗口标题,包含桌号order_window.geometry("800x500")# 窗口大小,适当增大以容纳新按钮# 2. 顶部欢迎信息welcome_label=t.Label(order_window,text="欢迎来到大哥大饭店管理系统!",font=("宋体",14))welcome_label.pack(pady=10)# 3. 中间区域:左侧菜品表格 + 右侧已选列表middle_container=t.Frame(order_window)# 创建中间容器middle_container.pack(fill="both",expand=True,padx=20)# 左侧:菜品列表middle_container=t.Frame(order_window)middle_container.pack(fill="both",expand=True,padx=20)# 左侧:菜品列表(使用Listbox与右侧已选列表保持一致格式)dish_table=t.Frame(middle_container,bd=1,relief="solid")dish_table.pack(side="left",fill="both",expand=True,padx=10)t.Label(dish_table,text="菜品列表(双击点餐)",font=("宋体",12)).pack(pady=5)# 菜品列表框(与右侧已选列表保持一致格式)dish_listbox=t.Listbox(dish_table,width=30,height=10)dish_listbox.pack(pady=5,padx=5)# 初始填充菜品列表update_dish_list()dish_listbox.bind("<Double-Button-1>",double_click_order)# ========== 中间功能按钮区(动态菜单功能) ==========''' # 调用修改菜单数据函数 modify_dish() '''# 右侧:已选菜品列表selected_container=t.Frame(middle_container,bd=1,relief="solid")selected_container.pack(side="right",fill="both",expand=True,padx=10)t.Label(selected_container,text="已选菜品",font=("宋体",12)).pack(pady=5)# 左侧:已选菜品列表框selected_listbox=t.Listbox(selected_container,width=30,height=10)selected_listbox.pack(pady=5,padx=5)# 启用用户功能按钮user_function_buttons()# ========== 第四步:运行程序 ==========order_window.mainloop()# 启动主循环,显示界面# 如果直接运行该文件,则创建订单窗口if__name__=="__main__":# 初始化桌号init_desk_number()# 创建订单窗口create_order_window(current_desk_number)

3. guanli.py(管理员管理)

Python

importtkinterastfromtkinterimportmessageboxasinfoimportjsonimportos,datetimeimportmain,shuju# 基本初始数据# 订单信息ordered_items=[]total_price=0.0#类集合#辣度类classLadu:def__init__(self,name):self.name=namedef__str__(self):returnself.name#菜单类classDish:def__init__(self,name,price,category="热菜",ladu=Ladu("微辣"),beizhu="无"):self.name=name self.price=price self.category=category self.ladu=ladu self.beizhu=beizhu# 菜单显示格式(和__init__同级缩进!)defdisplay_in_menu(self):returnf"{self.name}-{self.price}元 -{self.category}"# 菜品字符串表示,用于已选列表显示def__str__(self):returnf"{self.name}-{self.ladu}- 备注:{self.beizhu}-{self.price}元"# 菜品库menu_items={}# 函数集合#保存菜单函数defsave_menu_to_json():#保存内容到菜单文件globalmenu json_file=r"./菜单.json"try:menu_data=[]fora,(dish_name,dish_object)inenumerate(menu_items.items(),1):menu_data.append({"id":a,"name":dish_object.name,"price":dish_object.price,"category":dish_object.category})withopen(json_file,"w",encoding="utf-8")asfile:json.dump(menu_data,file,ensure_ascii=False,indent=4)print(f"保存了{len(menu_data)}菜品")exceptExceptionase:print(f"保存失败,原因为{str(e)}")# 保存营业额函数defsave_sales_data():"""将当前订单保存为营业数据到JSON文件"""globalordered_items,total_price sales_file_path=r"./营业数据.json"try:# 获取当前时间now=datetime.datetime.now()current_date=now.strftime("%Y%m%d")# YYYYMMDD格式的日期current_time=now.strftime("%Y-%m-%d %H:%M:%S")# 完整时间格式# 读取现有营业数据sales_data=[]ifos.path.exists(sales_file_path):try:withopen(sales_file_path,'r',encoding='utf-8')asfile:file_content=file.read().strip()iffile_content:sales_data=json.loads(file_content)exceptjson.JSONDecodeError:# 文件内容不是有效的JSON,创建一个新的空列表sales_data=[]# 生成订单编号(当天时间+编号)today_orders=0fororderinsales_data:if'订单编号'inorderandorder['订单编号'].startswith(current_date):today_orders+=1order_number=f"{current_date}{today_orders+1:03d}"# 生成订单编号# 准备订单详情order_details=[]fordishinordered_items:order_details.append({"菜品名":dish.name,"辣度":str(dish.ladu),"备注":dish.beizhu,"价格":dish.price})# 创建新订单数据new_order={"订单编号":order_number,"时间":current_time,"桌号":"未知桌号",# 管理系统订单设置默认桌号"具体内容":order_details,"总金额":total_price}# 添加新订单到营业数据sales_data.append(new_order)# 保存更新后的营业数据(确保使用UTF-8编码)withopen(sales_file_path,'w',encoding='utf-8',newline='')asfile:json.dump(sales_data,file,ensure_ascii=False,indent=4,default=str)print(f"营业数据保存成功:{order_number}")exceptExceptionase:print(f"保存营业数据失败:{str(e)}")info.showerror("错误",f"保存营业数据失败:{str(e)}")#清空当前订单函数defclear_current_order():"""清空当前订单的所有菜品和总价"""globalordered_items,total_price ordered_items=[]# 清空已点菜品列表total_price=0.0# 清空总价selected_listbox.delete(0,"end")# 清空列表框显示# 读取菜单函数defmenu():#从json文件加载菜单数据并传唤为菜单globalmenu_items json_path=r'./菜单.json'try:withopen(json_path,'r',encoding='utf-8')asfile:menu_data=json.load(file)# 将json数据转换为dish对象字典menu_items={}foriteminmenu_data:dish_name=item['name']dish_price=item['price']dish_category=item.get('category','热菜')menu_items[dish_name]=Dish(dish_name,dish_price,dish_category)print(f"共打印了{len(menu_items)}个菜品")exceptFileNotFoundError:print(f"JSON文件{json}为找到")# 备用方案menu_items={"麻辣香锅":Dish("麻辣香锅",58,"热菜"),"水煮鱼":Dish("水煮鱼",88,"热菜"),"回锅肉":Dish("回锅肉",42,"热菜"),"番茄炒蛋":Dish("番茄炒蛋",22,"热菜"),"鱼香肉丝":Dish("鱼香肉丝",38,"热菜"),}save_menu_to_json()# 加菜函数defadd_dish(dish_object):"""加菜函数说明 点击下单后弹出备注窗口 功能如下: 辣度选择 备注 取消下单 """"""弹出辣度选择窗口,让用户选择菜品的辣度和备注"""# 创建新的辣度选择窗口beizhu_window=t.Toplevel()beizhu_window.title(f"选择{dish_object.name}的辣度")beizhu_window.geometry("300x250")# 固定窗口大小# 辣度选择(单选按钮)ladu_var=t.StringVar(value="微辣")# 默认微辣t.Label(beizhu_window,text="选择辣度:").pack(pady=5)t.Radiobutton(beizhu_window,text="清淡",variable=ladu_var,value="清淡").pack()t.Radiobutton(beizhu_window,text="微辣",variable=ladu_var,value="微辣").pack()t.Radiobutton(beizhu_window,text="中辣",variable=ladu_var,value="中辣").pack()t.Radiobutton(beizhu_window,text="爆辣",variable=ladu_var,value="爆辣").pack()# 备注输入框t.Label(beizhu_window,text="备注(忌口等):").pack(pady=5)beizhu_var=t.StringVar(value="无")# 默认无备注beizhu_entry=t.Entry(beizhu_window,textvariable=beizhu_var)beizhu_entry.pack()# 确认添加按钮函数defxiadan():"""确认添加菜品到订单"""selected_ladu_name=ladu_var.get()# 获取选择的辣度名称selected_beizhu=beizhu_var.get()# 获取备注# 创建辣度对象selected_ladu=Ladu(selected_ladu_name)# 创建新的菜品对象(包含用户选择的辣度和备注)selected_dish=Dish(dish_object.name,dish_object.price,dish_object.category,selected_ladu,selected_beizhu)# 将菜品对象添加到订单ordered_items.append(selected_dish)globaltotal_price# 声明使用全局变量total_price+=dish_object.price# 更新总价# 更新右侧已选菜品列表selected_listbox.insert("end",str(selected_dish))# 关闭辣度选择窗口beizhu_window.destroy()# 显示成功信息info.showinfo("提示",f"{dish_object.name}已加入订单!")# 返回主界面函数defback():"""返回主界面,不添加菜品"""beizhu_window.destroy()# 创建按钮容器button_frame=t.Frame(beizhu_window)button_frame.pack(pady=10)# 添加按钮t.Button(button_frame,text="确认添加",command=xiadan).pack(side="left",padx=5)t.Button(button_frame,text="返回",command=back).pack(side="left",padx=5)#删菜函数defdelete_dish():"""删除选中的已点菜品"""# 获取选中菜品的索引selected_index=selected_listbox.curselection()ifnotselected_index:# 如果没有选中任何菜品则显示提示info.showwarning("提示","先选中要删除的菜品!")returnindex=selected_index[0]# 从订单中删除菜品对象并更新总价deleted_dish=ordered_items.pop(index)globaltotal_price total_price-=deleted_dish.price# 更新总价# 从列表框显示中删除selected_listbox.delete(index)info.showinfo("提示",f"已删除{deleted_dish.name}")# 预览菜单函数defview_preview():"""显示当前订单的预览信息"""ifnotordered_items:# 如果没有点任何菜品则显示提示info.showwarning("提示","还没点任何菜品!")return# 组装预览内容preview_content="===== 订单预览 =====\n"fornumber,dish_objectinenumerate(ordered_items,1):preview_content+=f"{number}.{dish_object.name}-{dish_object.ladu}- 备注:{dish_object.beizhu}-{dish_object.price}元\n"preview_content+=f"\n总价:{total_price}元"# 显示弹窗info.showinfo("订单预览",preview_content)# 下单函数defxiadan_dish():"""确认当前订单并保存营业数据"""ifnotordered_items:info.showwarning("提示","没点菜品不能下单!")return# 组装订单信息order_content="===== 下单成功 =====\n"fornumber,dish_objectinenumerate(ordered_items,1):order_content+=f"{number}.{dish_object.name}-{dish_object.ladu}- 备注:{dish_object.beizhu}-{dish_object.price}元\n"order_content+=f"\n总价:{total_price}元\n\n坐等上菜!"info.showinfo("下单成功",order_content)# 保存营业数据到JSON文件save_sales_data()# 清空当前订单clear_current_order()# 用户/加菜功能函数集合调用函数defuser_function_buttons():"""创建底部的功能按钮区域"""bottom_container=t.Frame(order_window)# 创建按钮容器bottom_container.pack(pady=20)# 放置容器# 删除按钮t.Button(bottom_container,text="删除选中菜品",command=delete_dish,width=15).pack(side="left",padx=10)# 预览按钮t.Button(bottom_container,text="查看订单预览",command=view_preview,width=15).pack(side="left",padx=10)# 下单按钮t.Button(bottom_container,text="确认下单",command=xiadan_dish,width=15).pack(side="left",padx=10)# 数据分析按钮t.Button(bottom_container,text="数据分析",command=lambda:shuju.shuju_gui(order_window),width=15).pack(side="left",padx=10)# 返回主界面按钮t.Button(bottom_container,text="返回主界面",command=lambda:[order_window.destroy(),main.main_window()],width=15).pack(side="left",padx=10)# 加载菜单数据menu()# 修改菜单数据函数(管理功能)defmodify_dish():"""菜单管理功能,包括添加、修改、删除菜品"""# 添加新菜品defadd_new_dish():"""添加新菜品到菜单"""new_dish_window=t.Toplevel()new_dish_window.title("添加新菜品")new_dish_window.geometry("300x250")# 增加窗口高度以容纳品类选择t.Label(new_dish_window,text="菜品名:").pack(pady=5)new_dish_name=t.StringVar()t.Entry(new_dish_window,textvariable=new_dish_name).pack()t.Label(new_dish_window,text="价格:").pack(pady=5)new_price=t.StringVar()t.Entry(new_dish_window,textvariable=new_price).pack()# 添加品类选择t.Label(new_dish_window,text="品类:").pack(pady=5)new_category=t.StringVar(value="热菜")# 默认热菜category_options=["热菜","凉菜","主食","饮料"]category_dropdown=t.OptionMenu(new_dish_window,new_category,*category_options)category_dropdown.pack()defconfirm_add_new_dish():"""确认添加新菜品"""dish_name=new_dish_name.get()# 获取菜品名price=new_price.get()# 获取价格category=new_category.get()# 获取品类ifnotdish_nameornotprice:info.showwarning("提示","请输入菜品名和价格!")returntry:price=float(price)# 转换价格为浮点数menu_items[dish_name]=Dish(dish_name,price,category)# 创建新菜品update_dish_list()# 更新菜品列表显示save_menu_to_json()# 保存到JSON文件new_dish_window.destroy()info.showinfo("提示","新菜品添加成功!")exceptValueError:info.showwarning("提示","价格必须是数字!")defback():"""返回菜单管理界面"""new_dish_window.destroy()button_frame=t.Frame(new_dish_window)button_frame.pack(pady=10)t.Button(button_frame,text="确认添加",command=confirm_add_new_dish,width=12).pack(side="left",padx=5)t.Button(button_frame,text="返回",command=back,width=12).pack(side="left",padx=5)# 修改菜品价格defmodify_dish_price():"""修改现有菜品的价格"""modify_window=t.Toplevel()modify_window.title("修改菜品价格")modify_window.geometry("300x200")t.Label(modify_window,text="选择菜品:").pack(pady=5)selected_menu_item=t.StringVar()dish_dropdown=t.OptionMenu(modify_window,selected_menu_item,*menu_items.keys())dish_dropdown.pack()t.Label(modify_window,text="新价格:").pack(pady=5)new_price=t.StringVar()t.Entry(modify_window,textvariable=new_price).pack()defconfirm_modify_price():"""确认修改菜品价格"""dish_name=selected_menu_item.get()# 获取选择的菜品price=new_price.get()# 获取新价格ifnotdish_nameornotprice:info.showwarning("提示","请选择菜品并输入新价格!")returntry:price=float(price)# 转换价格为浮点数# 更新菜品对象价格dish_object=menu_items[dish_name]dish_object.price=price update_dish_list()# 更新菜品列表显示save_menu_to_json()# 保存到JSON文件modify_window.destroy()info.showinfo("提示","价格修改成功!")exceptValueError:info.showwarning("提示","价格必须是数字!")defback():"""返回菜单管理界面"""modify_window.destroy()button_frame=t.Frame(modify_window)button_frame.pack(pady=10)t.Button(button_frame,text="确认修改",command=confirm_modify_price,width=12).pack(side="left",padx=5)t.Button(button_frame,text="返回",command=back,width=12).pack(side="left",padx=5)# 从菜单中删除菜品defdelete_dish_from_menu():"""从菜单中删除菜品"""delete_window=t.Toplevel()delete_window.title("删除菜品")delete_window.geometry("300x150")t.Label(delete_window,text="选择要删除的菜品:").pack(pady=5)selected_menu_item=t.StringVar()dish_dropdown=t.OptionMenu(delete_window,selected_menu_item,*menu_items.keys())dish_dropdown.pack()defconfirm_delete():"""确认删除菜品"""dish_name=selected_menu_item.get()# 获取要删除的菜品ifnotdish_name:info.showwarning("提示","请选择要删除的菜品!")returnifinfo.askyesno("确认",f"确定要删除{dish_name}吗?"):delmenu_items[dish_name]# 删除菜品update_dish_list()# 更新菜品列表显示save_menu_to_json()# 保存到JSON文件delete_window.destroy()info.showinfo("提示","菜品删除成功!")defback():"""返回菜单管理界面"""delete_window.destroy()button_frame=t.Frame(delete_window)button_frame.pack(pady=10)t.Button(button_frame,text="确认删除",command=confirm_delete,width=12).pack(side="left",padx=5)t.Button(button_frame,text="返回",command=back,width=12).pack(side="left",padx=5)# 创建功能按钮容器function_button_area=t.Frame(dish_table)function_button_area.pack(pady=10)# 添加按钮t.Button(function_button_area,text="添加新菜品",command=add_new_dish).pack(side="left",padx=5)t.Button(function_button_area,text="修改菜品价格",command=modify_dish_price).pack(side="left",padx=5)t.Button(function_button_area,text="删除菜品",command=delete_dish_from_menu).pack(side="left",padx=5)# 更新菜单函数defupdate_dish_list():"""更新菜品列表显示"""# 清空当前列表dish_listbox.delete(0,"end")# 重新填充菜品列表fordish_objectinmenu_items.values():dish_listbox.insert("end",dish_object.display_in_menu())#双击点餐函数defdouble_click_order(event):"""双击菜品列表触发点餐功能"""# 获取选中菜品的索引selected_index=dish_listbox.curselection()ifselected_index:index=selected_index[0]# 获取菜品项文本dish_item=dish_listbox.get(index)# 解析菜品名dish_name=dish_item.split(" - ")[0]# 从菜单中获取菜品对象dish_object=menu_items[dish_name]# 调用添加菜品函数(传递菜品对象)add_dish(dish_object)# 主窗口创建函数defcreate_guanli_window():"""创建并显示管理系统窗口"""globalorder_window,dish_listbox,selected_listbox,dish_table# ========== 搭建界面 ==========# 1. 创建订单窗口order_window=t.Tk()# 创建主窗口order_window.title("大哥大饭店")# 设置窗口标题order_window.geometry("800x500")# 窗口大小,适当增大以容纳新按钮# 2. 顶部欢迎信息welcome_label=t.Label(order_window,text="欢迎来到大哥大饭店管理系统!",font=("宋体",14))welcome_label.pack(pady=10)# 3. 中间区域:左侧菜品表格 + 右侧已选列表middle_container=t.Frame(order_window)# 创建中间容器middle_container.pack(fill="both",expand=True,padx=20)# 左侧:菜品列表middle_container=t.Frame(order_window)middle_container.pack(fill="both",expand=True,padx=20)# 左侧:菜品列表(使用Listbox与右侧已选列表保持一致格式)dish_table=t.Frame(middle_container,bd=1,relief="solid")dish_table.pack(side="left",fill="both",expand=True,padx=10)t.Label(dish_table,text="菜品列表(双击点餐)",font=("宋体",12)).pack(pady=5)# 菜品列表框(与右侧已选列表保持一致格式)dish_listbox=t.Listbox(dish_table,width=30,height=10)dish_listbox.pack(pady=5,padx=5)# 更新菜品列表的函数(可调用以刷新显示)# 初始填充菜品列表update_dish_list()dish_listbox.bind("<Double-Button-1>",double_click_order)# ========== 中间功能按钮区(动态菜单功能) ==========# 调用修改菜单数据函数modify_dish()# 右侧:已选菜品列表selected_container=t.Frame(middle_container,bd=1,relief="solid")selected_container.pack(side="right",fill="both",expand=True,padx=10)t.Label(selected_container,text="已选菜品",font=("宋体",12)).pack(pady=5)# 左侧:已选菜品列表框selected_listbox=t.Listbox(selected_container,width=30,height=10)selected_listbox.pack(pady=5,padx=5)# 启用用户功能按钮user_function_buttons()# ========== 第四步:运行程序 ==========order_window.mainloop()# 启动主循环,显示界面# 如果直接运行该文件,则创建管理系统窗口if__name__=="__main__":create_guanli_window()

4. shuju.py(数据统计与可视化)

Python

importjsonimportosimportdatetimeimporttkinterastfromtkinterimportmessageboxasinfo,ttkimportmatplotlib.pyplotaspltfrommatplotlib.backends.backend_tkaggimportFigureCanvasTkAgg# 设置matplotlib支持中文plt.rcParams['font.sans-serif']=['SimHei']# 使用黑体字体,支持中文显示plt.rcParams['axes.unicode_minus']=False# 负号显示#读取文件数据defdu_qu_shuju():"""读取文件数据 返回值: list:包含所有订单数据的列表,如果失败返回空列表 """#读取当前目录以绝对路径获取文件数据dangqian_dir=os.path.dirname(os.path.abspath(__file__))shuju=os.path.join(dangqian_dir,'营业数据.json')yingye_data=[]try:ifos.path.exists(shuju):withopen(shuju,'r',encoding='utf-8')asfile:content=file.read().strip()ifcontent:yingye_data=json.loads(content)returnyingye_dataexceptExceptionase:info.showerror("错误",f"读取数据失败")return[]#小时函数defhour_sales(data,riqi):"""按小时统计指定日期的销售额 参数: data: 包含所有订单数据的列表 riqi: 要统计的日期,格式为"YYYY-MM-DD" 命名: hour_sales: 符合条件的日期 order_time: 订单时间 返回值: dict: 包含每个小时销售额的字典,键为小时(如"00"-"23"),值为销售额 """hour_sales={}# 遍历所有订单fororderindata:try:# 查找关键字存储起来time_key=Noneforkeyinorder:if'时间'inkey:time_key=keybreak# 修正:没有找到时间字段才跳过iftime_keyisNone:continue# 解析订单时间order_time=datetime.datetime.strptime(order[time_key],'%Y-%m-%d %H:%M:%S')# 获取订单日期order_date=order_time.strftime("%Y-%m-%d")# 只统计选择的日期iforder_date!=riqi:continue# 提取订单时间的时段hour=order_time.strftime("%H")# 修正:查找总金额字段逻辑total_key=Noneforkeyinorder:if'总金额'inkey:total_key=keybreak# 没有总金额字段则跳过iftotal_keyisNone:continue# 获取订单金额并转换为数字hour_qian=float(order[total_key])# 确保金额是数字类型# 小时销售额累加逻辑ifhourinhour_sales:hour_sales[hour]+=hour_qianelse:hour_sales[hour]=hour_qian# 出错则跳到下一个订单exceptExceptionase:continue# 确保所有小时(00-23)都有数据,没有销售额的小时设置为0forhourinrange(24):hour_str=f"{hour:02d}"ifhour_strnotinhour_sales:hour_sales[hour_str]=0.0# 按小时排序并返回结果returndict(sorted(hour_sales.items()))#天数函数defday_sales(data):"""日统计表 参数: data:订单数据内容 返回值: weekly_sales: 包含每天销售额的字典,以周为一个范围 """weekdays=['周一','周二','周三','周四','周五','周六','周日']#初始化每周各天销售额为0weekly_sales={day:0fordayinweekdays}# 遍历订单获取数据fororderindata:try:time_key=Noneforkeyinorder:if'时间'inkey:time_key=keybreakiftime_keyisNone:continue# 解析订单时间并通过datetime库分析为周几order_time=datetime.datetime.strptime(order[time_key],"%Y-%m-%d %H:%M:%S")# 获取星期几的索引(0一6日)weekday_index=order_time.weekday()# 根据索引获取星期几的中文名称weekday_name=weekdays[weekday_index]total_key=Noneforkeyinorder:if'总金额'inkey:total_key=keybreakiftotal_keyisNone:continue# 获取订单金额为浮点数total_amount=float(order[total_key])# 累加每周各天总金额weekly_sales[weekday_name]+=total_amountexceptExceptionase:print("该订单出错,跳过处理下一单")continuereturnweekly_sales#画图函数defcreate_tubiao(data,title,x_label,y_label,horizontal=False):"""创建条形图 参数: data: 要显示的数据字典 title: 图表标题 x_label: X轴标签 y_label: Y轴标签 horizontal: 是否创建水平条形图,默认为False(垂直条形图) 返回值: matplotlib.figure.Figure: 创建的图表对象 """# 从数据字典中提取标签和数值labels=list(data.keys())amounts=list(data.values())# 根据是否为水平条形图和数据量调整图表大小,使其更紧凑ifhorizontalandlen(labels)>10:# 水平条形图且数据量较多时,增加高度以便完整显示fig,ax=plt.subplots(figsize=(10,8))else:# 默认图表大小fig,ax=plt.subplots(figsize=(8,5))# 创建条形图ifhorizontal:# 创建水平条形图bars=ax.barh(labels,amounts,color='lightblue')else:# 创建垂直条形图bars=ax.bar(labels,amounts,color='lightgreen')# 设置图表标题和坐标轴标签ax.set_title(title,fontsize=14)ax.set_xlabel(x_label,fontsize=12)ax.set_ylabel(y_label,fontsize=12)# 在条形图上显示数值forbarinbars:ifhorizontal:# 水平条形图,数值显示在条形右侧width=bar.get_width()ax.text(width+1,bar.get_y()+bar.get_height()/2,f'{width:.2f}',va='center',fontsize=10)else:# 垂直条形图,数值显示在条形顶部height=bar.get_height()ax.text(bar.get_x()+bar.get_width()/2,height+1,f'{height:.2f}',ha='center',fontsize=10)# 添加网格线ax.grid(axis='x'ifhorizontalelse'y',linestyle='--',alpha=0.7)plt.tight_layout()# 自动调整图表布局returnfig#数据窗口defshuju_gui(parent=None):""" 参数:parent:父窗口,如果为None则创建一个新的根窗口 """ifparentisNone:root=t.Tk()else:root=t.Toplevel(parent)root.title('营业数据分析')root.geometry('1200x800')root.resizable(width=True,height=True)sales_data=du_qu_shuju()# 获取所有可用日期all_dates=set()fororderinsales_data:try:# 找到时间字段time_key=Noneforkeyinorder:if'时间'inkeyor'时'inkey:time_key=keybreak# 如果有时间字段,提取日期iftime_key:order_time=datetime.datetime.strptime(order[time_key],"%Y-%m-%d %H:%M:%S")all_dates.add(order_time.strftime("%Y-%m-%d"))except:# 如果处理出错,跳过此订单continue# 倒序排序,方便查看近期数据daoxun_dates=sorted(list(all_dates),reverse=True)# 选择最新的日期作为默认值mongren_date=daoxun_dates[0]ifdaoxun_dateselse""control_frame=ttk.LabelFrame(root,text="数据选择",padding="10")control_frame.pack(fill=t.X,padx=10,pady=10)ttk.Label(control_frame,text="查看方式:").pack(side=t.LEFT,padx=10,pady=10)view_type=t.StringVar(value="按天")defupdate_tubiao():"""更新图表"""ifview_type.get()=="按天":# 按天查看 - 显示每小时销售额date=date_combo.get()ifnotdate:info.showinfo("提示","请选择日期!")return# 统计该日期的每小时销售额hourly_sales=hour_sales(sales_data,date)title=f"{date}每小时销售额"# 创建水平条形图fig=create_tubiao(hourly_sales,title,"销售额(元)","小时",horizontal=True)# 计算并更新统计信息total=sum(hourly_sales.values())# 总销售额max_hour=max(hourly_sales,key=hourly_sales.get)# 销售额最高的小时max_sales=hourly_sales[max_hour]# 最高销售额else:# 按星期查看 - 显示每周各天销售额# 统计每周各天的销售额weekly_sales=day_sales(sales_data)title="每周各天销售额统计"# 创建垂直条形图fig=create_tubiao(weekly_sales,title,"星期","销售额(元)",horizontal=False)# 计算并更新统计信息total=sum(weekly_sales.values())# 总销售额max_day=max(weekly_sales,key=weekly_sales.get)# 销售额最高的星期max_sales=weekly_sales[max_day]# 最高销售额# 更新图表# 首先清除旧的图表显示forwidgetinchart_frame.winfo_children():widget.destroy()# 创建新的canvas并显示canvas=FigureCanvasTkAgg(fig,master=chart_frame)canvas.draw()canvas.get_tk_widget().pack(fill=t.BOTH,expand=True,padx=10,pady=10)# 更新统计信息stats_label.config(text=f"总销售额:{total:.2f}元 | 最高:{max_sales:.2f}元")# 按天查看按钮look_day_btn=ttk.Radiobutton(control_frame,text="按天",variable=view_type,value="按天",command=update_tubiao)look_day_btn.pack(side=t.LEFT,padx=5)# 按星期查看按钮look_week_btn=ttk.Radiobutton(control_frame,text="按星期",variable=view_type,value="按星期",command=update_tubiao)look_week_btn.pack(side=t.LEFT,padx=5)# 日期选择下拉框ttk.Label(control_frame,text="日期:").pack(side=t.LEFT,padx=5)date_combo=ttk.Combobox(control_frame,values=daoxun_dates,state="readonly",width=12)date_combo.pack(side=t.LEFT,padx=5)# 设置默认选中最新的日期ifdaoxun_dates:date_combo.set(daoxun_dates[0])# 刷新按钮refresh_btn=ttk.Button(control_frame,text="刷新",command=update_tubiao)refresh_btn.pack(side=t.RIGHT,padx=10)# 图表显示区域chart_frame=ttk.Frame(root)chart_frame.pack(fill=t.BOTH,expand=True,padx=10,pady=10)# 创建初始图表ifdaoxun_dates:# 有数据时,显示最新日期的每小时销售额hourly_sales=hour_sales(sales_data,daoxun_dates[0])title=f"{daoxun_dates[0]}每小时销售额"fig=create_tubiao(hourly_sales,title,"销售额(元)","小时",horizontal=True)total=sum(hourly_sales.values())max_hour=max(hourly_sales,key=hourly_sales.get)max_sales=hourly_sales[max_hour]else:# 无数据时,显示提示信息fig,ax=plt.subplots(figsize=(10,6))ax.set_title("暂无数据",fontsize=16)total=0max_sales=0# 嵌入图表到界面canvas=FigureCanvasTkAgg(fig,master=chart_frame)canvas.draw()canvas.get_tk_widget().pack(fill=t.BOTH,expand=True,padx=10,pady=10)# 统计信息显示stats_label=ttk.Label(root,text=f"总销售额:{total:.2f}元 | 最高:{max_sales:.2f}元",font=("Arial",12))stats_label.pack(fill=t.X,padx=10,pady=10)# 增加底部边距,使界面更美观# 启动主循环root.mainloop()if__name__=="__main__":shuju_gui()

5. 数据文件示例

菜单.json

JSON

[{"id":1,"name":"宫保鸡丁","price":28,"category":"热菜"},{"id":2,"name":"鱼香肉丝","price":25,"category":"热菜"},// ... 共19道菜]
用户信息.json(默认管理员账号)

JSON

[{"账号":"1","密码":"123","角色":"管理者"},{"账号":"123","密码":"456","角色":"管理者"}]
营业数据.json(订单记录示例)

JSON

[{"订单编号":"20251212001","时间":"2025-12-12 10:30:00","桌号":"1","具体内容":[...],"总金额":53.0}// ... 更多订单]

运行方式:直接双击 main.py 即可启动。首次运行会自动创建默认数据文件。


总结

这个项目虽然不大,但几乎涵盖了 Tkinter 开发的所有核心知识点:

  • 窗口与控件布局
  • 事件绑定(双击、按钮)
  • 模块间传参与跳转
  • JSON 文件读写持久化
  • Matplotlib 嵌入 GUI 实现动态图表
  • 面向对象封装(Dish、Ladu 类)

通过这个项目,我对 Python 桌面应用开发有了更深的理解,也体会到模块化设计和异常处理的重要性。

项目还有很多可扩展空间,比如:

  • 完善管理员菜品增删改界面
  • 加入打印小票功能
  • 引入 SQLite 数据库
  • 开发网络版支持多台平板同时点餐
  • 引用Tkinter的ttk进行美化

喜欢的话欢迎 Star 或 Fork,后续有更新会继续分享!

完整源码下载:直接复制以上所有文件到同一文件夹,即可运行。 如果有任何问题,欢迎在评论区交流~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/9 15:17:37

《快来!AI原生应用与联邦学习的联邦零样本学习探索》

快来&#xff01;AI原生应用与联邦学习的联邦零样本学习探索 一、引入&#xff1a;当AI遇到“看不见的新问题”&#xff0c;该怎么办&#xff1f; 深夜11点&#xff0c;小张刷着电商APP&#xff0c;突然看到一款“智能宠物喂食器”——它能根据宠物体重自动调整食量&#xff0c…

作者头像 李华
网站建设 2026/4/1 10:53:51

8、无限图上的量子行走:深入解析与实践探索

无限图上的量子行走:深入解析与实践探索 1. 量子行走基础 量子行走的相关空间为 $H_M \otimes H_P$,其计算基为 ${|s, n\rangle, s \in {0, 1}, -\infty \leq n \leq \infty}$,这里规定 $s = 0$ 表示向右,$s = 1$ 表示向左。基于此,移位算子 $S$ 定义为: [S = \sum_{s…

作者头像 李华
网站建设 2026/4/7 15:58:57

9、量子行走:无限图与有限图的探索

量子行走:无限图与有限图的探索 无限图上的二维晶格量子行走 在无限图的二维晶格中,量子行走的研究涉及到不同类型的硬币操作,包括哈达玛硬币、傅里叶硬币和格罗弗硬币。这些硬币操作会影响量子行走的概率分布和标准偏差。 哈达玛硬币 哈达玛硬币的矩阵表示为: [ C =…

作者头像 李华
网站建设 2026/4/13 1:29:42

11、超立方体上的量子行走:理论与分析

超立方体上的量子行走:理论与分析 1. 傅里叶变换 傅里叶变换作用于计算基的方式如下: [ |\vec{E} k\rangle = \frac{1}{\sqrt{2^n}} \sum {\vec{E} v = 0}^{2^n - 1} (-1)^{\vec{E}_k \cdot \vec{E}_v} |\vec{E}_v\rangle ] 其中,(\vec{E}_k \cdot \vec{E}_v) 是二…

作者头像 李华
网站建设 2026/4/11 11:36:23

13、量子行走中的极限分布与混合时间

量子行走中的极限分布与混合时间 1. 引言 在量子行走的研究中,极限分布和混合时间是非常重要的概念。极限分布描述了量子行走在长时间演化后所处状态的概率分布,而混合时间则衡量了量子行走达到这种极限分布的速度。本文将详细探讨在不同图结构(如循环图、超立方体和有限晶…

作者头像 李华
网站建设 2026/4/14 11:23:34

14、量子漫步中的极限分布、混合时间与空间搜索算法

量子漫步中的极限分布、混合时间与空间搜索算法 1. 极限分布与混合时间 1.1 极限分布相关公式推导 在研究量子漫步时,我们会遇到一些关键的公式推导。首先,通过特定的条件(对应项消失),利用相关公式(7.11)和(7.46),可以得到如下公式: [D(\bar{p}(t), \pi) = \fr…

作者头像 李华