1.项目结构
![](https://i-blog.csdnimg.cn/direct/7a82d443fad54fd99e3f69ed4707299d.png)
sql:
sql
CREATE TABLE IF NOT EXISTS School (
SchoolId TEXT not null,
SchoolName TEXT NOT NULL,
SchoolTelNo TEXT NOT NULL
)
整体思路
- Model:负责与 SQLite 数据库进行交互,包括创建表、插入、删除、更新和查询数据等操作。
- View :使用 Tkinter 和
ttk.Treeview
创建用户界面,包含输入框、按钮和分页控件,用于显示数据并处理用户的交互。 - Controller:处理用户界面的事件,调用 Model 中的方法进行数据操作,并更新 View 中的显示。
代码实现:
Model 部分
python
# encoding: utf-8
# 版权所有 2024 ©涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# OS : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 oracle 11g sqlite
# Datetime : 2024-11-20 22:35:21
# database :sql server 2019
# User : geovindu
# Product : PyCharm
# Project : pySQLiteMvcDemo
# File : bll/School.py
# explain : 学习
from __future__ import annotations
from abc import ABC, abstractmethod
import os
import sys
from Model.school import SchoolInfo
from Factory.AbstractFactory import AbstractFactory
class SchoolBll(object):
"""
学校表
"""
dal = AbstractFactory.createSchool
"""
类属性 接口DAL
"""
def __init__(self):
"""
"""
self.__name = "SchoolBll"
self.createtable() # 如果不存在,初始化表
def __del__(self):
"""
:return:
"""
print(f"{self.__name} ERASE MEMORY")
def Close(cls):
"""
关闭
:return:
"""
cls.dal().Close()
def createtable(self):
"""
建表
"""
self.dal().createtable()
def selectData(self) -> list:
"""
:return:
"""
data = self.dal().selectSql()
return data
def select(self) -> list[SchoolInfo]:
"""
:return:
"""
schools = []
data = self.dal().selectSql()
if len(data) > 0:
for SchoolId,SchoolName,SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectSql(cls) -> list[SchoolInfo]:
"""
元组数据
:return: list 列表
"""
schools = []
data = cls.dal().selectSql()
if len(data) > 0:
for SchoolId,SchoolName,SchoolTelNo in data[0]:
info=SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectSqlCount(cls) -> int:
"""
查询数据 总数
:return:
"""
#print(cls.dal().selectSqlCount()[0][0])
total = cls.dal().selectSqlCount()[0][0]
return total
def Count(self) -> int:
"""
查询数据 总数
:return:
"""
total = self.dal().selectSqlCount()[0][0]
return total
def getcount(cls, search_query=""):
"""
计算
:param search_query:
:return:
"""
return cls.dal().getcount(search_query)
def getschools(cls, page, limit, search_query=""):
"""
查询
:param page:
:param limit:
:param search_query:
:return:
"""
data=cls.dal().getschools(page, limit, search_query)
print("data:",data)
return data
def selectSqlOrder(cls, order: str) -> list[SchoolInfo]:
"""
元组数据
:param order: SchoolName desc/asc
:return:
"""
schools = []
data = cls.dal().selectSqlOrder(order)
if len(data) > 0:
for SchoolId,SchoolName,SchoolTelNo in data[0]:
info=SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectSort(cls,field:str,isOrder:bool)->list[SchoolInfo]:
"""
:param field SchoolId
:param order: desc/asc
:return:
"""
schools = []
data = cls.dal().selectSort(field,isOrder)
if len(data) > 0:
for SchoolId,SchoolName,SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectIdSql(cls,SchoolId:str) -> list[SchoolInfo]:
"""
:param SchoolId:ID
:return:
"""
schools = []
data = cls.dal().selectIdSql(SchoolId)
#print(data)
if len(data)>0:
for SchoolId,SchoolName,SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def addSql(cls,info:SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
return cls.dal().addSql(info)
def add(self,info:SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
return self.dal().addSql(info)
def editSql(cls,info:SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
#print(info)
return cls.dal().editSql(info)
def edit(self,info:SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
#print(info)
return self.dal().editSql(info)
def delSql(cls, SchoolId: str) -> int:
"""
:param SchoolId:
:return:
"""
return cls.dal().delSql(SchoolId)
def delinfo(self, SchoolId: str) -> int:
"""
:param SchoolId:
:return:
"""
return self.dal().delSql(SchoolId)
View 部分
python
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# OS : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 oracle 21c Neo4j sqlite
# Datetime : 2025/2/11 20:37
# User : geovindu
# Product : PyCharm
# Project : pySQLiteMvcDemo
# File : ViewUI/SchoolView.py
# explain : 学习
import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from Model.school import SchoolInfo
class SchoolView(tk.Frame):
"""
View 类:负责创建和管理用户界面
"""
def __init__(self, master=None):
"""
:param master:
"""
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
"""
:return:
"""
self.search_frame = tk.Frame(self)
self.search_frame.pack(pady=10)
# 搜索输入框
self.search_label = tk.Label(self.search_frame, text="搜索:")
#self.search_label.pack()
self.search_label.grid(row=0, column=0)
self.search_entry = tk.Entry(self.search_frame)
#self.search_entry.pack()
self.search_entry.grid(row=0, column=1)
# 搜索按钮
self.search_button = tk.Button(self.search_frame, text="搜索")
#self.search_button.pack()
self.search_button.grid(row=0, column=2)
self.tree_frame = tk.Frame(self)
self.tree_frame.pack(pady=10)
# Treeview 控件
self.tree = ttk.Treeview(self.tree_frame, columns=('ID', '校名', '电话'), show='headings')
self.tree.heading('ID', text='ID')
self.tree.heading('校名', text='校名')
self.tree.heading('电话', text='电话')
self.tree.pack()
# Pagination Frame
self.pagination_frame = tk.Frame(self)
self.pagination_frame.pack(pady=10)
# 分页控件
self.prev_button = tk.Button(self.pagination_frame, text="上一页")
self.prev_button.pack(side=tk.LEFT)
self.page_label = tk.Label(self.pagination_frame, text="第 1 页")
self.page_label.pack(side=tk.LEFT)
self.pagetotal_label = tk.Label(self.pagination_frame, text="/共 1 条")
self.pagetotal_label.pack(side=tk.LEFT)
self.next_button = tk.Button(self.pagination_frame, text="下一页")
self.next_button.pack(side=tk.LEFT)
# Add/Update/Delete Frame
self.action_frame = tk.Frame(self)
self.action_frame.pack(pady=10)
self.id_label = tk.Label(self.action_frame, text="編號:")
#self.id_label.pack()
self.id_label.grid(row=0, column=0)
self.id_entry = tk.Entry(self.action_frame)
self.id_entry.grid(row=0, column=1)
# 校名输入框
self.name_label = tk.Label(self.action_frame, text="校名:")
self.name_label.grid(row=1, column=0)
self.name_entry = tk.Entry(self.action_frame)
self.name_entry.grid(row=1, column=1)
# 电话输入框
self.phone_label = tk.Label(self.action_frame, text="电话:")
self.phone_label.grid(row=2, column=0)
self.phone_entry = tk.Entry(self.action_frame)
self.phone_entry.grid(row=2, column=1)
# 添加按钮
self.add_button = tk.Button(self.action_frame, text="添加")
self.add_button.grid(row=3, column=0)
# 修改按钮
self.update_button = tk.Button(self.action_frame, text="修改")
self.update_button.grid(row=3, column=1)
# 删除按钮
self.delete_button = tk.Button(self.action_frame, text="删除")
self.delete_button.grid(row=3, column=2)
def clear_entries(self):
"""
:return:
"""
self.id_entry.delete(0, tk.END)
self.name_entry.delete(0, tk.END)
self.phone_entry.delete(0, tk.END)
self.search_entry.delete(0, tk.END)
def populate_treeview(self, contacts):
"""
:param contacts:
:return:
"""
for i in self.tree.get_children():
self.tree.delete(i)
for contact in contacts:
self.tree.insert('', 'end', values=contact)
def update_page_label(self, page, total_pages):
"""
:param page:
:param total_pages:
:return:
"""
self.page_label.config(text=f"第 {page} 页/共 {total_pages} 页")
def update_page_total(self,tatal):
"""
:param tatal:
:return:
"""
self.pagetotal_label.config(text=f"共{tatal} 条")
class AddSchoolWindow(tk.Toplevel):
"""
弹出窗口 - 添加学校
"""
def __init__(self, master, controller):
"""
:param master:
:param controller:
"""
super().__init__(master)
self.controller = controller
self.title("添加学校")
self.create_widgets()
def create_widgets(self):
"""
:return:
"""
self.action_frame = tk.Frame(self)
self.action_frame.pack(pady=10)
# 学校 ID 输入框
self.id_label = tk.Label(self.action_frame, text="学校 ID:")
self.id_label.grid(row=0, column=0)
self.id_entry = tk.Entry(self.action_frame)
self.id_entry.grid(row=0, column=1)
# 学校名称输入框
self.name_label = tk.Label(self.action_frame, text="学校名称:")
self.name_label.grid(row=1, column=0)
self.name_entry = tk.Entry(self.action_frame)
self.name_entry.grid(row=1, column=1)
# 学校电话输入框
self.tel_label = tk.Label(self.action_frame, text="学校电话:")
self.tel_label.grid(row=2, column=0)
self.tel_entry = tk.Entry(self.action_frame)
self.tel_entry.grid(row=2, column=1)
# 保存按钮
self.save_button = tk.Button(self.action_frame, text="保存", command=self.save_school)
self.save_button.grid(row=3, column=0)
def save_school(self):
"""
:return:
"""
school_id = self.id_entry.get()
school_name = self.name_entry.get()
school_tel = self.tel_entry.get()
info = SchoolInfo()
info.SchoolId = school_id
info.SchoolName = school_name
info.SchoolTelNo = school_tel
if school_id and school_name and school_tel:
self.controller.add_school(info)
self.destroy()
else:
messagebox.showerror("错误", "所有字段均为必填项")
class EditSchoolWindow(tk.Toplevel):
"""
弹出窗口 - 修改学校
"""
def __init__(self, master, controller, school_id, school_name, school_tel):
"""
:param master:
:param controller:
:param school_id:
:param school_name:
:param school_tel:
"""
super().__init__(master)
self.controller = controller
self.school_id = school_id
self.title("修改学校")
self.create_widgets(school_name, school_tel)
def create_widgets(self, school_name, school_tel):
"""
:param school_name:
:param school_tel:
:return:
"""
self.action_frame = tk.Frame(self)
self.action_frame.pack(pady=10)
# 学校 ID 标签(不可编辑)
self.id_label = tk.Label(self.action_frame, text="学校 ID:")
self.id_label.grid(row=0, column=0)
self.id_entry = tk.Entry(self.action_frame)
self.id_entry.insert(0, self.school_id)
self.id_entry.config(state='readonly')
self.id_entry.grid(row=0, column=1)
# 学校名称输入框
self.name_label = tk.Label(self.action_frame, text="学校名称:")
self.name_label.grid(row=1, column=0)
self.name_entry = tk.Entry(self.action_frame)
self.name_entry.insert(0, school_name)
self.name_entry.grid(row=1, column=1)
# 学校电话输入框
self.tel_label = tk.Label(self.action_frame, text="学校电话:")
self.tel_label.grid(row=2, column=0)
self.tel_entry = tk.Entry(self.action_frame)
self.tel_entry.insert(0, school_tel)
self.tel_entry.grid(row=2, column=1)
# 保存按钮
self.save_button = tk.Button(self.action_frame, text="保存", command=self.save_school)
self.save_button.grid(row=3, column=0)
def save_school(self):
"""
:return:
"""
school_name = self.name_entry.get()
school_tel = self.tel_entry.get()
if school_name and school_tel:
print("edit",self.school_id)
info=SchoolInfo()
info.SchoolId=self.school_id
info.SchoolName=school_name
info.SchoolTelNo=school_tel
#self.controller.update_school(self.school_id, school_name, school_tel)
self.controller.update_school(info)
self.destroy()
else:
messagebox.showerror("错误", "所有字段均为必填项")
Controller 部分
python
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# OS : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 oracle 21c Neo4j sqlite
# Datetime : 2025/2/11 20:36
# User : geovindu
# Product : PyCharm
# Project : Controller/pySQLiteMvcDemo
# File : SchoolController.py
# explain : 学习
import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from Model.school import SchoolInfo
from BLLModel.school import SchoolBll
from ViewUI.SchoolView import AddSchoolWindow
from ViewUI.SchoolView import EditSchoolWindow
class SchoolController(object):
"""
Controller 类:负责处理用户输入和更新模型和视图
"""
def __init__(self, model, view):
"""
:param model:
:param view:
"""
self.model = SchoolInfo()
self.view = view
self.current_page = 1
self.limit = 10
self.search_query = ""
# 绑定按钮点击事件到相应的处理方法
self.view.add_button.config(command=self.addschools)
#self.view.add_button.config(command=self.open_add_window)
self.view.update_button.config(command=self.updateschools)
self.view.delete_button.config(command=self.deleteschools)
self.view.search_button.config(command=self.searchschools)
self.view.prev_button.config(command=self.prev_page)
self.view.next_button.config(command=self.next_page)
self.view.tree.bind("<Double-1>", self.open_edit_window)
self.showschools()
self.bll = SchoolBll()
def open_add_window(self):
"""
:return:
"""
AddSchoolWindow(self.view.master, self)
def open_edit_window(self, event):
"""
:param event:
:return:
"""
selected_item = self.view.tree.selection()
if selected_item:
values = self.view.tree.item(selected_item)['values']
school_id = values[0]
school_name = values[1]
school_tel = values[2]
EditSchoolWindow(self.view.master, self, school_id, school_name, school_tel)
def add_school(self, info:SchoolInfo):
"""
:param info:
:return:
"""
self.bll.addSql(info)
self.show_schools()
def delete_school(self):
"""
:return:
"""
selected_item = self.view.tree.selection()
if selected_item:
school_id = self.view.tree.item(selected_item)['values'][0]
self.bll.delSql(school_id)
self.show_schools()
else:
messagebox.showerror("错误", "请选择要删除的学校")
def update_school(self, info):
"""
:param info:
:return:
"""
self.bll.editSql(info)
self.show_schools()
def search_schools(self):
"""
:return:
"""
self.search_query = self.view.search_entry.get()
self.current_page = 1
self.show_schools()
def show_schools(self):
"""
:return:
"""
total_count = self.bll.getcount(self.search_query)[0][0]
print(total_count)
if total_count >= 1:
total_pages = (total_count + self.limit - 1) // self.limit
contacts = self.bll.getschools(self.current_page, self.limit, self.search_query)
self.view.populate_treeview(contacts)
self.view.update_page_label(self.current_page, total_pages)
self.view.update_page_total(total_count)
def addschools(self):
"""
:return:
"""
id = self.view.id_entry.get()
name = self.view.name_entry.get()
phone = self.view.phone_entry.get()
self.model.SchoolId=id
self.model.SchoolName=name
self.model.SchoolTelNo=phone;
if name and phone:
self.bll.add(self.model)
self.view.clear_entries()
self.showschools()
else:
messagebox.showerror("错误", "校名和电话不能为空")
def deleteschools(self):
"""
:return:
"""
selected_item = self.view.tree.selection()
if selected_item:
id = str(self.view.tree.item(selected_item)['values'][0])
print("id",id)
self.bll.delSql(id)
self.showschools()
else:
messagebox.showerror("错误", "请选择要删除的学校")
def updateschools(self, event):
"""
:return:
"""
selected_item = self.view.tree.selection()
if selected_item:
id = str(self.view.tree.item(selected_item)['values'][0])
#values = self.view.tree.item(selected_item)['values']
name = self.view.name_entry.get()
phone = self.view.phone_entry.get()
self.model.SchoolId = id
self.model.SchoolName = name
self.model.SchoolTelNo = phone
if name and phone:
self.bll.editSql(self.model)
self.view.clear_entries()
self.showschools()
else:
messagebox.showerror("错误", "校名和电话不能为空")
else:
messagebox.showerror("错误", "请选择要修改的学校")
def searchschools(self):
"""
:return:
"""
self.search_query = self.view.search_entry.get()
self.current_page = 1
self.showschools()
def prev_page(self):
"""
:return:
"""
if self.current_page > 1:
self.current_page -= 1
self.showschools()
def next_page(self):
"""
:return:
"""
total_count = self.bll.getcount(self.search_query)[0][0]
total_pages = (total_count + self.limit - 1) // self.limit
if self.current_page < total_pages:
self.current_page += 1
self.showschools()
def showschools(self):
"""
:return:
"""
bll=SchoolBll()
total_count = bll.getcount(self.search_query)[0][0]
print(total_count)
if total_count >= 1:
total_pages = (total_count + self.limit - 1) // self.limit
contacts = bll.getschools(self.current_page, self.limit, self.search_query)
self.view.populate_treeview(contacts)
self.view.update_page_label(self.current_page, total_pages)
self.view.update_page_total(total_count)
调用:
python
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.塗聚文
# IDE : PyCharm Community Edition 2024.3 python 3.11
# OS : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 oracle 11g oracle 20c Neo4j sqlite
# Datetime : 2025/2/11 20:11
# User : geovindu
# Product : PyCharm
# Project : pySQLiteMVCDemo
# File : main.py
# explain : 学习
import tkinter as tk
from tkinter import ttk, messagebox
from BLLModel.school import SchoolBll
from ViewUI.SchoolView import SchoolView
from Controller.SchoolController import SchoolController
if __name__ == '__main__':
"""
"""
root = tk.Tk()
bllmodel = SchoolBll()
view = SchoolView(master=root)
controller = SchoolController(bllmodel, view)
root.title("School Management")
root.iconbitmap('favicon.ico')
root.mainloop()
输出:
![](https://i-blog.csdnimg.cn/direct/fa9592244e7d4c4d8604b60e1cd05128.png)