ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python库存管理系统实战:SQLite数据库、Tkinter界面与exe打包

Python库存管理系统实战:SQLite数据库、Tkinter界面与exe打包 简介这是一份面向计算机相关专业在校学生与初学者的Python库存管理系统课程设计/毕业设计源码包含完整项目文件与前端资源可帮助快速理解并复现一个轻量级库存管理应用的后端逻辑、数据库设计与页面交互也适合作为课设作业或项目立项演示的基础。压缩包共26个文件大小仅1.35MB主要包含Python源码py、前端脚本js、样式表css、页面结构html、依赖清单requirements.txt、Docker部署配置Dockerfile、Caddyfile以及数据库备份bak等涵盖开发、运行与部署所需的核心内容。目前已有993人学习下载说明该资源在同类课设资料中具备一定参考价值。代码经过测试运行成功结构清晰易读读者可根据CONFIG.py调整配置在main.py与frontend目录中分别查看后端逻辑和页面交互并可基于现有模块进行二次开发扩展出入库管理、库存查询等功能适合从基础练习到毕业设计的多种场景。1. 基于Python的库存管理系统先跑通再谈优化拿到“课程设计-基于Python的库存管理系统源码.zip”这类压缩包第一步不是读代码而是先把它跑起来。库存管理系统的技术栈选型很固定Python做业务逻辑SQLite或MySQL做持久化Tkinter或PyQt做桌面界面pandas做数据分析和报表导出。为什么这套组合是课程设计和中小型项目的首选因为Python的开发效率高Tkinter无需额外安装第三方GUI库SQLite单文件存储天然适合课程设计这种“拷贝即运行”的交付场景。很多人在跑别人源码时卡在环境配置上——Python版本不对、缺第三方库、路径写死、数据库文件没生成。这篇博文按“环境准备 → 数据模型 → 核心功能 → 报表与预警 → 打包交付”的顺序讲清楚整个系统的落地路径每段代码都可以直接复用。2. 搭建运行环境与项目结构把解压后的源码变成可运行系统2.1 用 conda 隔离 Python 版本避免系统环境被污染课程设计源码通常基于 Python 3.8 到 3.11 开发如果本机装了多个 Python 版本直接用系统环境跑容易出现ImportError或ModuleNotFoundError。我习惯用 conda 创建独立环境conda create -n inventory python3.9 -y conda activate inventory pip install pandas openpyxl pyinstaller代码说明buyer是环境名python3.9固定解释器版本pandas用于数据分析和报表openpyxl是 pandas 导出 Excel 的引擎pyinstaller用于最终打包 exe。如果源码里用的是 PyQt5 而不是 Tkinter还需要pip install pyqt5——在requirements.txt里能看到依赖列表。2.2 识别项目目录结构找到入口文件解压 zip 后我一般先用tree命令看结构tree /f inventory_system典型结构如下inventory_system/ ├── main.py # 程序入口 ├── database/ │ ├── db_helper.py # SQLite 连接与建表逻辑 │ └── init_db.py # 初始化数据库生成初始数据 ├── ui/ │ ├── login_window.py # 登录窗口 │ ├── main_window.py # 主界面 │ └── product_manage.py # 商品管理模块 ├── utils/ │ └── excel_export.py # 导出 Excel 报表 ├── requirements.txt └── README.mdmain.py是入口通常在文件末尾有if __name__ __main__:的启动代码。第一次运行前先执行python database/init_db.py初始化数据库否则主程序会因找不到表而报sqlite3.OperationalError: no such table。2.3 最常见的启动错误与修复跑不起来的原因排名前三的是缺少第三方库、数据库未初始化、Python 版本不兼容。示例报错修复如下ModuleNotFoundError: No module named openpyxl修复pip install openpyxl如果是TypeError: int object is not iterable多半是 Python 2 迁移到 Python 3 的历史代码问题检查range(len(list))这类关键字。3. 数据库设计SQLite 建表、索引与库存数量约束3.1 SQLite 为什么适合课程设计级的库存系统SQLite 无需单独安装数据库服务数据保存在单个.db文件中备份就是复制粘贴。对比 MySQLSQLite 在单机并发写入场景下没有锁竞争问题对于并发量很低的课程设计足够。核心表设计如下CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY AUTOINCREMENT, sku TEXT UNIQUE NOT NULL, name TEXT NOT NULL, category TEXT, quantity INTEGER NOT NULL DEFAULT 0, price REAL NOT NULL DEFAULT 0.0, min_threshold INTEGER NOT NULL DEFAULT 10, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS stock_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, change_type TEXT NOT NULL, -- IN 入库, OUT 出库 change_quantity INTEGER NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (product_id) REFERENCES products(id) );参数说明sku设为 UNIQUE防止同一商品重复录入quantity用 INTEGER 而非 REAL避免浮点精度问题min_threshold是库存预警阈值低于这个数就在界面上高亮显示。stock_records表记录每次出入库操作用于审计回溯。3.2 用触发器保证库存不为负数业务上不允许出库数量超过当前库存但只靠应用层判断容易漏。我在数据库层加触发器CREATE TRIGGER prevent_negative_stock BEFORE UPDATE ON products FOR EACH ROW WHEN NEW.quantity 0 BEGIN SELECT RAISE(ABORT, 库存不能为负数); END;提示如果源码里没有触发器可以在init_db.py中手动执行这一段能有效防止 “出库后库存变负数” 的逻辑漏洞。3.3 建索引与初始化数据的 Python 实现db_helper.py的常见写法import sqlite3 from contextlib import closing DB_PATH inventory.db def get_connection(): conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row conn.execute(PRAGMA foreign_keys ON) return conn def init_database(): with closing(get_connection()) as conn: conn.executescript(SCHEMA_SQL) conn.execute(CREATE INDEX IF NOT EXISTS idx_products_sku ON products(sku)) conn.execute(CREATE INDEX IF NOT EXISTS idx_stock_records_product ON stock_records(product_id)) conn.commit()逻辑说明conn.row_factory sqlite3.Row让查询结果可以通过列名取值方便传参给 Tkinter 表格控件PRAGMA foreign_keys ON必须每次连接时设置否则外键约束不生效。4. 核心功能实现Tkinter 界面、出入库操作与库存预警4.1 主界面框架Treeview 表格与刷新函数Tkinter 的ttk.Treeview是库存列表的容器显示所有商品。核心代码如下import tkinter as tk from tkinter import ttk class MainWindow(tk.Tk): def __init__(self): super().__init__() self.title(库存管理系统) self.geometry(1024x600) self.product_table ttk.Treeview(self, columns(id, sku, name, quantity, price), showheadings) self.product_table.heading(id, textID) self.product_table.heading(sku, textSKU) self.product_table.heading(name, text商品名) self.product_table.heading(quantity, text库存量) self.product_table.heading(price, text单价) self.product_table.pack(filltk.BOTH, expandTrue) self.refresh_product_list() def refresh_product_list(self): for row in self.product_table.get_children(): self.product_table.delete(row) rows get_all_products() for row in rows: tag low if row[quantity] row[min_threshold] else normal self.product_table.insert(, tk.END, values(row[id], row[sku], row[name], row[quantity], row[price]), tags(tag,)) self.product_table.tag_configure(low, background#ffcccc)代码说明get_all_products()返回sqlite3.Row列表每次刷新前先delete清空旧数据再插入新数据这是 Treeview 的唯一可靠刷新方式。tag_configure(low, background#ffcccc)让低于预警线的商品行显示浅红色做到可视化的低库存提醒。4.2 入库与出库方法事务保证数据一致性出入库操作直接更新products.quantity同时插入stock_records记录def adjust_stock(product_id, change_type, quantity): if quantity 0: raise ValueError(数量必须大于0) with closing(get_connection()) as conn: try: conn.execute(BEGIN) if change_type OUT: cur conn.execute(SELECT quantity FROM products WHERE id? FOR UPDATE, (product_id,)) row cur.fetchone() if not row: raise ValueError(商品不存在) if row[quantity] quantity: raise ValueError(库存不足当前库存: {}.format(row[quantity])) conn.execute(UPDATE products SET quantity quantity - ? WHERE id?, (quantity, product_id)) elif change_type IN: conn.execute(UPDATE products SET quantity quantity ? WHERE id?, (quantity, product_id)) conn.execute(INSERT INTO stock_records (product_id, change_type, change_quantity) VALUES (?, ?, ?), (product_id, change_type, quantity)) conn.commit() except Exception: conn.rollback() raise参数说明显式BEGIN和rollback保证如果插入流水失败库存数量不会已变更。SQLite 默认是自动提交模式多个语句必须包在事务里才是原子的。查询库存后先判断再更新避免并发下超卖。4.3 模糊搜索与分页加载库存数据量大时全表加载会卡界面。常见的做法是加搜索框用LIKE查询 LIMIT分页SELECT * FROM products WHERE name LIKE ? OR sku LIKE ? ORDER BY id DESC LIMIT ? OFFSET ?Python 侧传参def search_products(keyword, page1, page_size50): offset (page - 1) * page_size pattern f%{keyword}% with closing(get_connection()) as conn: rows conn.execute( SELECT * FROM products WHERE name LIKE ? OR sku LIKE ? LIMIT ? OFFSET ?, (pattern, pattern, page_size, offset) ).fetchall() total conn.execute( SELECT COUNT(*) FROM products WHERE name LIKE ? OR sku LIKE ?, (pattern, pattern) ).fetchone()[0] return rows, total提示如果源码里的搜索是SELECT * FROM products WHERE name ?的精确匹配建议改成LIKE模糊查询这才是“管理系统”的基本交互要求。5. 数据报表与低库存预警pandas 统计与 Excel 导出5.1 用 pandas 做库存周转与滞销分析除了基础的增删改查报表能力是拉开评分差距的地方。用 pandas 读数据库直接做统计import pandas as pd import sqlite3 def generate_stock_report(): conn sqlite3.connect(inventory.db) df pd.read_sql_query(SELECT * FROM products, conn) conn.close() df[stock_value] df[quantity] * df[price] category_summary df.groupby(category).agg( total_kinds(sku, count), total_quantity(quantity, sum), total_value(stock_value, sum) ).reset_index() low_stock df[df[quantity] df[min_threshold]] return df, category_summary, low_stock代码说明pd.read_sql_query一步把 SQLite 查询结果转成 DataFramestock_value是库存金额用于评估资金占用low_stock筛选低于阈值商品直接用于预警报表。这里不需要手动写循环遍历数据库行pandas 向量化计算性能更好。5.2 导出带合并单元格的 Excel 报表导出 Excel 时直接df.to_excel()够用但要做出课程设计级别的美观报表需要ExcelWriter配合openpyxlfrom openpyxl.styles import Font, PatternFill, Alignment def export_excel(df, category_summary, low_stock, filepath): with pd.ExcelWriter(filepath, engineopenpyxl) as writer: df.to_excel(writer, sheet_name库存明细, indexFalse) category_summary.to_excel(writer, sheet_name分类汇总, indexFalse) low_stock.to_excel(writer, sheet_name低库存预警, indexFalse) ws writer.sheets[库存明细] header_font Font(boldTrue, colorFFFFFF) header_fill PatternFill(start_color4472C4, end_color4472C4, fill_typesolid) for col_idx, cell in enumerate(ws[1], start1): cell.font header_font cell.fill header_fill cell.alignment Alignment(horizontalcenter) ws.column_dimensions[A].width 8 ws.column_dimensions[B].width 18 ws.column_dimensions[C].width 25参数说明PatternFill的start_color和end_color要写一样的颜色值fill_typesolid才是实心填充ws[1]是第一行即表头行。不同 sheet 分别放明细、汇总、预警让一个 Excel 文件同时满足查看和打印需求。5.3 定时自动生成报表的简单方案课程设计不需要上 Celery 或 APScheduler 那样的重型调度。用schedule库或time.sleep轮询即可import schedule import time def job(): df, category_summary, low_stock generate_stock_report() export_excel(df, category_summary, low_stock, freports/库存报表_{time.strftime(%Y%m%d)}.xlsx) schedule.every().day.at(21:00).do(job) while True: schedule.run_pending() time.sleep(60)这段逻辑在main.py中以独立线程启动不阻塞主界面的 Tkinter 事件循环。6. 打包成 exe 与 zip 交付前的最后坑点6.1 用 PyInstaller 打包需要连带数据库初始化课程设计交付时往往要求一个双击就能运行的程序PyInstaller 打包命令如下pyinstaller -F -w -i icon.ico main.py --hidden-import openpyxl --collect-all openpyxl参数说明-F打包成单文件-w去掉命令行窗口--hidden-import是因为 openpyxl 部分模块是动态导入的缺了会在运行时报ModuleNotFoundError。打包完的dist/main.exe首次运行时会在同目录生成数据库文件不要在打包前手动创建数据库否则路径不一致会找不到表。6.2 压缩交付时注意文件路径与数据库文件把源码和打包产物一起压缩成 zip 前先清理以下内容__pycache__目录这是运行缓存的 Python 字节码体积大且无用。开发时生成的inventory.db。如果用户拿到的是一个已经包含测试数据的数据库可能影响验收观感。建议删掉数据库文件并在README.md写明“首次运行自动创建数据库”。打包时的build/目录和.spec文件。如果重新压缩 zip 后运行 exe 报错failed to copy spatial iop zip或invalid zip archive,那是压缩工具的问题——不要用某些国产压缩软件的高压缩率模式改用 7-Zip 的“存储”模式重新打包路径层级保持inventory_system/根目录完整。6.3 验证打包是否成功的三个检查点验证 exe 是否能独立运行依次检查三点在没有安装 Python的干净虚拟机或另一台电脑上运行 exe观察是否报缺 DLL 或缺模块。执行一次完整的“入库→出库→导出 Excel”流程确认openpyxl能正常写入文件。PyInstaller 打包最常见的问题是 Excel 导出功能在开发环境正常、打包后报错。查看生成的inventory.db是否在当前工作目录而不是在C:\Windows\System32——如果用sqlite3.connect(inventory.db)相对路径双击 exe 时工作目录是 exe 所在目录用绝对路径os.path.join(sys._MEIPASS, inventory.db)会把数据库打进临时解压目录退出后被系统回收数据丢失。最后一个小技巧在main.py顶部加一段if getattr(sys, frozen, False): BASE_DIR os.path.dirname(sys.executable) else: BASE_DIR os.path.dirname(__file__)然后所有文件操作都基于BASE_DIR打包和源码运行两种模式下都能准确定位数据库与报表文件的落点。本文还有配套的精品资源点击获取
返回列表