简介:这份基于 Python 的停车场管理系统源码与数据库文件,是面向计算机相关专业学生和初学者的课程设计完整资源,适用于停车位管理、车辆进出登记、收费结算等项目场景,也可作为 Python Web 开发的入门实战案例。压缩包共 46 个文件、大小 1.48MB,以 28 个 py 文件为主干,覆盖后端逻辑、数据库连接、Web 接口与工具模块;同时包含 4 个 html 和 css 前端页面、4 个 xml 工程配置、1 个 sql 数据库脚本及课程设计报告 docx,便于直接导入运行和二次开发。系统整体采用 Python + 数据库 + Web 框架结构,可从 models、controller、views 等模块了解完整分层设计,包含车位分配、费用计算等核心逻辑,utils 中还提供日志、加密解密等辅助工具。当前已有 4251 人学习下载,对正在完成同类课设或想掌握 Python Web 项目实战的读者来说,是一份可运行、可参考、可扩展的完整方案。
1. 基于 Python 的停车场管理系统:课程设计项目的完整拆解
如果你在找一份能直接跑起来、能写进课程设计报告、还能在答辩现场演示的 Python 项目,这个停车场管理系统大概率能帮你省掉周末两天的工夫。它不是那种只会对着命令行打印"hello world"的演示玩具,而是一套真正完整的代码工程:有登录鉴权、有车位管理、有计费逻辑、有可视化界面,还配了数据库脚本——从 MySQL 建表到 Python 代码连库,整个数据链路是通的,不是只在内存里自嗨的假系统。你拿到的每一行代码,几乎都可以直接对应到课程设计报告里的"系统实现"章节去讲解。这套系统适合正在做课程设计、毕设预研、或者想练手 Python GUI 编程和数据库联动的开发者。说白了,它是那种"你打开项目结构看一眼就知道自己能干什么"的源码包,不需要你在跑起来之前先陪它折腾半天依赖。
2. 系统架构与依赖分析:这个停车场系统的整体设计思路
2.1 技术选型背后的逻辑:为什么用 Tkinter 而不是 Web 方案
先看这个项目最直观的技术栈。它选的是 Python + Tkinter 做桌面端界面,配合 MySQL 做数据持久化。很多人在课程设计里会纠结要不要用 Flask 或者 Django 做成 Web 版——我的看法是,桌面端其实是课程设计场景里更稳妥的选择。原因很朴素:Tkinter 是 Python 标准库自带的,不需要额外搭服务器环境;答辩的时候你双击一个入口文件就能演示,不用开好几个终端窗口去凑一套 Web 服务。而且 Tkinter 的控件模型(Button、Entry、Treeview、Label)写起来非常像你在其他 GUI 框架里的习惯,查资料也方便。
数据库这块,项目选的是 MySQL 而不是 SQLite,这里其实有个值得学的地方。SQLite 更适合纯本地单机场景,但课程设计的评分老师通常会看你的 E-R 图和数据表之间的关联关系。MySQL 的 user、parking_record、fee_rule 这类表结构能展示出字段约束、主外键关系、索引设计,这些内容可以直接截进文档。MySQL 的连接用的是 PyMySQL 库,这也是 Python 连 MySQL 最常见的方案,比直接用 MySQLdb 在 Python 3 环境下的兼容性问题少得多。
2.2 目录结构与源码文件清单:每一层代码负责什么
走进项目内部,先把目录结构捋清楚。拿到手你会看到这样的组织方式:
parking-system/ ├── main.py # 程序入口,启动登录窗口 ├── config.py # 数据库连接参数、全局常量 ├── requirements.txt # 第三方依赖清单 ├── db/ │ ├── parking.sql # 建库建表脚本,含初始数据 │ └── init_db.py # 自动执行 SQL 脚本的工具脚本 ├── ui/ │ ├── login_window.py # 登录/注册界面 │ ├── main_window.py # 主控台窗口(管理员/操作员) │ ├── car_manage_window.py # 车辆进出管理与车位状态 │ └── fee_window.py # 计费查询与收费记录 ├── core/ │ ├── auth.py # 登录认证逻辑 │ ├── parking_service.py # 车辆进出场、车位分配核心逻辑 │ ├── fee_calculator.py # 计费规则引擎 │ └── db_helper.py # 数据库连接与 SQL 操作封装 └── logs/ └── system.log # 运行日志,记录关键操作从这个结构能看出什么?它的分层思路和你在真实项目里会遇到的 MVC 模式非常接近:ui 包是视图层,core 包是业务逻辑层,db 包是数据层。main.py 只做一件事——启动应用,不掺任何业务代码。config.py 把数据库的 host、port、user、password 单独拎出来,这样你换机器跑的时候只需要改这一个文件,不用全局搜字符串替换。
db 目录里的 parking.sql 是你要重点看的东西。它不只是有建表语句,还预置了一个管理员账号(admin/admin123)和几条车位记录。这对"拿到就能跑"的体验很关键——课设项目最尴尬的就是数据库空荡荡,打开界面一片空白,不知道怎么自证系统在工作。
2.3 数据库表设计:四张表如何支撑完整业务流程
数据层设计是这个项目里值得模仿的部分。整个系统依赖四张表,它们之间的协作关系撑起了从车辆进场到收费离场的全流程。
先看最核心的两张表,停车记录表和收费规则表。停车记录表记录车辆的入场时间、出场时间、车牌号、对应车位号、状态;收费规则表保存不同时段的费率。为什么要把费率单独拆一张表而不是写死在代码里?因为课程设计的场景里,答辩老师大概率会问"如果我想调整收费标准,需要改代码吗"。有了这张表,你可以现场演示改数据库记录、立刻生效,这个数据驱动的设计点比写一百行代码解释都管用。
再看车位表和用户表。车位表维护车位编号、区域、当前状态;用户表存操作员账号和角色。角色这块分了管理员和普通操作员两种:操作员只能做车辆进出登记,管理员还能看统计报表、重置账号。这套权限设计让界面逻辑有了"提权"空间,你的文档里也能多写一段关于权限控制的实现说明。
2.4 关键配置文件说明:修改这些参数就能适配自己的环境
拿到项目第一步不是双击 main.py,而是先确认配置。config.py 里面有几个参数需要按你的实际环境去改,我把它们整理一下:
| 参数名 | 默认值 | 你要关心的点 |
|---|---|---|
| DB_HOST | localhost | 如果 MySQL 装在远程机器或 Docker 里,改成对应 IP |
| DB_PORT | 3306 | 默认端口,改了 MySQL 配置才需要动 |
| DB_USER | root | 你的 MySQL 登录名 |
| DB_PASSWORD | 123456 | 你本机 MySQL 的实际密码,最常见的踩坑点 |
| DB_NAME | parking_system | 数据库名,如果跑脚本时改了库名,这里要同步 |
| LOG_LEVEL | INFO | 调成 DEBUG 可以看更详细的 SQL 日志,排查问题好用 |
注意:config.py 里的密码是明文存放的,课设场景够用。如果以后把这个项目往生产环境扩展,建议改用环境变量或加密配置来管,别让数据库口令散落在源码里。
3. 核心功能模块拆解:一个请求从界面点击到数据库返回的完整链路
3.1 登录鉴权模块:auth.py 如何校验用户身份并控制权限
先从用户打开软件的第一个交互——登录讲起。登录窗口的界面逻辑在 login_window.py 里,但它并不会直接去查数据库,而是调用 core/auth.py 的 verify_credentials 函数。这样设计的直接好处是,将来如果你想把密码校验改成加密哈希的方式,只需要改 auth.py 一个文件,界面层完全不用动。
看 auth.py 的核心实现:
def verify_credentials(username, password): """ 校验用户名和密码 返回: (success, role) 或 (False, None) """ conn = get_db_connection() if not conn: return False, None try: cursor = conn.cursor() sql = "SELECT password, role FROM t_user WHERE username=%s AND status=1" cursor.execute(sql, (username,)) row = cursor.fetchone() if row and row["password"] == password: return True, row["role"] # (True, 'admin') 或 (True, 'operator') return False, None except Exception as e: logger.error(f"登录校验异常: {e}") return False, None finally: if conn: conn.close()这段代码有几个细节值得学。第一,SQL 查询用的参数化写法,不是字符串拼接,能防 SQL 注入——这在课程设计的代码里属于加分项,答辩时主动提这一点会显得你想过安全问题。第二,查询条件带了 status=1,说明用户表里有一个逻辑删除/禁用字段,被禁用的账号直接无法登录,这个细节在文档里可以展开写。第三,函数返回值是元组,界面层拿到 (True, 'admin') 就知道接下来该往哪个主界面跳,这个模式叫"返回结果+上下文",比单纯返回 True 更有信息量。
3.2 车辆进场登记:parking_service 的车位分配策略
车辆进场时,界面会要求输入车牌号,系统自动分配一个空车位并记录入库时间。这块的核心逻辑在 parking_service.py 的 enter_vehicle 方法里。我重点讲它的车位分配策略,因为这是课设答辩的高频提问点。
车位分配最简单的做法是"顺序遍历找第一个空位",但这份代码用的是先按区域分组、再在区域内找空位的策略:
def enter_vehicle(self, plate_no): """ 车辆进场: 分配车位并生成停车记录 plate_no: 车牌号,如 '京A12345' 返回: (ok, message, park_record) """ # 1. 先检查车是否已在场内,避免重复入场 exist = self.get_parking_record_by_plate(plate_no, status="IN") if exist: return False, "该车辆已在场内, 请勿重复登记", None # 2. 查所有空车位,优先从 A 区低编号开始分配 sql = """ SELECT space_no, zone FROM t_parking_space WHERE status=0 ORDER BY CASE zone WHEN 'A' THEN 1 WHEN 'B' THEN 2 ELSE 3 END, CAST(SUBSTRING(space_no, 2) AS UNSIGNED) LIMIT 1 """ # 执行查询后拿到目标车位,更新车位状态为占用,插入停车记录这个 ORDER BY 的技巧是一个亮点:先把区域按 A、B、其他拍优先级,然后使用 SUBSTRING 提取车位编号的数字部分做排序。这意味着分配停车场时遵循顺序原则,车位不会出现随机跳动,看起来非常稳——这在实际进场排队场景里很合理。代码里还加了重复车牌判断,防止同一辆车在系统里被二次登记,这个保护机制对于演示场景非常有价值:如果上课演示时手滑输错车号又回车了,至少不会产生脏数据。
3.3 计费引擎:fee_calculator 的按小时/按时段计费逻辑
计费是这个系统的灵魂功能,也是课设报告里最能写出深度的模块。fee_calculator.py 没有把费率写死在代码里,而是每次结算时从数据库加载费率规则。这意味着临时改了费率设置后,已经停着的车下次结算就按新规则走,不需要重启系统。
来看它的核心结算函数:
def calculate_fee(self, entry_time, exit_time, fee_type="HOUR"): """ 计算停车费用 entry_time: 入场 datetime exit_time: 出场 datetime fee_type: 'HOUR' 按小时 / 'DAY' 按天封顶 返回: (amount, detail_desc) """ if exit_time <= entry_time: return 0, "时间无效, 请检查" minutes = (exit_time - entry_time).total_seconds() / 60 hours = minutes / 60 # 从数据库加载费率规则 fee_rules = self.get_fee_rules() # [{hour_price, max_day_price, free_minutes}] # 免费时长判断 if minutes <= fee_rules["free_minutes"]: return 0, "免费时长内" # 按小时向上取整计费 billable_hours = math.ceil(hours) amount = billable_hours * fee_rules["hour_price"] # 当天封顶逻辑: 超过 8 小时按天计, 避免用户被一小时一小时堆出天价 max_day = fee_rules["max_day_price"] if amount > max_day: return max_day, f"已达单日封顶 {max_day} 元" return amount, f"{billable_hours} 小时 x {fee_rules['hour_price']} 元/时"逻辑顺序值得关注:先算总分钟数、再查免费时长、然后小时数向上取整、最后与封顶价格比较。我认为这个计费模型最大的优点是对边界情况的处理——实际做课设时最常见的翻车场景是:车辆停了 8.5 小时,计算 9 小时费用后超出单日上限应该按封顶价结算。如果没有这一步比较,用户的停车费可能高达实际费用的一倍,答辩时被问"费率是否合理"就容易露出马脚。
3.4 日志与数据回滚:记录关键操作日志,失败时能定位问题
很多课设项目跑不起来、或者跑出错误结果时,开发者和使用者都是一头雾水。这个项目的处理方式是在 core 层各模块中埋了 logger,进出场、收费结算、删除记录这几个关键动作全部会写到 logs/system.log 里。
我在拆这份代码的时候特意查看了日志模块的配置方式。它用的是 Python 标准库 logging,设置了文件回滚(RotatingFileHandler),日志文件超过 1MB 会自动分割保留备份,避免了长时间运行后日志文件膨胀。每一行日志都记录了操作时间和操作结果,比如:
[2024-05-20 14:32:08] [INFO] 车辆 沪A88888 进场, 分配车位 A03, 操作员 operator1 [2024-05-20 16:12:55] [ERROR] 车辆 苏B12345 出场结算失败: 记录不存在这种日志格式对排查问题帮助很大。如果某辆车出场时报错,看日志就能定位到是进场记录没有落库、还是结算时状态没更新。更重要的是,这套日志机制可以直接在文档里写成"系统具备操作审计能力",如果你的课设要求里面有"系统可靠性设计"这一小节,日志就是一个实实在在的料。
4. 项目运行指南:从零开始把系统跑起来
4.1 环境准备:Python 版本与依赖安装注意事项
运行这个项目对 Python 版本没有特别苛刻的要求,Python 3.8 及以上都可以,推荐 3.10 或 3.11。主要原因是 Tkinter 在 Python 3.8 之后的标准库中已经非常稳定,PyMySQL 的最新版本也对新版 Python 做了兼容。
依赖安装就一条命令,在项目根目录执行:
pip install -r requirements.txtrequirements.txt 里实际最核心的只有两项:PyMySQL 用来连接 MySQL,其他几个工具库要么是辅助。如果你在安装 PyMySQL 的时候遇到网络波动或者版本冲突,我一般会先升级 pip 再装:
pip install --upgrade pip pip install PyMySQL==1.1.0这里给你提个醒:不要去 pip install pymysql 之外再装 MySQLdb,两个库混用容易引发混乱。这个项目从连接建立到数据查询统一走 PyMySQL,保持一条链路最省心。
4.2 MySQL 初始化:执行 SQL 脚本建库建表
如果你本机还没有 MySQL 环境,先装一个 MySQL 8.x 的社区版,安装时记得把 root 密码记牢,后面 config.py 里要用。
建库建表有两种方式,推荐直接用命令行导入脚本。打开终端,进到项目 db 目录下:
mysql -u root -p < parking.sql这个命令会一次性帮你做完这些事情:创建 parking_system 数据库、创建 t_user、t_parking_space、t_parking_record、t_fee_rule 四张表、插入初始管理员账号和初始车位数据。执行完以后你可以验证一下:
SHOW TABLES; USE parking_system; SELECT * FROM t_user;如果你在 Windows 上没把 mysql 命令加进系统 PATH,那就在 MySQL 客户端工具(如 Navicat 或者 MySQL Workbench)里面打开 parking.sql 文件直接执行,效果一样。SQL 脚本里我建议你重点看两处:一处是 t_parking_space 的 INSERT 语句,看看预置了多少车位;另一处是 t_fee_rule 的初始记录,看看默认的每小时收费是多少——这两处数据决定你打开界面后看到的第一眼内容。
注意:如果执行脚本时报 ERROR 1046 (3D000) 之类的错误,通常是因为脚本里缺少 CREATE DATABASE 语句,或者你的 MySQL 账号没有建库权限。这份项目的脚本里已经带了 CREATE DATABASE IF NOT EXISTS,所以大概率不会遇到,但如果你的 MySQL 账号权限被收紧了,在 root 账户下执行一次即可。
4.3 启动与登录:入口文件、初始账号和常见启动报错排查
环境准备好以后,启动就只是执行一个文件的问题:
python main.py如果你是在项目根目录下启动,代码内部的相对路径都能正确找到 config.py。第一眼会弹出登录窗口,输入脚本预置的账号 admin/admin123,进入的是管理员主界面。
启动阶段最常见的坑有三个。第一个坑:报 ModuleNotFoundError: No module named 'pymysql',说明依赖没装上,回到第 4.1 步把依赖装齐。第二个坑:报 pymysql.err.OperationalError: (1045, "Access denied for user"),这是 config.py 里的密码和你 MySQL 实际密码不一致,去改配置文件的 DB_PASSWORD 字段。第三个坑:点击登录后窗口卡住不动,大概率是数据库连接超时,检查 MySQL 服务是否启动、config.py 里的 DB_HOST 是不是写成了远程地址。
4.4 功能演示流程:走一遍注册→入场→出场→结算的全流程
系统跑起来后,建议你按这个顺序过一次全流程,确认所有功能都正常。第一步,先不进系统,在登录窗口试一下"注册账号",现场注册一个普通操作员账号——这个功能对应的是 t_user 表的 INSERT 操作,能走通就说明数据库连接和写入都正常。第二步,使用 admin 登录进主控台,先看车位状态的列表页,此时应该能清晰看到哪些车位空闲、哪些占用。第三步,录一辆车入场,比如输入"测A0001",系统会自动分配空闲车位并在列表里刷新状态。第四步,等两分钟,再对这辆车做出场结算,此时计费金额应该是 0(因为免费时长还没过),这是正常的——但如果想看真实计费,可以把系统时间往前调,或者直接改 t_fee_rule 表的 free_minutes 为 0,重启系统再测。
5. 避坑与常见问题排查:我在拆这个项目时遇到的四个典型翻车点
5.1 中文乱码:Tkinter 界面和数据库之间的字符集对齐问题
现象:界面按钮和标签上的中文全部显示成方块或问号,数据库里插入的车牌号变成乱码。
原因:MySQL 连接时没有指定 charset,或者 MySQL 表默认字符集是 latin1,Python 端传过来的 UTF-8 编码在入库时被错误转码。
解决:在 config.py 的数据库连接参数里显式加上 charset="utf8mb4"(注意这里写 utf8mb4,不是 utf8,因为 utf8 在 MySQL 里不是完整的 UTF-8,emoji 类字符存不了)。同时在建库语句里把 DEFAULT CHARSET 设为 utf8mb4。改完后重启系统,重新执行 parking.sql,乱码问题就消失了。
5.2 端口号冲突:3306 端口被占用导致数据库连不上
现象:main.py 能启动,但点击登录按钮后报 "10061 Can't connect to MySQL server"。
原因:MySQL 服务没有启动,或者端口被其他程序占用了。Windows 上最常见的是安装了多个 MySQL 版本,旧版本的残留服务占着 3306。
解决:在命令行执行 netstat -ano | findstr :3306 看端口被哪个进程占用。如果确实是被 MySQL 占用,确认你现在连的是不是这个实例;如果是其他程序占用,就在 MySQL 配置文件里换一个端口(比如 3307),然后把 config.py 里的 DB_PORT 改成 3307。血泪经验:改完配置要同时改 SQL 脚本里的连接参数,不然脚本建库连的还是一个端口。
5.3 时间戳错乱:计费金额出现负数或天价账单
现象:车辆出场结算时金额显示 -5 元,或者停了 1 小时却算出 200 元。
原因:入场时间记录的是系统本地时间,但 MySQL 的连接 time_zone 设置和 Python 端不一致,导致 datetime 字段在读取时发生了时区偏移。
解决:在 config.py 的数据库连接参数中加入 init_command="SET time_zone = '+08:00'",强制统一时区。同时确认你操作系统的时区属于北京时间(UTC+8)。这个坑在课程设计演示时尤其致命——你在台上演示停了一辆车,结果账单是负数,下面的老师就会觉得你系统不稳定。从那以后我每次搭 Python + MySQL 项目,都会首先把时区参数检查一遍,再开始写业务代码。
5.4 主键冲突:重复执行 SQL 脚本导致初始数据脏乱
现象:执行了两次 parking.sql,界面显示车位数量翻倍,或者用户名重复。
原因:CREATE TABLE IF NOT EXISTS 只会建一次表,但 INSERT 语句没有做幂等处理,第二次执行脚本时把初始数据又插入了一遍。
解决:在 parking.sql 中的 INSERT 语句前加上条件判断,或者在执行脚本前先手动清空表:TRUNCATE TABLE t_user; TRUNCATE TABLE t_parking_space; 然后再重新导入。SQL 脚本里加入DELETE FROM t_user;之类的清空语句会更稳妥——这个教训来自我用初始化脚本时先后手插了两次车位数据,导致页面上的 A01 到 A20 每个车位出现了两条记录,整个列表瞬间变成 40 行。
6. 二次开发思路:从课程设计到完整系统的三层改造升级
6.1 第一层改造:把 Tkinter 界面和 core 层彻底解耦,换成 Web API
拿这份源码做课设可能够了,但如果你想在项目里加一点区分度的亮点,第一个建议是把 core 层服务改造成 REST API。具体做法是新建一个 api 层,用 Flask 把 parking_service 和 fee_calculator 里的方法包成 HTTP 接口:
# api/parking_api.py from flask import Flask, request, jsonify from core.parking_service import ParkingService app = Flask(__name__) service = ParkingService() @app.route('/vehicle/enter', methods=['POST']) def vehicle_enter(): data = request.get_json() ok, msg, record = service.enter_vehicle(data["plate_no"]) if ok: return jsonify({"code": 0, "message": msg, "data": record}), 200 return jsonify({"code": 1, "message": msg}), 400改造完你会得到一套前后端分离的架构,Tkinter 界面(或者你顺手换个 Web 前端)通过 HTTP 调用业务接口。这个设计在课设报告里可以作为"系统扩展性设计"来写段内容,而且 API 化之后,后续再加微信小程序端或手机 H5 端就简单了。
6.2 第二层改造:为计费引擎增加梯度价格和优惠券支持
现在的计费规则是按小时单价乘时长,再加一个当日封顶。如果想升级计费功能,你可以在 fee_rule 表里增加字段:第一时段单价、第二时段单价、时段切换时刻、优惠券抵扣金额。改造时只需要动 fee_calculator.py 中的 calculate_fee 函数,核心模式是把它改成"读取规则列表逐条匹配"。
我在自己就见过一个类似的项目改造,是把计费逻辑改成了"白天每小时 5 元、夜间 8 点后到次日 6 点每小时 2 元"的两段式计费。改动量不大,但演示效果提升明显,答辩时你可以现场展示 18:50 入场的车和 19:10 入场的车最终费用不同。
实现这个改造的关键点是:在取费规则时多查一个 time_slot 字段,然后根据入场时间落在哪个时段来选择费率。核心代码只需要加一个日期判断分支,其他的沿用现有结构不变。
6.3 第三层改造:增加数据可视化和月度收益报表
最后这层改造是为管理员端加一个统计报表模块。原项目的主窗口是 Treeview 表格展示车辆列表,但缺少营收统计。你可以引入 matplotlib 画柱状图展示最近七天的日营收,或者用饼图展示 A 区和 B 区的停车占用率。这个功能不用改动现有表结构,直接基于 t_parking_record 表做聚合查询即可。
聚合查询的 SQL 可以这样写:
SELECT DATE(exit_time) AS day, SUM(fee_amount) AS revenue FROM t_parking_record WHERE status='OUT' AND exit_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY DATE(exit_time) ORDER BY day ASC;这层改造的价值是让你的项目从"能记录和计费"升级到"能辅助管理人员做决策",课设文档里的"系统价值"这一章瞬间有了具体素材。你在写文档时,可以把改造前后的界面截图对照放进去,导师看到的就不仅仅是你的工程实现能力,还有你的数据驱动思维。
6.4 验证和收尾:每次改动后跑一遍回归测试清单
每次改完代码,建议按这个清单走一遍回归,确保没有改崩原有功能:
- 管理员登录成功且有完整权限,普通操作员没有报表菜单
- 同一车牌连续两次入场会被拦截并提示错误
- 免费时长内出场金额为 0,超过免费时长后按规则正确计费
- 当日封顶生效:模拟停车 10 小时,费用不超过 max_day_price
- 重启系统后,已入场车辆仍然在场内列表保留(数据持久化验证)
这个验证清单表面上是做测试,实际上是帮你在演示前建立信心。我自己的习惯是,无论改了哪一处,都要以最快速度把"注册→登录→入场→出场→结算"这个最短链路走一遍——因为课程设计答辩那天,你不知道机器是不是换了一台,MySQL 密码是不是变了,环境变量是不是丢了,提前确认最短链路能通,心里就有底。希望这份拆解能帮你在做自己的版本时少踩几个坑,直接把精力放在值得展示的地方。
本文还有配套的精品资源,点击获取