前言
這一節(jié)我們將實(shí)現(xiàn)校驗(yàn)、添加、修改、刪除、保存的功能。
part5 完成項(xiàng)目
一、創(chuàng)建StudentServicel類
1. 準(zhǔn)備好校驗(yàn)類check
為了完善學(xué)生或教師信息的添加功能,我們需要對(duì)輸入的數(shù)據(jù)進(jìn)行校驗(yàn)。前面我們已經(jīng)使用過(guò)Check類,利用正則表達(dá)式分別對(duì)學(xué)號(hào)、姓名、性別、手機(jī)號(hào)、郵箱的基本規(guī)范做校驗(yàn)?,F(xiàn)在我們可以直接拿來(lái)使用。
2. 定義抽象類services
我們?cè)俣x一個(gè)抽象類services,準(zhǔn)備定義抽象方法有:add
、update、delete、check;
from abc import *
class Services(metaclass=ABCMeta):
@abstractmethod
def add(self): pass
@abstractmethod
def update(self): pass
@abstractmethod
def delete(self): pass
@abstractmethod
def check(self): pass
之后,我們?cè)趯?shí)現(xiàn)學(xué)生操作的功能創(chuàng)建一個(gè)studentservice模塊,繼承Services和Check。
class StudentServices(Services, Check):
def __init__(self, list_all, student_current: Student):
Services.__init__(self)
Check.__init__(self)
self.list_all = list_all
self.student_current = student_current
二、對(duì)輸入的數(shù)據(jù)數(shù)據(jù)進(jìn)行校驗(yàn)
開發(fā)到這個(gè)階段,在主界面點(diǎn)擊添加按鈕的時(shí)候,如果系統(tǒng)報(bào)錯(cuò)。可能是我們的StudentDetail類的構(gòu)造函數(shù)第二個(gè)參數(shù)current_student沒有設(shè)置默認(rèn)值,我們只需要給其添加一個(gè)默認(rèn)值current_student=""即可。它即使沒有收到參數(shù)的傳遞它也不會(huì)報(bào)錯(cuò)了。
1. 實(shí)現(xiàn)校驗(yàn)功能
當(dāng)我們點(diǎn)擊添加按鈕打開明細(xì)窗體,填入數(shù)據(jù)的時(shí)候,點(diǎn)擊保存按鈕時(shí),此時(shí)應(yīng)當(dāng)觸發(fā)對(duì)所填入數(shù)據(jù)的校驗(yàn)。
由于我們的保存按鈕寫在了basedetail中,所以我們先定義一個(gè)commit函數(shù)。然后在studentdetail重寫commit方法。
之后我們?cè)?code>StudentService類中定義一個(gè)check方法,用來(lái)校驗(yàn)輸入的信息是否有效。由于性別是通過(guò)radiobutton獲取的無(wú)需校驗(yàn),這里我們只校驗(yàn)學(xué)號(hào)、姓名、電話、郵箱,并作出如下規(guī)定:
| check的返回值 | 含義 |
|---|---|
| 1 | 符合要求 |
| 2 | 學(xué)號(hào)不符合要求 |
| 3 | 姓名不符合要求 |
| 4 | 電話不符合要求 |
| 5 | 郵箱地址不符合要求 |
我們定義校驗(yàn)函數(shù)如下:
def check(self):
return_number = 0
if not self.check_sno(self.student_current.sno):
return_number = 2
elif not self.check_name(self.student_current.name):
return_number = 3
elif not self.check_mobile(self.student_current.mobile):
return_number = 4
elif not self.check_email(self.student_current.email):
return_number = 5
else:
return_number = 1
return return_number
我們回到studentdetail模塊中,給commit函數(shù)添加功能,其中包括:封裝當(dāng)前填入的學(xué)生信息。
我們定義一個(gè)方法get_current_student把填入的信息封裝到student類
def get_current_student(self):
sno = self.var_sno.get()
name = self.var_name.get()
gender = ""
if self.var_gender.get() == 1:
gender = "男"
elif self.var_gender.get() == 2:
gender = "女"
birthday = self.var_birthday.get()
mobile = self.var_mobile.get()
email = self.var_email.get()
profession = self.var_profession.get()
entry_time = self.var_entry_time
# 封裝
current_student = Student(sno, gender, birthday, birthday, mobile, email, profession, entry_time)
# 返回
return current_student
然后在commit方法里,對(duì)封裝的學(xué)生對(duì)象調(diào)用check方法進(jìn)行校驗(yàn):
# 【1】封裝當(dāng)前的學(xué)生
current_student = self.get_current_student()
current_studentservices = StudentServices(self.list_student_all, current_student)
# 【2】校驗(yàn)學(xué)生
return_number = current_studentservices.check()
if return_number == 2:
showinfo("系統(tǒng)消息", "學(xué)號(hào)不符合要求")
elif return_number == 3:
showinfo("系統(tǒng)消息", "姓名不符合要求")
elif return_number == 4:
showinfo("系統(tǒng)消息", "手機(jī)號(hào)碼不符合要求")
elif return_number == 5:
showinfo("系統(tǒng)消息", "郵箱地址不符合要求")
elif return_number == 1:
showinfo("系統(tǒng)消息", "所有都符合要求")
這里我們只對(duì)學(xué)號(hào)、姓名、手機(jī)號(hào)碼、郵箱進(jìn)行校驗(yàn),如果不滿足,就彈出警示框。

三、添加學(xué)生信息
1. 需求分析
我們希望打開添加學(xué)生信息窗口后,填入學(xué)生信息,點(diǎn)擊保存按鈕后,數(shù)據(jù)會(huì)立即顯示在學(xué)校信息列表里。
2. 實(shí)現(xiàn)過(guò)程
為了獲取填入的信息,我們?cè)?code>studentservices中定義add方法,將student_current中的各個(gè)屬性依次添加到current_student_list列表中,再加current_student_list列表加入到總列表list_all中即可。
def add(self):
"""
添加學(xué)生
"""
# 暫存填入的學(xué)生信息
current_student_list = []
current_student_list.append(self.student_current.sno)
current_student_list.append(self.student_current.name)
current_student_list.append(self.student_current.gender)
current_student_list.append(self.student_current.birthday)
current_student_list.append(self.student_current.mobile)
current_student_list.append(self.student_current.email)
current_student_list.append(self.student_current.profession)
current_student_list.append(self.student_current.entry_time)
# 當(dāng)前學(xué)生添加到集合中
self.list_all.append(current_student_list)
我們?cè)俚?code>studentdetail模塊中,將commit方法繼續(xù)完善,補(bǔ)充添加功能。
current_studentservices.add()
執(zhí)行add方法后,list_student_all總學(xué)生列表已經(jīng)發(fā)生了變化,在明細(xì)窗口當(dāng)我們點(diǎn)擊關(guān)閉的時(shí)候,我們要將變化體現(xiàn)到主窗口的表格中來(lái)。
在我們添加完成后定義一個(gè)反饋信號(hào)給主窗體,
self.userinfo = 1
然后關(guān)閉這個(gè)明細(xì)窗體:
self.destory()
另一邊,在主窗體中繼續(xù)完善add_student方法,在加載添加頁(yè)面的方法中傳入所有學(xué)生列表,和當(dāng)前學(xué)生信息。然后獲取明細(xì)窗體返回的值,刷新學(xué)生信息列表的Treeview,這樣添加學(xué)生信息,并且顯示在主窗體表格的功能就實(shí)現(xiàn)了。
def add_student(self):
"""
添加學(xué)生信息
"""
self.flag = 3
# 加載添加的頁(yè)面
detail = StudentDetail(self.flag, self.student_all, self.student_current)
self.wait_window(detail)
# 接受返回的值
get_return_number = detail.userinfo
# 如果為1 就刷新
if get_return_number == 1:
self.load_student_treeview()
3. 效果演示

四、修改學(xué)生信息
1. 需求分析
上面我們講了添加學(xué)生信息,現(xiàn)在我們繼續(xù)將修改學(xué)生信息。我們需要完成的功能是在主界面表格中選擇某一行,點(diǎn)擊修改按鈕,打開修改明細(xì)窗口界面。
2. 實(shí)現(xiàn)過(guò)程
首先我們?cè)谥鹘缑嫘薷膶W(xué)生信息的方法中,在update_student方法中,修改flag,獲取當(dāng)前行的信息,最后再實(shí)例化明細(xì)窗體界面。這些操作與添加學(xué)生信息的過(guò)程基本一致,這里不做過(guò)多重復(fù)講解。
def update_student(self):
"""
修改學(xué)生信息
"""
# 修改flag
self.flag = 2
# 獲取當(dāng)前行的信息
item = self.Tree_student.selection()[0]
current_student = self.Tree_student.item(item,"values")
for item in self.student_all:
if current_student[0] == item[0]:
self.student_current = item
# 實(shí)例化界面
detail = StudentDetail(self.flag, self.student_all, self.student_current)
接著,我們?cè)诿骷?xì)窗體中隊(duì)load_gui_by_flag方法再做完善,補(bǔ)充當(dāng)flag==2的情況。和查看明細(xì)窗口一樣,我們需要將主窗體表格中選中的學(xué)生信息填充到明細(xì)窗體中來(lái)。由于學(xué)號(hào)是不允許修改的,所以將學(xué)號(hào)輸入框禁用。
self.Entry_sno["state"] = DISABLED
效果演示:

然后在
studentservices完善一下update方法,我們通過(guò)循環(huán)遍歷
list_all,判斷如果list_all中的學(xué)號(hào)與current_student學(xué)號(hào)相同,那么把明細(xì)窗體組建中的變量值賦值給list_all中匹配的那個(gè)學(xué)生信息。
# 主要通關(guān)過(guò)循環(huán)遍歷list,修改信息
for index in range(len(self.list_all)):
if self.list_all[index][0] == self.student_current.sno:
self.list_all[index][1] = self.student_current.name
self.list_all[index][2] = self.student_current.gender
self.list_all[index][3] = self.student_current.birthday
self.list_all[index][4] = self.student_current.mobile
self.list_all[index][5] = self.student_current.email
self.list_all[index][6] = self.student_current.profession
self.list_all[index][7] = self.student_current.entry_time
數(shù)據(jù)修改完成后,我們?cè)?code>commit方法中完善修改功能,與添加學(xué)生信息的操作一樣,我們需要封裝當(dāng)前學(xué)生類對(duì)象、校驗(yàn)輸入的學(xué)生信息是否符合要求、調(diào)用update方法修改列表、提示用戶修改成功、反饋信號(hào)給主窗體、最后關(guān)閉這個(gè)窗體。
然后我們?cè)谥鞔绑w界面中接收反饋,并刷新表格。
我們來(lái)看一下修改的動(dòng)作有沒有在主窗體表格中表現(xiàn)出來(lái)。
這樣,我們修改的動(dòng)作就已經(jīng)完成了,其實(shí)套路和添加學(xué)生信息一樣,現(xiàn)在我們演示一下將小新的專業(yè)改為“物聯(lián)網(wǎng)”,一下為演示效果:

五、刪除學(xué)生信息
1.需求分析
我們希望這主窗體界面的表格中選中學(xué)生,然后點(diǎn)擊刪除按鈕,學(xué)生信息就被刪除掉。注意,以上包括添加、修改、已經(jīng)這里的刪除,其操作對(duì)象都是list,我們還并沒有保存到本地文件。寫入到本地文件等會(huì)我們會(huì)統(tǒng)一來(lái)做。
刪除操作,相對(duì)簡(jiǎn)單,因?yàn)樗恍枰谥鹘缑孢M(jìn)行,沒有跨越GUI,所有比較方便操作。
2.實(shí)現(xiàn)過(guò)程
我們首先把student_services模塊中的delete方法給寫好,我們同樣還是通過(guò)遍歷list_all,然后匹配選中項(xiàng),根據(jù)索引刪除該項(xiàng)。
def delete(self):
# 主要通過(guò)循環(huán)遍歷list,刪除某條信息
for index in range(len(self.list_all)):
if self.list_all[index][0] == self.student_current.sno:
# pop是根據(jù)索引號(hào)來(lái)刪除的
self.list_all.pop(index)
緊接著,我們完善主窗體中的delete_studet方法,先獲取選中行的數(shù)據(jù),然后遍歷list_all,找到與被選中學(xué)生學(xué)號(hào)相同的項(xiàng)item,然后把該條學(xué)生項(xiàng)封裝成一個(gè)待刪除的對(duì)象student_object,實(shí)例化student_services后,彈出窗口詢問是否刪除,如果是則執(zhí)行刪除動(dòng)作,否則不刪除。
def delete_student(self):
"""
刪除學(xué)生信息
"""
# 獲取雙擊行的數(shù)據(jù)
item = self.Tree_student.selection()[0]
current_student = self.Tree_student.item(item, "values")
student_object = ""
for item in self.student_all:
if current_student[0] == item[0]:
# 封裝到student
student_object = Student(item[1], item[2], item[3], item[4],
item[5], item[0], item[6], item[7])
# 實(shí)例化StudentServices
current_student_services = StudentServices(self.student_all, student_object)
# 詢問是否刪除
choose = askyesno("刪除確認(rèn)","確認(rèn)要?jiǎng)h除學(xué)生信息【學(xué)號(hào):"+student_object.sno +
",姓名:"+student_object.name+"】的信息嗎?")
if choose:
# 執(zhí)行刪除動(dòng)作
current_student_services.delete()
# 提醒刪除成功
showinfo("系統(tǒng)消息", "學(xué)生消息刪除成功")
# 更新表格
self.load_student_treeview()
else:
return
3. 效果演示

六、保存數(shù)據(jù)到文件
1. 需求分析
我們以上所做的所有增刪改都是對(duì)list進(jìn)行操作,當(dāng)程序重新打開后又恢復(fù)原樣了,我們最后需要做的是把操作結(jié)果寫入到文本文件中。
2. 實(shí)現(xiàn)過(guò)程
在file類中,我們定義方法write_to_file,先把文本內(nèi)容清空,然后遍歷list_student_all中的每一個(gè)項(xiàng),用,填充在每一個(gè)項(xiàng)中,然后去除其中可能存在的換行符,但是在每一行的結(jié)束加上換行符。
def write_to_file(self):
# ============ 把學(xué)生信息寫入文件 ========
try:
# 把原先的文件清空
with open(self.student_path, mode="w", encoding="UTF-8") as fd_student:
fd_student.write("")
with open(self.student_path, mode="a", encoding="UTF-8") as fd_student:
for item in self.list_student_all:
temp = ",".join(item)
temp = temp.replace("\n", "") + "\n" # 取出行中可能存在的換行符
fd_student.write(temp)
except Exception as e:
raise e
寫入操作完成后,我們應(yīng)當(dāng)設(shè)置關(guān)閉窗口按鈕時(shí)觸發(fā)寫入文件操作。
我們需要把窗體關(guān)閉按鈕的行為轉(zhuǎn)化為方法,在主窗體的構(gòu)造函數(shù)中添加一個(gè)把窗體關(guān)閉按鈕的行為轉(zhuǎn)化為方法的語(yǔ)句。
self.protocol("WM_DELETE_WINDOW", self.close_window)
然后在下面定義close_window的方法,點(diǎn)擊窗口關(guān)閉按鈕時(shí)跳出彈窗,詢問是否保存修改,如果是則調(diào)用write_to_file方法寫入文件,寫入成功后系統(tǒng)彈窗提醒用戶已經(jīng)寫入成功,寫入成功后關(guān)閉窗體即可。
def close_window(self):
# 詢問是否保存修改
choose= askyesno("關(guān)閉前的提醒", "關(guān)閉窗體前是否要將修改寫入到文件?")
if choose:
try:
self.file_info.write_to_file()
showinfo("系統(tǒng)提示", "所有的修改已經(jīng)保存并寫入到文件")
# 關(guān)閉窗口
self.destroy()
except Exception as e:
showinfo("系統(tǒng)消息", e)
else:
self.destroy()
注意:即使點(diǎn)擊否,也要調(diào)用destory方法關(guān)閉窗口,否則窗體會(huì)陷入無(wú)法關(guān)閉的死循環(huán)。
3. 效果演示

最后
以上,就是利用面向?qū)ο笏枷雽?shí)現(xiàn)學(xué)生教師信息管理的全部?jī)?nèi)容了,如果大家能手動(dòng)完成這個(gè)項(xiàng)目,必將對(duì)面向?qū)ο笏枷胗兄由羁痰睦斫狻?br> 配套的源碼及素材可以私信我獲取。