前言
上一節(jié)我們實現(xiàn)了在主窗體的數(shù)據(jù)查詢功能,系統(tǒng)能夠在所有的數(shù)據(jù)中精確地找到某個具體的符合條件的學(xué)生信息。但是在主窗體中顯示的都是概要信息,我們想要鼠標(biāo)雙擊即可查看某個具體的學(xué)生信息該怎么實現(xiàn)呢?
這一節(jié)我們將搭建一個顯示學(xué)生明細信息的窗體,顯示該學(xué)生的所有信息,只要雙擊表格中的某一行就能打開明細窗體,顯示學(xué)生明細信息。顯示學(xué)生明細窗體有三種不同的狀態(tài),在不同的狀態(tài)下窗體顯示的標(biāo)題是不同的。由于內(nèi)容比較多,我們這一節(jié)主要的工作內(nèi)容是GUI框架的搭建。
一、學(xué)生明細窗體GUI基本布局
我們新建一個detailgui.py文件,顯示學(xué)生明細信息。主要顯示的內(nèi)容有:學(xué)號、姓名、性別、出生日期、身份證號碼、手機號碼、郵箱地址、家庭住址、入學(xué)時間、專業(yè)、緊急聯(lián)系人、緊急聯(lián)系電話;其中性別我們通過Radiobutton的方式顯示,其余都通過Label和Entry來顯示。
本文重點講解的內(nèi)容是邏輯與實現(xiàn)部分;關(guān)于tkinter布局的基礎(chǔ)知識,這里主要用到的主要是Label標(biāo)簽和Entry輸入框還有Radiobutton單選框的繪制,我們前面的文章《Python基礎(chǔ)學(xué)習(xí)筆記:Tkinter》已經(jīng)做了具體的講解,這里就不再繼續(xù)重復(fù)了。
下面直接給出界面布局的代碼:
from tkinter import *
from tkinter.ttk import *
import os
class DetailWindow(Tk):
def __init__(self):
super().__init__()
self.title("學(xué)生明細信息")
self.geometry("600x500+600+150")
self.resizable(0,0) # 不能改變大小
# 加載控件
self.setup_UI()
def setup_UI(self):
# 設(shè)置style
self.Style01 = Style()
self.Style01.configure("title.TLabel",font=("微軟雅黑",25,"bold"),foreground = "navy")
self.Style01.configure("TLabel", font=("微軟雅黑", 16, "bold"), foreground="navy")
self.Style01.configure("TButton",font=("微軟雅黑",16,"bold"),foreground = "navy")
self.Style01.configure("TEntry", font=("微軟雅黑", 16, "bold"),width = 10)
self.Style01.configure("TRadiobutton",font=("微軟雅黑",16,"bold"),foreground = "navy")
# 加載上面的banner
self.Login_image = PhotoImage(file = "."+os.sep+"img"+os.sep+"stu_detail_banner.png")
self.Label_image = Label(self,image = self.Login_image)
self.Label_image.pack()
# 添加一個title
self.var_title = StringVar()
self.Label_title = Label(self,text="==明細窗體==",style = "title.TLabel")
self.Label_title.place(x=380,y=20)
# 加載一個pane
self.Pane_detail = PanedWindow(width = 590,height = 380)
self.Pane_detail.place(x = 5,y = 88)
# 添加屬性
# 第一排:學(xué)號
self.Label_sno = Label(self.Pane_detail,text = "學(xué)號:")
self.Label_sno.place(x=30,y=10)
self.var_sno = StringVar()
self.Entry_sno = Entry(self.Pane_detail,textvariable = self.var_sno,font=("微軟雅黑", 16, "bold"),width = 10)
self.Entry_sno.place(x=80,y=8)
# 姓名
self.Label_name = Label(self.Pane_detail,text = "姓名:")
self.Label_name.place(x=220,y=10)
self.var_name = StringVar()
self.Entry_name = Entry(self.Pane_detail,textvariable = self.var_name,font=("微軟雅黑", 16, "bold"),width = 10)
self.Entry_name.place(x=270,y=8)
# 性別
self.Label_gender = Label(self.Pane_detail,text = "性別:").place(x=410,y = 10)
self.var_gender = IntVar()
self.Radio_man = Radiobutton(self.Pane_detail,text="男",variable = self.var_gender,value = 1)
self.Radio_man.place(x=460,y = 10)
self.Radio_woman = Radiobutton(self.Pane_detail, text="女", variable=self.var_gender, value=0)
self.Radio_woman.place(x=510, y=10)
# 第二排:出生日期
self.Label_age = Label(self.Pane_detail,text="出生日期:")
self.Label_age.place(x=30,y=60)
self.var_age = StringVar()
self.Entry_age = Entry(self.Pane_detail,textvariable = self.var_age,font=("微軟雅黑", 16, "bold"),width = 12)
self.Entry_age.place(x=110,y=58)
# 身份證號碼
self.Label_id = Label(self.Pane_detail, text="身份證號碼:")
self.Label_id.place(x=250, y=60)
self.var_id = StringVar()
self.Entry_id = Entry(self.Pane_detail, textvariable=self.var_id,font=("微軟雅黑", 16, "bold"), width=19)
self.Entry_id.place(x=350, y=58)
# 第三排:手機號碼
self.Label_mobile = Label(self.Pane_detail, text="手機號碼:")
self.Label_mobile.place(x=30, y=110)
self.var_mobile = StringVar()
self.Entry_mobile = Entry(self.Pane_detail, textvariable=self.var_mobile,font=("微軟雅黑", 16, "bold"), width=14)
self.Entry_mobile.place(x=110, y=108)
# 郵箱地址
self.Label_email = Label(self.Pane_detail, text="郵箱地址:")
self.Label_email.place(x=270, y=110)
self.var_email = StringVar()
self.Entry_email = Entry(self.Pane_detail, textvariable=self.var_email,font=("微軟雅黑", 16, "bold"), width=19)
self.Entry_email.place(x=350, y=108)
# 第四排:家庭住址
self.Label_home = Label(self.Pane_detail, text="家庭住址:")
self.Label_home.place(x=30, y=160)
self.var_address = StringVar()
self.Entry_home = Entry(self.Pane_detail, textvariable=self.var_address,font=("微軟雅黑", 16, "bold"), width=43)
self.Entry_home.place(x=110, y=158)
# 第五排:入學(xué)時間
self.Label_studyin = Label(self.Pane_detail, text="入學(xué)時間:")
self.Label_studyin.place(x=30, y=210)
self.var_studyin = StringVar()
self.Entry_studyin = Entry(self.Pane_detail, textvariable=self.var_studyin,font=("微軟雅黑", 16, "bold"), width=12)
self.Entry_studyin.place(x=110, y=208)
# 專業(yè):
self.Label_pro = Label(self.Pane_detail, text="專業(yè):")
self.Label_pro.place(x=250, y=210)
self.var_pro = StringVar()
self.Entry_pro = Entry(self.Pane_detail, textvariable=self.var_pro,font=("微軟雅黑", 16, "bold"), width=24)
self.Entry_pro.place(x=300, y=208)
# 第六排:緊急聯(lián)系人
self.Label_emcon = Label(self.Pane_detail, text="緊急聯(lián)系人:")
self.Label_emcon.place(x=30, y=260)
self.var_emcon = StringVar()
self.Entry_emcon = Entry(self.Pane_detail, textvariable=self.var_emcon,font=("微軟雅黑", 16, "bold"), width=11)
self.Entry_emcon.place(x=120, y=258)
# 緊急聯(lián)系電話
self.Label_emtel = Label(self.Pane_detail, text="緊急聯(lián)系人電話:")
self.Label_emtel.place(x=250, y=260)
self.var_emtel = StringVar()
self.Entry_emtel = Entry(self.Pane_detail, textvariable=self.var_emtel,font=("微軟雅黑", 16, "bold"), width=16)
self.Entry_emtel.place(x=380, y=258)
# 放置兩個按鈕
self.Button_save = Button(self,text = "保存",style = "TButton").place(x=400,y=472)
self.Button_exit = Button(self,text = "關(guān)閉",style = "TButton").place(x=502,y=472)
if __name__ == '__main__':
this_window = DetailWindow()
this_window.mainloop()
效果演示:

二、加載明細窗體
1. 功能需求
加載學(xué)生明細信息我們應(yīng)該設(shè)置三種狀態(tài):查看、添加、修改;
(1)在查看狀態(tài)下,標(biāo)題是:查看學(xué)生明細,各信息欄輸入框中所有的信息都是只讀狀態(tài),并且右下角的保存按鈕處于隱藏狀態(tài);
(2)在添加狀態(tài)下,標(biāo)題是:添加學(xué)生明細,各信息欄輸入框清空,右下角保存按鈕可用;
(3)在修改狀態(tài)下,標(biāo)題是:修改學(xué)生明細,各信息欄除學(xué)號不能修改外,其余信息欄均可修改,并且右下角保存按鈕可用。
2. 觸發(fā)條件
我們實現(xiàn)查看學(xué)生明細信息的三種狀態(tài)的觸發(fā)條件:

3. 遇到問題
為了能查看學(xué)生明細信息,我們要在主窗體中定義一個函數(shù)load_detail_window(),在此之前我們要導(dǎo)入detailgui模塊,調(diào)用DetailWindow類;
import detailgui
def load_detail_window(self):
detail_window = detailgui.DetailWindow()
接著,我們再定義一個add_student的函數(shù),在這個函數(shù)中調(diào)用load_detail_window方法
def add_student(self):
self.load_detail_window()
然后在按鈕中添加command參數(shù),其值為add_student;
這樣做似乎貌似實現(xiàn)了簡單的加載學(xué)生明細窗體的功能,實際上當(dāng)我們執(zhí)行程序,點擊添加學(xué)生按鈕后,系統(tǒng)會報錯
_tkinter.TclError: image "pyimage4" doesn't exist
這是什么原因呢?
在tkinter里面有一個類Tk,其功能是產(chǎn)生一個主窗體,我們前面每次創(chuàng)建窗體時都用到了它。但是在tkinter里有一個規(guī)定,應(yīng)用程序同時只能運行一個主窗體。而這里我們的主窗體一直在運行,是沒有辦法再加載一個主窗體(明細信息)的。如果想同時加載兩個窗體,第二個窗體必須要以子窗體的形式打開,這里我們就可以使用
Toplevel來創(chuàng)建子窗體。
所以,detialgui的DetailWindow類所繼承的類應(yīng)該由Tk,換成Toplevel即可。
這時候又出現(xiàn)了一個小bug,pane與明細窗體發(fā)生了脫離,這是因為我們前面創(chuàng)建pane容器的時候忘記設(shè)置它的屬主了,這里添加一個構(gòu)建函數(shù)中添加一個self參數(shù)就可以了。現(xiàn)在點擊添加學(xué)生按鈕,就可以正常加載明細信息窗口了!

所以,出現(xiàn)這個問題的根源所在就是:使用Tk這個類實例化一個窗體,這個窗體是一個主窗體。已經(jīng)實例化了一個主窗體,再實例化一個是不可以的。所以第二個窗體必須用
Toplevel實例化。
三、實現(xiàn)三種狀態(tài)加載明細窗體
1. 點擊按鈕觸發(fā)
前面我們已經(jīng)實現(xiàn)了添加明細信息的功能,現(xiàn)在我們繼續(xù)實現(xiàn)修改明細信息的功能。我們新定義一個update_student()函數(shù):
def update_student(self):
self.load_detail_window()
然后在修改學(xué)生按鈕中添加command參數(shù)即可。
2. 雙擊表格觸發(fā)
我們想雙擊TreeView中的某一行表格,就能觸發(fā)查看明細信息的功能,首先我們定義一個查看明細信息的方法view_student(),由于雙擊是一個事件,所以在調(diào)用函數(shù)的時候需要添加一個參數(shù)event
def view_student(self,event):
self.load_detail_window()
然后在 setup_UI()函數(shù)的結(jié)尾處,添加觸發(fā)雙擊表格某一行的事件,寫法是:使用bind()方法,第一個參數(shù)一定要以Double開頭,體現(xiàn)是一個雙擊事件,第二個函數(shù)為對應(yīng)的觸發(fā)函數(shù)。
self.Tree.bind("<Double-1>",self.view_student)
效果演示:

四、呈現(xiàn)三種不同的狀態(tài)
以上三種觸發(fā)函數(shù)實現(xiàn)的都是同一種功能,然而我們想實現(xiàn)查看、添加、修改三種不同狀態(tài),那么怎樣才能體現(xiàn)差異化呢?
1.添加標(biāo)志參數(shù)
在實例化明細窗體的時候,添加一個標(biāo)志參數(shù)action_flag,我們通過區(qū)分action_flag參數(shù)的值,來確定以什么樣的模式進行加載。
這里我們規(guī)定:
- 當(dāng)action_flag = 1時,表示查看模式;
- 當(dāng)action_flag = 2時,表示添加模式;
- 當(dāng)action_flag = 3時,表示修改模式。
2. 修改窗體標(biāo)題
我們在明細窗體的構(gòu)造函數(shù)中,定義一個全局變量:
self.flag = action_flag
再定義一個修改窗體標(biāo)題的函數(shù):
def load_windows_flag(self):
if self.flag == 1:
self.Label_title.configure(text="==查看學(xué)生明細==")
elif self.flag == 2:
self.Label_title.configure(text="==新建學(xué)生明細==")
elif self.flag == 3:
self.Label_title.configure(text="==修改學(xué)生明細==")
創(chuàng)建好修改標(biāo)題的函數(shù)后,不要忘了在構(gòu)造函數(shù)中添加上:
self.load_windows_flag()
我們在主窗體函數(shù)中也定義一個全局變量,作為修改明細窗體標(biāo)題的標(biāo)志位:
self.action_flag = 0
然后在主窗體函數(shù)中,給load_detail_window()函數(shù)添加參數(shù)self.action_flag:
def load_detail_window(self):
detail_window = detailgui.DetailWindow(self.action_flag)
于此同時,add_student()、update_student()、view_student()三個方法也要做相應(yīng)的修改:
def add_student(self):
self.action_flag = 2
self.load_detail_window()
def update_student(self):
self.action_flag = 3
self.load_detail_window()
def view_student(self,event):
self.action_flag = 1
self.load_detail_window()
這樣三種不同的觸發(fā)事件,對應(yīng)的明細窗體標(biāo)題也發(fā)生了相應(yīng)的變化:

點擊添加學(xué)生按鈕,明細窗體標(biāo)題顯示“添加學(xué)生明細”;點擊修改學(xué)生按鈕,明細窗體標(biāo)題顯示“修改學(xué)生明細”;雙擊表格學(xué)生學(xué)生信息某一行,明細窗體標(biāo)題顯示“查看學(xué)生明細”。
最后
本節(jié)我們完成了明細窗體GUI的搭建,設(shè)置了明細窗體的查看模式、添加模式、和修改模式,并且實現(xiàn)了在不同觸發(fā)按鈕下同一個明細窗體顯示不同的標(biāo)題。希望小伙伴們不僅僅學(xué)習(xí)的同時,思考一下為什么要這么做?我們是怎么實現(xiàn)呈現(xiàn)三種不同窗體狀態(tài)的,并且結(jié)合著前面的文章自己動手練一練,所有的數(shù)據(jù)源、素材和源碼直接私信我,我發(fā)給你。
本節(jié)的明細窗體GUI的搭建已經(jīng)完成了,前期準(zhǔn)備工作已經(jīng)做好了,下一節(jié)我們就要正式將數(shù)據(jù)源學(xué)生信息填充進明細窗體中顯示出來,敬請期待吧~