一、项目简介
1.1 项目说明
本项目使用 Python + Tkinter 开发一款桌面版电商购物系统,不需要连接数据库,全部数据内存模拟实现。实现用户登录、商品浏览、加入购物车、购物车管理、结算下单、退出登录完整电商流程。适合 Python 课程设计、大作业、毕业设计练手。
技术栈:
Python 3.x
Tkinter(GUI 图形界面)
ttk 组件美化界面
decimal 高精度金额计算(避免浮点数精度 bug)
内存模拟用户库、商品库,无需 MySQL 数据库
1.2 实现功能清单
用户登录验证,内置两套测试账号
登录窗口居中,密码隐藏输入
商品列表表格展示:ID、商品名称、价格、分类、商品描述
选择商品,自定义购买数量加入购物车
独立购物车子窗口:展示商品、单价、数量、小计金额
清空购物车功能
结算下单,订单确认,下单成功清空购物车
退出登录,返回登录界面
窗口居中弹窗提示,消息框交互
使用 decimal 处理金额,解决浮点运算误差
1.3 测试账号
| 用户名 | 密码 | 昵称 |
|---|---|---|
| admin | 123456 | 系统管理员 |
| user1 | 111111 | 普通用户 |
二、项目整体架构
项目采用面向对象编程,拆分多个类:
CartService:购物车业务逻辑服务类,纯粹业务,不操作 UI,实现添加、删除、清空、计算总价
LoginWindow:登录主窗口 (Tk)
MainWindow:系统主界面,商品展示页面 (Tk)
CartWindow:购物车弹窗 (Toplevel),子窗口
模拟内存数据库:USER_DB 用户字典,PRODUCTS商品列表
三、完整源代码
import tkinter as tk from tkinter import ttk, messagebox, simpledialog import decimal # ======================== 数据模型 ======================== # 模拟用户数据库 USER_DB = { "admin": {"password": "123456", "nickname": "系统管理员"}, "user1": {"password": "111111", "nickname": "普通用户"} } # 模拟商品数据库 PRODUCTS = [ {"id": 1, "name": "华为Mate70 Pro", "price": decimal.Decimal("6999.00"), "category": "手机", "desc": "鸿蒙系统,麒麟芯片"}, {"id": 2, "name": "苹果iPhone 16", "price": decimal.Decimal("7999.00"), "category": "手机", "desc": "A18芯片,iOS 18"}, {"id": 3, "name": "小米笔记本Pro 2025", "price": decimal.Decimal("5999.00"), "category": "电脑", "desc": "酷睿i7,16G内存"}, {"id": 4, "name": "华为平板MatePad Pro", "price": decimal.Decimal("2999.00"), "category": "平板", "desc": "12.6英寸,鸿蒙4.0"}, {"id": 5, "name": "AirPods Pro 2", "price": decimal.Decimal("1499.00"), "category": "耳机", "desc": "主动降噪,无线充电"} ] # ======================== 购物车服务类 ======================== class CartService: def __init__(self): self.cart_items = [] # 购物车项:[(product, quantity), ...] def add_to_cart(self, product, quantity): """添加商品到购物车(已存在则更新数量)""" if quantity <= 0: raise ValueError("购买数量必须大于0") # 检查商品是否已在购物车 for idx, (p, q) in enumerate(self.cart_items): if p["id"] == product["id"]: self.cart_items[idx] = (p, q + quantity) return # 新增商品到购物车 self.cart_items.append((product, quantity)) def remove_from_cart(self, product_id): """从购物车移除指定商品""" self.cart_items = [(p, q) for p, q in self.cart_items if p["id"] != product_id] def clear_cart(self): """清空购物车""" self.cart_items.clear() def get_cart_items(self): """获取购物车所有商品""" return self.cart_items.copy() def calculate_total(self): """计算购物车总金额""" total = decimal.Decimal("0.00") for product, quantity in self.cart_items: total += product["price"] * quantity return total def is_empty(self): """检查购物车是否为空""" return len(self.cart_items) == 0 # ======================== 登录窗口 ======================== class LoginWindow(tk.Tk): def __init__(self): super().__init__() self.title("电商系统 - 用户登录") self.geometry("450x350") self.resizable(False, False) self.center_window() # 窗口居中 # 初始化购物车服务 self.cart_service = CartService() # 创建UI self.create_ui() def center_window(self): """窗口居中显示""" self.update_idletasks() width = self.winfo_width() height = self.winfo_height() x = (self.winfo_screenwidth() // 2) - (width // 2) y = (self.winfo_screenheight() // 2) - (height // 2) self.geometry(f"{width}x{height}+{x}+{y}") def create_ui(self): """创建登录界面""" # 主面板 main_frame = ttk.Frame(self, padding="30") main_frame.pack(expand=True, fill=tk.BOTH) # 标题 title_label = ttk.Label(main_frame, text="Python电商购物系统", font=("微软雅黑", 20, "bold")) title_label.pack(pady=(0, 30)) # 用户名行 user_frame = ttk.Frame(main_frame) user_frame.pack(fill=tk.X, pady=10) ttk.Label(user_frame, text="用户名:", font=("微软雅黑", 12)).pack(side=tk.LEFT) self.username_var = tk.StringVar() username_entry = ttk.Entry(user_frame, textvariable=self.username_var, font=("微软雅黑", 12), width=25) username_entry.pack(side=tk.LEFT, padx=10) # 密码行 pwd_frame = ttk.Frame(main_frame) pwd_frame.pack(fill=tk.X, pady=10) ttk.Label(pwd_frame, text="密 码:", font=("微软雅黑", 12)).pack(side=tk.LEFT) self.pwd_var = tk.StringVar() pwd_entry = ttk.Entry(pwd_frame, textvariable=self.pwd_var, font=("微软雅黑", 12), width=25, show="*") pwd_entry.pack(side=tk.LEFT, padx=10) # 登录按钮 login_btn = ttk.Button(main_frame, text="登录", command=self.login) login_btn.pack(pady=20) # 提示信息 tip_label = ttk.Label(main_frame, text="测试账号:admin/123456 | user1/111111", font=("微软雅黑", 10)) tip_label.pack() def login(self): """处理登录逻辑""" username = self.username_var.get().strip() password = self.pwd_var.get().strip() # 验证输入 if not username: messagebox.showerror("错误", "请输入用户名!") return if not password: messagebox.showerror("错误", "请输入密码!") return # 验证用户 user = USER_DB.get(username) if not user or user["password"] != password: messagebox.showerror("错误", "用户名或密码错误!") self.pwd_var.set("") # 清空密码框 return # 登录成功,打开主窗口 messagebox.showinfo("成功", f"登录成功!欢迎您,{user['nickname']}!") self.destroy() # 关闭登录窗口 MainWindow(user, self.cart_service).mainloop() # ======================== 主窗口(商品展示) ======================== class MainWindow(tk.Tk): def __init__(self, user, cart_service): super().__init__() self.title(f"电商系统 - {user['nickname']}") self.geometry("900x600") self.resizable(True, True) self.center_window() # 初始化数据 self.current_user = user self.cart_service = cart_service self.cart_window = None # 购物车窗体引用 # 创建UI self.create_ui() def center_window(self): """窗口居中""" self.update_idletasks() width = self.winfo_width() height = self.winfo_height() x = (self.winfo_screenwidth() // 2) - (width // 2) y = (self.winfo_screenheight() // 2) - (height // 2) self.geometry(f"{width}x{height}+{x}+{y}") def create_ui(self): """创建主界面""" # 顶部工具栏 toolbar = ttk.Frame(self) toolbar.pack(fill=tk.X, padx=10, pady=10) # 购物车按钮 cart_btn = ttk.Button(toolbar, text="我的购物车", command=self.open_cart) cart_btn.pack(side=tk.RIGHT) # 退出按钮 exit_btn = ttk.Button(toolbar, text="退出登录", command=self.logout) exit_btn.pack(side=tk.RIGHT, padx=10) # 商品表格 product_frame = ttk.Frame(self) product_frame.pack(expand=True, fill=tk.BOTH, padx=10, pady=10) # 表格列名 columns = ("ID", "商品名称", "价格(元)", "分类", "描述") self.product_tree = ttk.Treeview(product_frame, columns=columns, show="headings", height=15) # 设置列标题和宽度 for col in columns: self.product_tree.heading(col, text=col) if col == "商品名称": self.product_tree.column(col, width=200) elif col == "描述": self.product_tree.column(col, width=250) else: self.product_tree.column(col, width=100) # 滚动条 scrollbar = ttk.Scrollbar(product_frame, orient=tk.VERTICAL, command=self.product_tree.yview) self.product_tree.configure(yscrollcommand=scrollbar.set) # 布局表格和滚动条 self.product_tree.pack(side=tk.LEFT, expand=True, fill=tk.BOTH) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 加载商品数据 self.load_products() # 底部操作区 op_frame = ttk.Frame(self) op_frame.pack(fill=tk.X, padx=10, pady=10) # 数量输入 ttk.Label(op_frame, text="购买数量:").pack(side=tk.LEFT) self.quantity_var = tk.StringVar(value="1") quantity_entry = ttk.Entry(op_frame, textvariable=self.quantity_var, width=10) quantity_entry.pack(side=tk.LEFT, padx=10) # 加入购物车按钮 add_cart_btn = ttk.Button(op_frame, text="加入购物车", command=self.add_to_cart) add_cart_btn.pack(side=tk.LEFT) def load_products(self): """加载商品数据到表格""" # 清空现有数据 for item in self.product_tree.get_children(): self.product_tree.delete(item) # 添加商品 for product in PRODUCTS: self.product_tree.insert("", tk.END, values=( product["id"], product["name"], f"{product['price']:.2f}", product["category"], product["desc"] )) def add_to_cart(self): """添加选中商品到购物车""" # 获取选中行 selected_items = self.product_tree.selection() if not selected_items: messagebox.showwarning("提示", "请先选择要购买的商品!") return # 获取数量 try: quantity = int(self.quantity_var.get().strip()) if quantity <= 0: messagebox.showerror("错误", "购买数量必须大于0!") return except ValueError: messagebox.showerror("错误", "请输入有效的数字!") return # 获取选中商品 selected_item = selected_items[0] product_id = int(self.product_tree.item(selected_item, "values")[0]) product = next(p for p in PRODUCTS if p["id"] == product_id) # 添加到购物车 try: self.cart_service.add_to_cart(product, quantity) messagebox.showinfo("成功", f"{product['name']} 已加入购物车!") except ValueError as e: messagebox.showerror("错误", str(e)) def open_cart(self): """打开购物车窗体""" if self.cart_window is None or not self.cart_window.winfo_exists(): self.cart_window = CartWindow(self, self.cart_service) self.cart_window.lift() # 置于顶层 def logout(self): """退出登录""" if messagebox.askyesno("确认", "确定要退出登录吗?"): self.destroy() LoginWindow().mainloop() # ======================== 购物车窗体 ======================== class CartWindow(tk.Toplevel): def __init__(self, parent, cart_service): super().__init__(parent) self.title("我的购物车") self.geometry("700x400") self.resizable(False, False) self.center_window() # 初始化数据 self.cart_service = cart_service # 创建UI self.create_ui() self.update_cart() def center_window(self): """窗口居中""" self.update_idletasks() width = self.winfo_width() height = self.winfo_height() x = (self.winfo_screenwidth() // 2) - (width // 2) y = (self.winfo_screenheight() // 2) - (height // 2) self.geometry(f"{width}x{height}+{x}+{y}") def create_ui(self): """创建购物车界面""" # 购物车表格 cart_frame = ttk.Frame(self, padding="20") cart_frame.pack(expand=True, fill=tk.BOTH) # 表格列名 columns = ("ID", "商品名称", "单价(元)", "数量", "小计(元)") self.cart_tree = ttk.Treeview(cart_frame, columns=columns, show="headings", height=10) # 设置列属性 for col in columns: self.cart_tree.heading(col, text=col) self.cart_tree.column(col, width=120 if col == "商品名称" else 80) # 滚动条 scrollbar = ttk.Scrollbar(cart_frame, orient=tk.VERTICAL, command=self.cart_tree.yview) self.cart_tree.configure(yscrollcommand=scrollbar.set) # 布局表格 self.cart_tree.pack(side=tk.LEFT, expand=True, fill=tk.BOTH) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 底部操作区 bottom_frame = ttk.Frame(self, padding="0 20 20 20") bottom_frame.pack(fill=tk.X, anchor=tk.E) # 总价显示 self.total_var = tk.StringVar(value="总计:¥0.00") total_label = ttk.Label(bottom_frame, textvariable=self.total_var, font=("微软雅黑", 12, "bold")) total_label.pack(side=tk.LEFT, padx=20) # 操作按钮 clear_btn = ttk.Button(bottom_frame, text="清空购物车", command=self.clear_cart) clear_btn.pack(side=tk.LEFT, padx=10) checkout_btn = ttk.Button(bottom_frame, text="结算下单", command=self.checkout) checkout_btn.pack(side=tk.LEFT) def update_cart(self): """更新购物车数据""" # 清空表格 for item in self.cart_tree.get_children(): self.cart_tree.delete(item) # 加载购物车数据 cart_items = self.cart_service.get_cart_items() for product, quantity in cart_items: subtotal = product["price"] * quantity self.cart_tree.insert("", tk.END, values=( product["id"], product["name"], f"{product['price']:.2f}", quantity, f"{subtotal:.2f}" )) # 更新总价 total = self.cart_service.calculate_total() self.total_var.set(f"总计:¥{total:.2f}") def clear_cart(self): """清空购物车""" if self.cart_service.is_empty(): messagebox.showinfo("提示", "购物车已为空!") return if messagebox.askyesno("确认", "确定要清空购物车吗?"): self.cart_service.clear_cart() self.update_cart() messagebox.showinfo("成功", "购物车已清空!") def checkout(self): """结算下单""" if self.cart_service.is_empty(): messagebox.showerror("错误", "购物车为空,无法结算!") return total = self.cart_service.calculate_total() if messagebox.askyesno("结算确认", f"确认下单?\n订单总金额:¥{total:.2f}"): messagebox.showinfo("下单成功", f"订单创建成功!\n支付金额:¥{total:.2f}\n感谢您的购买!") self.cart_service.clear_cart() self.update_cart() self.destroy() # 关闭购物车 # ======================== 程序入口 ======================== if __name__ == "__main__": # 设置tkinter字体(解决中文显示问题) try: tk.font.nametofont("TkDefaultFont").configure(family="微软雅黑", size=10) except: pass # 启动登录窗口 app = LoginWindow() app.mainloop()四、运行说明
4.1 运行环境
Python 版本:Python3.6 及以上
Tkinter:Python 自带库,无需 pip 安装第三方包
如果 Linux 运行报错:sudo apt install python3-tk
4.2 运行步骤
将全部代码复制保存为 e_shop.py
直接运行脚本
在登录界面输入测试账号密码登录
在商品列表选中商品,输入购买数量,点击加入购物车
点击我的购物车打开弹窗,可以查看、清空、结算订单
退出登录回到登录页面