当前位置: 首页 > news >正文

基于Python与Tkinter的校园点餐系统设计与实现

import tkinter as tk
from tkinter import messagebox, ttk
import os
import json
from datetime import datetime# --- 数据存储文件 ---
USERS_FILE = "users.txt"
MENU_FILE = "menu.txt"
ORDERS_FILE = "orders.txt"# --- 初始化数据文件 ---
def init_data_files():if not os.path.exists(USERS_FILE):with open(USERS_FILE, 'w') as f:# 初始管理员账户f.write("admin,admin123,admin\n") if not os.path.exists(MENU_FILE):with open(MENU_FILE, 'w') as f:# 示例菜单数据menu_data = [{"id": 1, "name": "宫保鸡丁", "price": 18.0, "category": "主食"},{"id": 2, "name": "鱼香肉丝", "price": 16.0, "category": "主食"},{"id": 3, "name": "麻婆豆腐", "price": 12.0, "category": "主食"},{"id": 4, "name": "西红柿鸡蛋", "price": 10.0, "category": "主食"},{"id": 5, "name": "可乐", "price": 3.0, "category": "饮料"},{"id": 6, "name": "雪碧", "price": 3.0, "category": "饮料"},{"id": 7, "name": "矿泉水", "price": 2.0, "category": "饮料"},{"id": 8, "name": "米饭", "price": 1.5, "category": "主食"},]json.dump(menu_data, f)if not os.path.exists(ORDERS_FILE):with open(ORDERS_FILE, 'w') as f:json.dump([], f) # 初始化为空列表# --- 用户认证模块 ---
class UserAuth:@staticmethoddef load_users():users = {}try:with open(USERS_FILE, 'r') as f:for line in f:username, password, role = line.strip().split(',')users[username] = {'password': password, 'role': role}except FileNotFoundError:pass # 文件不存在时,返回空字典return users@staticmethoddef save_user(username, password, role='user'):with open(USERS_FILE, 'a') as f:f.write(f"{username},{password},{role}\n")@staticmethoddef authenticate(username, password):users = UserAuth.load_users()user = users.get(username)if user and user['password'] == password:return True, user['role']return False, None@staticmethoddef register(username, password):users = UserAuth.load_users()if username in users:return False, "用户名已存在"UserAuth.save_user(username, password)return True, "注册成功"# --- 菜单管理模块 ---
class MenuManager:@staticmethoddef load_menu():try:with open(MENU_FILE, 'r') as f:return json.load(f)except (FileNotFoundError, json.JSONDecodeError):return []@staticmethoddef get_menu_by_category():menu = MenuManager.load_menu()categorized_menu = {}for item in menu:cat = item['category']if cat not in categorized_menu:categorized_menu[cat] = []categorized_menu[cat].append(item)return categorized_menu# --- 订单管理模块 ---
class OrderManager:@staticmethoddef load_orders():try:with open(ORDERS_FILE, 'r') as f:return json.load(f)except (FileNotFoundError, json.JSONDecodeError):return []@staticmethoddef save_orders(orders):with open(ORDERS_FILE, 'w') as f:json.dump(orders, f)@staticmethoddef place_order(username, cart_items, total_price):orders = OrderManager.load_orders()order = {'id': len(orders) + 1,'username': username,'items': cart_items,'total': total_price,'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")}orders.append(order)OrderManager.save_orders(orders)return order['id']# --- 主应用类 ---
class CampusCanteenApp:def __init__(self, root):self.root = rootself.root.title("校园点餐系统")self.current_user = Noneself.cart = {} # {item_id: quantity}# 初始化数据文件init_data_files()# 创建主框架self.main_frame = tk.Frame(root)self.main_frame.pack(fill=tk.BOTH, expand=True)# 显示登录/注册界面self.show_login_screen()def show_login_screen(self):self.clear_frame()tk.Label(self.main_frame, text="校园点餐系统", font=("Arial", 18)).pack(pady=20)tk.Label(self.main_frame, text="用户名:").pack()self.username_entry = tk.Entry(self.main_frame)self.username_entry.pack()tk.Label(self.main_frame, text="密码:").pack()self.password_entry = tk.Entry(self.main_frame, show="*")self.password_entry.pack()tk.Button(self.main_frame, text="登录", command=self.login).pack(pady=10)tk.Button(self.main_frame, text="注册", command=self.show_register_screen).pack()def show_register_screen(self):self.clear_frame()tk.Label(self.main_frame, text="用户注册", font=("Arial", 16)).pack(pady=20)tk.Label(self.main_frame, text="新用户名:").pack()self.new_username_entry = tk.Entry(self.main_frame)self.new_username_entry.pack()tk.Label(self.main_frame, text="新密码:").pack()self.new_password_entry = tk.Entry(self.main_frame, show="*")self.new_password_entry.pack()tk.Button(self.main_frame, text="注册", command=self.register).pack(pady=10)tk.Button(self.main_frame, text="返回登录", command=self.show_login_screen).pack()def show_main_menu(self):self.clear_frame()welcome_text = f"欢迎, {self.current_user}!"tk.Label(self.main_frame, text=welcome_text, font=("Arial", 14)).pack(anchor=tk.W, padx=10, pady=5)# 创建按钮框架button_frame = tk.Frame(self.main_frame)button_frame.pack(pady=10)tk.Button(button_frame, text="浏览菜单", command=self.show_menu).pack(side=tk.LEFT, padx=5)tk.Button(button_frame, text="查看购物车", command=self.show_cart).pack(side=tk.LEFT, padx=5)tk.Button(button_frame, text="查看订单历史", command=self.show_order_history).pack(side=tk.LEFT, padx=5)tk.Button(button_frame, text="退出登录", command=self.logout).pack(side=tk.LEFT, padx=5)def show_menu(self):self.clear_frame()tk.Button(self.main_frame, text="返回主菜单", command=self.show_main_menu).pack(anchor=tk.NW, padx=5, pady=5)# 创建一个Canvas和Scrollbar用于滚动canvas = tk.Canvas(self.main_frame)scrollbar = ttk.Scrollbar(self.main_frame, orient="vertical", command=canvas.yview)scrollable_frame = ttk.Frame(canvas)scrollable_frame.bind("<Configure>",lambda e: canvas.configure(scrollregion=canvas.bbox("all")))canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")canvas.configure(yscrollcommand=scrollbar.set)# 加载并显示菜单categorized_menu = MenuManager.get_menu_by_category()for category, items in categorized_menu.items():tk.Label(scrollable_frame, text=category, font=("Arial", 12, "bold")).pack(anchor=tk.W, padx=10, pady=(10, 0))for item in items:item_frame = tk.Frame(scrollable_frame, relief=tk.RAISED, borderwidth=1)item_frame.pack(fill=tk.X, padx=10, pady=2)tk.Label(item_frame, text=f"{item['name']} - ¥{item['price']:.2f}", width=30, anchor=tk.W).pack(side=tk.LEFT)tk.Button(item_frame, text="加入购物车", command=lambda i=item: self.add_to_cart(i)).pack(side=tk.RIGHT, padx=5)canvas.pack(side="left", fill="both", expand=True)scrollbar.pack(side="right", fill="y")def show_cart(self):self.clear_frame()tk.Button(self.main_frame, text="返回主菜单", command=self.show_main_menu).pack(anchor=tk.NW, padx=5, pady=5)if not self.cart:tk.Label(self.main_frame, text="购物车是空的").pack(pady=20)return# 购物车列表cart_frame = tk.Frame(self.main_frame)cart_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)self.cart_tree = ttk.Treeview(cart_frame, columns=("名称", "单价", "数量", "小计"), show="headings")self.cart_tree.heading("名称", text="名称")self.cart_tree.heading("单价", text="单价")self.cart_tree.heading("数量", text="数量")self.cart_tree.heading("小计", text="小计")self.cart_tree.column("名称", width=150)self.cart_tree.column("单价", width=80)self.cart_tree.column("数量", width=80)self.cart_tree.column("小计", width=80)menu = MenuManager.load_menu()menu_dict = {item['id']: item for item in menu}total_price = 0for item_id, quantity in self.cart.items():item = menu_dict.get(item_id)if item:subtotal = item['price'] * quantitytotal_price += subtotalself.cart_tree.insert("", tk.END, values=(item['name'], f"¥{item['price']:.2f}", quantity, f"¥{subtotal:.2f}"))self.cart_tree.pack(fill=tk.BOTH, expand=True)# 总价和操作按钮total_frame = tk.Frame(self.main_frame)total_frame.pack(pady=10)tk.Label(total_frame, text=f"总计: ¥{total_price:.2f}", font=("Arial", 12, "bold")).pack(side=tk.LEFT, padx=10)tk.Button(total_frame, text="清空购物车", command=self.clear_cart).pack(side=tk.LEFT, padx=5)tk.Button(total_frame, text="下单", command=lambda: self.place_order(total_price)).pack(side=tk.LEFT, padx=5)def show_order_history(self):self.clear_frame()tk.Button(self.main_frame, text="返回主菜单", command=self.show_main_menu).pack(anchor=tk.NW, padx=5, pady=5)orders = OrderManager.load_orders()user_orders = [order for order in orders if order['username'] == self.current_user]if not user_orders:tk.Label(self.main_frame, text="暂无订单记录").pack(pady=20)return# 订单历史列表history_frame = tk.Frame(self.main_frame)history_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)self.history_tree = ttk.Treeview(history_frame, columns=("订单号", "时间", "总价"), show="headings")self.history_tree.heading("订单号", text="订单号")self.history_tree.heading("时间", text="下单时间")self.history_tree.heading("总价", text="总价")self.history_tree.column("订单号", width=80)self.history_tree.column("时间", width=150)self.history_tree.column("总价", width=80)for order in user_orders:self.history_tree.insert("", tk.END, values=(order['id'], order['timestamp'], f"¥{order['total']:.2f}"), tags=(order['id'],))self.history_tree.bind("<Double-1>", self.show_order_detail) # 双击查看详情self.history_tree.pack(fill=tk.BOTH, expand=True)def show_order_detail(self, event):item = self.history_tree.selection()[0]order_id = self.history_tree.item(item, "tags")[0]orders = OrderManager.load_orders()order = next((o for o in orders if o['id'] == int(order_id)), None)if not order:returndetail_window = tk.Toplevel(self.root)detail_window.title(f"订单详情 - {order_id}")tk.Label(detail_window, text=f"订单号: {order['id']}").pack(anchor=tk.W, padx=10, pady=2)tk.Label(detail_window, text=f"下单时间: {order['timestamp']}").pack(anchor=tk.W, padx=10, pady=2)tk.Label(detail_window, text=f"总价: ¥{order['total']:.2f}").pack(anchor=tk.W, padx=10, pady=2)tk.Label(detail_window, text="菜品:").pack(anchor=tk.W, padx=10, pady=(10, 2))menu = MenuManager.load_menu()menu_dict = {item['id']: item for item in menu}for item_id, quantity in order['items'].items():item = menu_dict.get(int(item_id))if item:tk.Label(detail_window, text=f"  - {item['name']} x {quantity} = ¥{item['price'] * quantity:.2f}").pack(anchor=tk.W, padx=20)def clear_frame(self):for item in self.main_frame.winfo_children():item.destroy()def login(self):username = self.username_entry.get()password = self.password_entry.get()success, role = UserAuth.authenticate(username, password)if success:self.current_user = usernamemessagebox.showinfo("成功", f"欢迎回来, {username}!")self.show_main_menu()else:messagebox.showerror("错误", "用户名或密码错误")def register(self):username = self.new_username_entry.get()password = self.new_password_entry.get()if not username or not password:messagebox.showerror("错误", "用户名和密码不能为空")returnsuccess, message = UserAuth.register(username, password)if success:messagebox.showinfo("成功", message)self.show_login_screen()else:messagebox.showerror("错误", message)def logout(self):self.current_user = Noneself.cart = {}messagebox.showinfo("注销", "您已成功注销")self.show_login_screen()def add_to_cart(self, item):item_id = item['id']self.cart[item_id] = self.cart.get(item_id, 0) + 1messagebox.showinfo("成功", f"{item['name']} 已添加到购物车")def clear_cart(self):self.cart = {}self.show_cart() # 刷新购物车界面def place_order(self, total_price):if not self.cart:messagebox.showwarning("警告", "购物车为空")returnorder_id = OrderManager.place_order(self.current_user, self.cart, total_price)self.cart = {} # 清空购物车messagebox.showinfo("成功", f"订单已提交,订单号: {order_id}")self.show_main_menu() # 返回主菜单# --- 程序入口 ---
if __name__ == "__main__":root = tk.Tk()app = CampusCanteenApp(root)# 设置窗口大小root.geometry("600x500")root.mainloop()

摘要:

随着信息技术的发展和移动互联网的普及,传统的校园餐饮服务模式已难以满足师生日益增长的便捷性、个性化需求。本文设计并实现了一个基于Python编程语言和Tkinter图形用户界面库的校园点餐系统。该系统旨在简化点餐流程,提高食堂运营效率,并为用户提供便捷的线上点餐体验。本文详细阐述了系统的开发背景、需求分析、架构设计、核心功能模块实现以及系统测试。系统采用模块化设计,主要包括用户认证、菜单管理、购物车和订单处理等模块,并使用文本文件模拟数据存储。通过功能测试,验证了系统各模块的基本功能,证明了该系统方案的可行性。

关键词: 校园点餐系统;Python;Tkinter;软件设计;信息管理系统

1. 引言

校园食堂作为师生日常生活的重要组成部分,其服务效率和质量直接影响着校园生活的便利性和满意度。传统的排队点餐方式存在等待时间长、高峰期拥堵、菜单信息不透明等问题。为了解决这些问题,提升用户体验和食堂管理效率,开发一个便捷、高效的校园点餐系统显得尤为必要。

近年来,国内外已有不少关于在线点餐系统的研究与实践。这些系统多采用Web或移动应用的形式,后端依赖于数据库和服务器。然而,对于一些规模较小或希望快速部署试点的校园场景,一个轻量级的、易于部署和维护的桌面应用程序同样具有价值。Python语言以其简洁易读、丰富的库支持和跨平台特性,成为快速开发此类应用的理想选择。Tkinter作为Python的标准GUI库,无需额外安装,即可构建基本的图形用户界面。

因此,本文提出并实现了一个基于Python和Tkinter的校园点餐系统。该系统专注于核心功能的实现,采用简单的文件存储机制,降低了部署和运行的门槛,适用于教学演示或小规模的实际应用。

2. 系统需求分析

2.1 功能性需求

用户管理:
用户注册:新用户能够创建账户。
用户登录:已注册用户能够使用账户凭证登录系统。
用户注销:登录用户能够安全退出系统。
菜单浏览:
菜品展示:系统应能清晰地展示所有可点菜品。
分类显示:菜品应按类别(如主食、饮料等)进行组织和展示。
购物车管理:
添加菜品:用户能够将感兴趣的菜品添加到购物车。
查看购物车:用户能够查看购物车中的菜品列表及数量。
计算总价:系统能够根据购物车中的菜品及单价自动计算总金额。
清空购物车:用户能够清空购物车中的所有内容。
订单处理:
提交订单:用户能够将购物车中的菜品生成订单并提交。
订单历史:用户能够查看自己历史提交的订单记录。
订单详情:用户能够查看历史订单的详细信息(包含菜品列表、数量、总价、下单时间等)。

2.2 非功能性需求

易用性:界面简洁直观,操作逻辑清晰,易于上手。
稳定性:系统应能稳定运行,避免频繁崩溃。
安全性:(基础)对用户账户信息进行基本的保护,防止未授权访问。(注:本原型在密码存储上未采用加密,实际应用需加强)
可扩展性:系统设计应具备一定的模块化,便于未来功能的增加和修改。

3. 系统设计

3.1 系统架构

本系统采用经典的客户端-数据存储架构。客户端使用Python的Tkinter库构建图形用户界面,负责与用户交互。数据存储层采用简单的文本文件(`.txt`)来模拟数据库,分别存储用户信息、菜单信息和订单信息。这种架构简化了部署,无需配置复杂的数据库服务器。

表示层 (Presentation Layer)**:由Tkinter GUI组件构成,负责展示信息和接收用户输入。
业务逻辑层 (Business Logic Layer)**:由Python类和函数实现,处理用户请求、执行业务规则(如登录验证、订单生成)。
数据访问层 (Data Access Layer)**:封装了对文本文件的读写操作,为业务逻辑层提供数据接口。

3.2 功能模块设计

系统主要划分为以下几个核心模块:

用户认证模块 (UserAuth):负责处理用户注册、登录、注销等与账户相关的操作。它读取和写入`users.txt`文件。
菜单管理模块 (MenuManager):负责加载和提供菜单数据。它从`menu.txt`文件读取菜品信息,并按类别组织。
订单管理模块 (OrderManager):负责处理订单的创建、存储和查询。它读取和写入`orders.txt`文件。
主应用模块 (CampusCanteenApp):作为系统的中枢,继承自`tk.Tk`,负责初始化GUI、管理不同界面(登录、注册、主菜单、菜单浏览、购物车、订单历史)的切换,并协调各功能模块的工作。

3.3 数据结构设计

用户数据 (`users.txt`):每行存储一个用户信息,格式为 `用户名,密码,角色`。例如:`admin,admin123,admin`。
菜单数据 (`menu.txt`):采用JSON格式存储一个包含所有菜品字典的列表。每个菜品字典包含 `id`, `name`, `price`, `category` 键。
订单数据 (`orders.txt`):采用JSON格式存储一个包含所有订单字典的列表。每个订单字典包含 `id`, `username`, `items` (一个字典,键为菜品ID,值为数量), `total`, `timestamp` 键。

4. 系统实现

系统使用Python 3.x开发,核心库为`tkinter`。以下是关键部分的实现细节:

*   **初始化 (`init_data_files`):程序启动时检查必要的数据文件是否存在,若不存在则创建并写入初始数据(如管理员账户、示例菜单)。
*   **用户认证 (`UserAuth`):
*   `load_users()`: 读取`users.txt`,构建用户字典。
*   `authenticate(username, password)`: 验证用户凭据。
*   `register(username, password)`: 检查用户名唯一性后,将新用户信息追加到`users.txt`。
*   **菜单管理 (`MenuManager`):
*   `load_menu()`: 读取`menu.txt`,加载菜单列表。
*   `get_menu_by_category()`: 将菜单列表按`category`键重新组织为字典,方便GUI展示。
*   **订单管理 (`OrderManager`):
*   `load_orders()`: 读取`orders.txt`,加载订单列表。
*   `place_order(username, cart_items, total_price)`: 构造新订单对象,追加到订单列表,并写回`orders.txt`。
*   **主应用 (`CampusCanteenApp`):
*   **界面切换**:通过`clear_frame()`方法清空主窗口内容,然后调用不同方法(如`show_login_screen()`, `show_main_menu()`)来绘制新的界面。
*   **事件处理**:为按钮等控件绑定命令函数(如`command=self.login`),当用户操作时触发相应逻辑。
*   **购物车 (`self.cart`):使用一个字典来存储,键为菜品ID,值为数量。
*   **滚动菜单**:在菜单浏览界面,使用`tk.Canvas`和`ttk.Scrollbar`实现菜品列表的垂直滚动,以适应较长的菜单。

5. 系统测试

对系统的主要功能进行了手动测试,测试用例及结果如下:

  • 用户注册与登录
    • 测试用例1:使用新用户名和密码注册,成功。
    • 测试用例2:使用已存在的用户名注册,提示“用户名已存在”。
    • 测试用例3:使用正确的用户名和密码登录,成功进入主菜单。
    • 测试用例4:使用错误的用户名或密码登录,提示“用户名或密码错误”。
  • 菜单浏览:成功加载并按类别显示了menu.txt中的菜品。
  • 购物车操作
    • 测试用例1:点击“加入购物车”按钮,菜品成功添加,购物车数量增加。
    • 测试用例2:多次添加同一菜品,数量正确累加。
    • 测试用例3:查看购物车,列表和总价计算正确。
    • 测试用例4:点击“清空购物车”,购物车列表变空。
  • 下单与订单历史
    • 测试用例1:购物车非空时点击“下单”,生成新订单,购物车清空,提示订单号。
    • 测试用例2:购物车为空时点击“下单”,提示“购物车为空”。
    • 测试用例3:查看订单历史,能列出当前用户的所有订单。
    • 测试用例4:双击订单历史中的订单,能弹出窗口显示订单详细信息。

测试结果表明,系统实现了预期的主要功能,基本满足了设计要求。

6. 总结与展望

本文成功设计并实现了一个基于Python和Tkinter的校园点餐系统原型。该系统结构清晰,功能完备,能够满足基本的在线点餐需求。通过使用文本文件作为数据存储,降低了系统部署的复杂度。

然而,本系统作为一个原型,仍存在一些不足之处:

  • 数据存储:使用文本文件存储数据,安全性、并发性和性能较低。未来应采用SQLite或MySQL等数据库。
  • 用户安全:密码以明文形式存储,存在严重安全隐患。应采用哈希算法(如bcrypt)对密码进行加密存储。
  • 界面美观:Tkinter界面相对简陋。可以考虑使用更现代的GUI库(如PyQt, Kivy)或开发Web前端。
  • 功能扩展:缺少菜品搜索、库存管理、支付接口集成、管理员后台等功能。

未来的工作将集中在上述方面的改进,以及增加用户反馈机制、优化用户体验,使其成为一个更成熟、更安全、功能更丰富的校园餐饮服务平台。

http://www.dtcms.com/a/347739.html

相关文章:

  • Spring Data Redis基础
  • [Vid-LLM] docs | 视频理解任务
  • Windows应急响应一般思路(三)
  • 第1.2节:早期AI发展(1950-1980)
  • 老字号:用 “老根” 熬活的 “新味道”
  • redis---string类型详解
  • 大模型四种常见安全问题与攻击案例
  • mysql的mvcc
  • 大语言模型应用开发——利用OpenAI函数与LangChain结合从文本构建知识图谱搭建RAG应用全流程
  • Redis全面详解:从配置入门到实战应用
  • 【前端debug调试】
  • 【Java SE】抽象类、接口与Object类
  • 从“一指禅”到盲打:如何系统提升电脑输入能力?
  • 25.深入对象
  • 联邦学习之----联邦批量归一化(FedBN)
  • 线程间Bug检测工具Canary
  • Python字符串
  • SOC估算方法-蜣螂优化算法结合极限学习
  • 1200 SCL学习笔记
  • 机器人控制基础:串级PID控制算法的参数如何整定?
  • 11.Shell脚本修炼手册---IF 条件语句的知识与实践
  • 无线数传模块保障智能立体车库多设备实时通信的可靠性
  • 二、BPMNJS简介
  • share logic in core or in example
  • 【typenum】 23 倒序存储的无符号整数(private.rs片段)
  • Linux mount 命令
  • PyInstaller将.py文件转为exe,执行文件在不同的电脑出现字体大小不一致问题原因分析及解决办法
  • Spring:IOC(控制反转 )、DI(依赖注入 )、AOP(通知类型、事务、拦截器)
  • 主流.NET 平台的NuGet 生态正在积极拥抱 AOT
  • 【84页PPT】智慧方案某著名企业某集团协同OA整体解决方案(附下载方式)