
Python,速成心法
敲代码,查资料,问Ai
练习,探索,总结,优化

★★★★★博文创作不易,源码使用过程中,如有疑问的地方,欢迎大家指正留言交流。喜欢的老铁可以多多点赞+收藏分享+置顶,小红牛在此表示感谢。★★★★★
------★Python练手项目源码★------
Python项目102:Tkinter实现电脑屏幕锁屏,输入正确密码才能解锁
Python项目99:Tkinter十六进制颜色对照表2.0(140种颜色)
Python项目94:全球疫情模拟数据可视化大屏(dash+plotly+pandas)
Python项目91:绘制红楼梦人物关系图(NetworkX+Matplotlib)
Python项目89:NetworkX最短路径规划(城市交通)
Python项目88:文件备份与压缩系统2.0(tkinter+shutil+zipfile)
Python项目86:增强版画板2.0(tk.Canvas)
Python项目81:Excel工作表批量重命名工具1.0(tkinter+openpyxl)
Python项目78:学生成绩分析系统(Tkinter+SQLite3)
Python项目77:模拟炒股训练系统3.0(Mplfinance+tkinter)
Python项目76:员工排班表系统1.0(tkinter+sqlite3+tkcalendar)
Python项目74:多线程数据可视化工具2.0(tkinter+matplotlib+mplcursors)
Python项目73:自动化文件备份系统1.0(tkinter)
Python项目源码71:药品管理系统1.0(tkinter+sqlite3)
Python项目源码69:Excel数据筛选器1.0(tkinter+sqlite3+pandas)
Python项目源码63:病历管理系统1.0(tkinter+sqlite3+matplotlib)
Python源码62:酒店住房管理系统1.0(tkinter+sqlite3)
Python项目源码57:数据格式转换工具1.0(csv+json+excel+sqlite3)
Python项目源码56:食堂饭卡管理系统1.0(tkinter+splite3)
Python项目源码54:员工信息管理系统2.0(tkinter+sqlite3)
Python项目源码52:模拟银行卡系统1.0(账户管理、存款、取款、转账和交易记录查询)
Python项目源码50:理发店会员管理系统1.0(tkinter+sqlite3)
Python项目源码48:正则表达式调试工具3.0(tkinter+re+requests)
Python项目源码44:图书管理系统1.0(tkinter+sqlite3)
Python项目源码42:仓库商品管理系统1.0(tkinter+sqlite3+Excel)
Python项目源码40:字符串处理工具(tkinter+入门练习)
Python项目源码39:学生积分管理系统1.0(命令行界面+Json)
Python项目源码35:音乐播放器2.0(Tkinter+mutagen)
Python项目源码33:待办事项列表应用2.0(命令行界面+Json+类)
Python项目32:订单销售额管理系统1.0(Tkinter+CSV)
Python项目源码29:学生缴费管理系统(Tkinter+CSV)
Python项目28:设计日志管理系统2.0(Tkinter+Json)

↓ 完整源码如下 ↓
# -*- coding: utf-8 -*-# @Author : 小红牛# 微信公众号:wdPythonimport sqlite3import tkinter as tkfrom tkinter import ttk, messagebox# ---------------------------- 数据库操作(直接持久化) ----------------------------class Database:def __init__(self, db_path="schedule.db"):self.conn = sqlite3.connect(db_path)self.cursor = self.conn.cursor()self._create_table()def _create_table(self):self.cursor.execute('''CREATE TABLE IF NOT EXISTS courses (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,weekday INTEGER NOT NULL, -- 1=星期一 ... 7=星期日period INTEGER NOT NULL, -- 1~8location TEXT)''')self.conn.commit()def get_all_courses(self):self.cursor.execute("SELECT id, name, weekday, period, location FROM courses ORDER BY weekday, period")rows = self.cursor.fetchall()return [{"id": r[0], "name": r[1], "weekday": r[2], "period": r[3], "location": r[4]} for r in rows]def add_course(self, name, weekday, period, location):self.cursor.execute("INSERT INTO courses (name, weekday, period, location) VALUES (?, ?, ?, ?)",(name, weekday, period, location))self.conn.commit()return self.cursor.lastrowiddef update_course(self, course_id, name, weekday, period, location):self.cursor.execute("UPDATE courses SET name=?, weekday=?, period=?, location=? WHERE id=?",(name, weekday, period, location, course_id))self.conn.commit()def delete_course(self, course_id):self.cursor.execute("DELETE FROM courses WHERE id=?", (course_id,))self.conn.commit()def delete_all_courses(self):self.cursor.execute("DELETE FROM courses")self.conn.commit()def check_conflict(self, weekday, period, exclude_id=None):if exclude_id:self.cursor.execute("SELECT id FROM courses WHERE weekday=? AND period=? AND id != ?",(weekday, period, exclude_id))else:self.cursor.execute("SELECT id FROM courses WHERE weekday=? AND period=?",(weekday, period))return self.cursor.fetchone() is not Nonedef close(self):self.conn.close()# ---------------------------- 课程编辑对话框 ----------------------------class CourseDialog:WEEKDAYS = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]WEEKDAY_TO_NUM = {name: i+1 for i, name in enumerate(WEEKDAYS)}NUM_TO_WEEKDAY = {i+1: name for i, name in enumerate(WEEKDAYS)}PERIODS = [(1, "第1节 (08:00-08:45)"),(2, "第2节 (08:55-09:40)"),(3, "第3节 (10:00-10:45)"),(4, "第4节 (10:55-11:40)"),(5, "第5节 (14:00-14:45)"),(6, "第6节 (14:55-15:40)"),(7, "第7节 (16:00-16:45)"),(8, "第8节 (16:55-17:40)")]PERIOD_NUM_TO_TEXT = {num: text for num, text in PERIODS}PERIOD_TEXT_TO_NUM = {text: num for num, text in PERIODS}def __init__(self, parent, title="课程信息", course_data=None):self.parent = parentself.course_data = course_data # 编辑时传入带id的字典self.result = Noneself.dialog = tk.Toplevel(parent)self.dialog.title(title)self.dialog.resizable(False, False)self.dialog.grab_set()frame = ttk.Frame(self.dialog, padding=10)frame.pack(fill=tk.BOTH, expand=True)ttk.Label(frame, text="课程名称:").grid(row=0, column=0, sticky=tk.W, pady=5)self.name_entry = ttk.Entry(frame, width=25)self.name_entry.grid(row=0, column=1, pady=5, padx=5)ttk.Label(frame, text="星期:").grid(row=1, column=0, sticky=tk.W, pady=5)self.weekday_cb = ttk.Combobox(frame, values=self.WEEKDAYS, state="readonly", width=22)self.weekday_cb.grid(row=1, column=1, pady=5, padx=5)period_texts = [text for _, text in self.PERIODS]ttk.Label(frame, text="节次:").grid(row=2, column=0, sticky=tk.W, pady=5)self.period_cb = ttk.Combobox(frame, values=period_texts, state="readonly", width=22)self.period_cb.grid(row=2, column=1, pady=5, padx=5)ttk.Label(frame, text="地点:").grid(row=3, column=0, sticky=tk.W, pady=5)self.location_entry = ttk.Entry(frame, width=25)self.location_entry.grid(row=3, column=1, pady=5, padx=5)btn_frame = ttk.Frame(frame)btn_frame.grid(row=4, column=0, columnspan=2, pady=15)ttk.Button(btn_frame, text="确定", command=self._ok).pack(side=tk.LEFT, padx=5)ttk.Button(btn_frame, text="取消", command=self._cancel).pack(side=tk.LEFT, padx=5)if course_data:self.name_entry.insert(0, course_data["name"])weekday_text = self.NUM_TO_WEEKDAY[course_data["weekday"]]self.weekday_cb.set(weekday_text)period_text = self.PERIOD_NUM_TO_TEXT[course_data["period"]]self.period_cb.set(period_text)self.location_entry.insert(0, course_data.get("location", ""))self.dialog.update_idletasks()w = self.dialog.winfo_width()h = self.dialog.winfo_height()x = (self.dialog.winfo_screenwidth() // 2) - (w // 2)y = (self.dialog.winfo_screenheight() // 2) - (h // 2)self.dialog.geometry(f"{w}x{h}+{x}+{y}")self.dialog.wait_window()def _ok(self):name = self.name_entry.get().strip()if not name:messagebox.showerror("错误", "课程名称不能为空", parent=self.dialog)returnweekday_text = self.weekday_cb.get()if not weekday_text:messagebox.showerror("错误", "请选择星期", parent=self.dialog)returnperiod_text = self.period_cb.get()if not period_text:messagebox.showerror("错误", "请选择节次", parent=self.dialog)returnlocation = self.location_entry.get().strip()weekday = self.WEEKDAY_TO_NUM[weekday_text]period = self.PERIOD_TEXT_TO_NUM[period_text]self.result = {"name": name,"weekday": weekday,"period": period,"location": location,"id": self.course_data["id"] if self.course_data else None}self.dialog.destroy()def _cancel(self):self.result = Noneself.dialog.destroy()# ---------------------------- 主应用程序(直接操作数据库) ----------------------------class ScheduleApp:WEEKDAYS = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]def __init__(self, root):self.root = rootself.root.title("课程表管理系统")self.root.geometry("900x600")self.db = Database()self._create_menu()self._create_layout()self.refresh_grid()def _create_menu(self):menubar = tk.Menu(self.root)self.root.config(menu=menubar)file_menu = tk.Menu(menubar, tearoff=0)menubar.add_cascade(label="文件", menu=file_menu)file_menu.add_command(label="加载示例课程", command=self.load_example_courses)file_menu.add_separator()file_menu.add_command(label="清空所有课程", command=self.clear_all_courses)file_menu.add_separator()file_menu.add_command(label="退出", command=self.on_exit)help_menu = tk.Menu(menubar, tearoff=0)menubar.add_cascade(label="帮助", menu=help_menu)help_menu.add_command(label="使用说明", command=self.show_help)def _create_layout(self):main_frame = ttk.Frame(self.root)main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)self.grid_frame = ttk.Frame(main_frame)self.grid_frame.pack(fill=tk.BOTH, expand=True)info_label = ttk.Label(main_frame, text="💡 提示:双击单元格添加/编辑课程,右键弹出菜单(删除/清空/示例)",foreground="gray")info_label.pack(side=tk.BOTTOM, pady=2)def refresh_grid(self):"""从数据库读取最新数据并刷新网格"""self.courses = self.db.get_all_courses()self._draw_grid()def _draw_grid(self):for child in self.grid_frame.winfo_children():child.destroy()max_period = 8weekdays = self.WEEKDAYS# 星期标题行for col, weekday_name in enumerate(weekdays, start=1):label = ttk.Label(self.grid_frame, text=weekday_name, font=("Arial", 10, "bold"),relief=tk.RIDGE, anchor=tk.CENTER)label.grid(row=0, column=col, sticky=tk.NSEW, padx=1, pady=1)# 左侧节次标签period_texts = {1: "第1节\n08:00", 2: "第2节\n08:55", 3: "第3节\n10:00", 4: "第4节\n10:55",5: "第5节\n14:00", 6: "第6节\n14:55", 7: "第7节\n16:00", 8: "第8节\n16:55"}for period in range(1, max_period+1):label = ttk.Label(self.grid_frame, text=period_texts[period], font=("Arial", 9),relief=tk.RIDGE, anchor=tk.CENTER)label.grid(row=period, column=0, sticky=tk.NSEW, padx=1, pady=1)# 网格权重配置for col in range(1, 8):self.grid_frame.columnconfigure(col, weight=1)for row in range(1, max_period+1):self.grid_frame.rowconfigure(row, weight=1)self.grid_frame.columnconfigure(0, weight=0)# 构建快速查找字典(仅用于显示)course_dict = {(c["weekday"], c["period"]): c for c in self.courses}# 填充单元格for weekday in range(1, 8):for period in range(1, max_period+1):key = (weekday, period)course = course_dict.get(key)display_text = ""if course:name = course["name"]loc = course.get("location", "")display_text = f"{name}\n({loc})" if loc else namecell = tk.Label(self.grid_frame, text=display_text, font=("Arial", 9),relief=tk.GROOVE, anchor=tk.CENTER, bg="#F9F9F9")cell.grid(row=period, column=weekday, sticky=tk.NSEW, padx=1, pady=1)# 存储单元格对应的课程ID(如果有)cell.course_id = course["id"] if course else Nonecell.weekday = weekdaycell.period = periodcell.bind("<Double-Button-1>", lambda e, w=weekday, p=period: self.on_double_click(w, p))cell.bind("<Button-3>", lambda e, w=weekday, p=period: self.show_context_menu(e, w, p))if not course:cell.configure(bg="#FFFFFF")# ---------------------------- 事件处理(直接操作数据库) ----------------------------def on_double_click(self, weekday, period):"""双击:有课则编辑,无课则添加(预设星期节次)"""course = self._find_course(weekday, period)if course:self.edit_course(course["id"], weekday, period)else:self.add_course(weekday, period)def show_context_menu(self, event, weekday, period):course = self._find_course(weekday, period)menu = tk.Menu(self.root, tearoff=0)if course:menu.add_command(label="编辑课程", command=lambda: self.edit_course(course["id"], weekday, period))menu.add_command(label="删除课程", command=lambda: self.delete_course(course["id"]))else:menu.add_command(label="添加课程", command=lambda: self.add_course(weekday, period))menu.add_separator()menu.add_command(label="加载示例课程", command=self.load_example_courses)menu.add_command(label="清空所有课程", command=self.clear_all_courses)menu.post(event.x_root, event.y_root)def _find_course(self, weekday, period):for c in self.courses:if c["weekday"] == weekday and c["period"] == period:return creturn None# ---------------------------- 数据库操作(直接持久化) ----------------------------def add_course(self, default_weekday=None, default_period=None):"""弹出添加对话框,直接写入数据库"""# 构造预设数据(不影响原course_data结构)preset = Noneif default_weekday is not None and default_period is not None:preset = {"weekday": default_weekday, "period": default_period}# 传递None表示新增,但预设星期节次self._open_course_dialog(None, preset)def edit_course(self, course_id, weekday, period):course = self._find_course(weekday, period)if not course:returnself._open_course_dialog(course, None)def delete_course(self, course_id):if messagebox.askyesno("确认删除", "确定删除该课程吗?"):self.db.delete_course(course_id)self.refresh_grid()messagebox.showinfo("成功", "课程已删除")def clear_all_courses(self):if messagebox.askyesno("清空确认", "此操作将删除所有课程,不可恢复!\n确定继续吗?"):self.db.delete_all_courses()self.refresh_grid()messagebox.showinfo("成功", "所有课程已清空")def load_example_courses(self):"""加载示例课程(直接写入数据库,会覆盖原有数据)"""if messagebox.askyesno("加载示例", "加载示例课程会删除当前所有课程,确定继续吗?"):self.db.delete_all_courses()examples = [("高等数学", 1, 1, "教2-101"),("大学英语", 1, 3, "教2-205"),("程序设计", 2, 1, "实验楼302"),("体育", 2, 7, "田径场"),("数据结构", 3, 2, "教3-110"),("毛概", 3, 5, "教1-202"),("操作系统", 4, 1, "实验楼205"),("概率论", 5, 1, "教2-109"),("形势与政策", 5, 5, "教1-305"),]for name, weekday, period, location in examples:self.db.add_course(name, weekday, period, location)self.refresh_grid()messagebox.showinfo("示例加载完成", "已成功加载示例课程。")# ---------------------------- 通用对话框(增/改) ----------------------------def _open_course_dialog(self, course_data, preset):"""course_data: 编辑时传入的课程字典(含id)preset: 新增时预设的星期和节次,格式 {"weekday": int, "period": int}"""# 如果是新增且有预设,构造一个临时course_data用于填充对话框temp_course_data = Noneif preset and course_data is None:temp_course_data = {"id": None,"name": "","weekday": preset["weekday"],"period": preset["period"],"location": ""}dialog = CourseDialog(self.root, "编辑课程" if course_data else "添加课程", course_data or temp_course_data)result = dialog.resultif result:# 冲突检测conflict = self.db.check_conflict(result["weekday"], result["period"],exclude_id=result["id"] if result["id"] else None)if conflict:messagebox.showerror("冲突", "该星期和节次已有其他课程,请重新选择")returnif result["id"]:# 更新self.db.update_course(result["id"], result["name"], result["weekday"], result["period"], result["location"])else:# 新增self.db.add_course(result["name"], result["weekday"], result["period"], result["location"])self.refresh_grid()# ---------------------------- 帮助与退出 ----------------------------def show_help(self):help_text = """课程表管理系统使用说明• 添加课程:双击空白单元格,或在空白单元格上右键选择「添加课程」。• 编辑课程:双击已有课程的单元格,或右键选择「编辑课程」。• 删除课程:在课程单元格上右键,选择「删除课程」。• 清空所有课程:右键菜单或“文件”菜单中的「清空所有课程」。• 加载示例课程:右键菜单或“文件”菜单中的「加载示例课程」(会覆盖现有数据)。所有操作直接保存到数据库,无需额外点击保存按钮。"""messagebox.showinfo("使用说明", help_text)def on_exit(self):if messagebox.askyesno("退出", "确定退出课程表系统吗?"):self.db.close()self.root.destroy()if __name__ == "__main__":root = tk.Tk()app = ScheduleApp(root)root.protocol("WM_DELETE_WINDOW", app.on_exit)root.mainloop()
完毕!!感谢您的收看
------★★历史博文集合★★------
