GUI編程的布局就相當於小孩搭積木,每個積木塊放在那里、面積多大,也就是對大小和位置進行管理,二布局管理器就是負責各組件的大小和位置的管理。此外,當用戶調整了窗口的大小之后,布局管理器還會自動調整窗口中各組件的大小和位置。
Pack 布局管理器
如果使用Pack布局,那么這些組件是依次向后排列,排列方向即可是水平的,也可是垂直的。
簡單示范pack用法
import tkinter
window = tkinter.Tk()
window.title('Pack布局')
window.geometry('300x150')
## 用for循環添加三個標簽
for i in range(3):
L = tkinter.Label(window,text='第%d個Label' % (i + 1),bg='green')
L.pack()
window.mainloop()
運行效果:
pack()方法支持的選項:
- anchor:當可用空間大於組件所需求的大小是,該選項決定組件放置在容器的什么地方。該選項支持N(北,代表上)、E(東,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左下)、NE(東北,代表右上)、SW(西南,代表左下)、SE(東南,代表右下)、CENTER(中,默認值)

- expand:父容器增大時是否拉伸組件
- fill:設置組件是否沿水平或垂直方向填充。該選項支持None(不填充)、x(水平填充)、y(垂直填充)、BOTH(兩個方向都填充)
- ipadx:指定組件在x方向(水平)上的內部留白(padding)
- ipady:指定組件在y方向(垂直)上的內部留白(padding)
- padx:指定組件在x方向(水平)上與其它組件的間距
- pady:指定組件在y方向(垂直)上與其它組件的間距
- side:設置組件的排列順序,top(從上到下)、bottom(從下到上)、left(從左到右)、right(從右到左)
當程序界面復雜時,就需要多個容器(Frame)分開布局,然后再將Frame添加到窗口中。
import tkinter
window = tkinter.Tk()
window.title('Pack布局')
window.geometry('500x150')
## 創建第一個容器
fm1 = tkinter.Frame()
## 指定容器從左到右排列,沿水平和垂直方向填充,父容器增大時拉伸組件
fm1.pack(side='left', fill='both', expand='YES')
## 創建三個按鈕,放置在fm1容器中,從上到下排列,水平填充,父容器增大時拉伸組件
tkinter.Button(fm1, text='第一個').pack(side='top', fill='x' ,expand='YES')
tkinter.Button(fm1, text='第二個').pack(side='top', fill='x' ,expand='YES')
tkinter.Button(fm1, text='第三個').pack(side='top', fill='x' ,expand='YES')
## 創建第二個容器
fm2 = tkinter.Frame()
## 指定容器從左到右排列,水平上與其它組件的間距為10,父容器增大時拉伸組件
fm2.pack(side='left', padx=10, expand='YES')
## 創建三個按鈕,放置在fm2容器中,從左到右排列,垂直填充,父容器增大時拉伸組件
tkinter.Button(fm2, text='第一個').pack(side='right', fill='y' ,expand='YES')
tkinter.Button(fm2, text='第二個').pack(side='right', fill='y' ,expand='YES')
tkinter.Button(fm2, text='第三個').pack(side='right', fill='y' ,expand='YES')
## 創建第三個容器
fm3 = tkinter.Frame()
## 指定容器從右到左排列,水平上與其它組件的間距為10,兩個方向都填充,父容器增大時拉伸組件
fm3.pack(side='right',padx=10, fill='both', expand='YES')
## 創建三個按鈕,放置在fm3容器中,從下到上排列,垂直填充,父容器增大時拉伸組件
tkinter.Button(fm3, text='第一個').pack(side='bottom', fill='y' ,expand='YES')
tkinter.Button(fm3, text='第二個').pack(side='bottom', fill='y' ,expand='YES')
tkinter.Button(fm3, text='第三個').pack(side='bottom', fill='y' ,expand='YES')
window.mainloop()
運行效果:
通過上面不難發現,Pack布局還是非常靈活的,它完全可以實現很復雜的用戶界面。這里有一個界面分解需要說明:無論多么復雜和古怪的界面,其實都可以分解為水平排序或垂直排序,而Pack布局即可實現水平排序,也可以實現垂直排序,然后在通過多個容器進行組合,實現復雜的界面。
Grid布局管理器
Grid是把組件空間分解成一個網格進行維護,即按照行、列的方式排列組件,組件位置由其所在的行號和列號決定:行號相同而列號不同的的幾個組件會被依次進行上下排列,列號相同而行號不同的幾個組件會被依次進行左右排序
Grid()方法支持的選項:
- column:指定將組件放入哪列。第一列的索引為0
- columnspan:指定組件橫跨多少列
- row:指定組件放入哪行,第一行的索引為0
- rowspan:指定組件橫跨多少行
- sticky:類似於pack()方法的anchor選項,支持N(北,代表上)、E(東,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左下)、NE(東北,代表右上)、SW(西南,代表左下)、SE(東南,代表右下)、CENTER(中,默認值)
grid布局管理器示范(計算器界面)
import tkinter
window = tkinter.Tk()
window.title('計算器')
E = tkinter.Entry(window,font=('Courier New',24),width=23)
## 放置在第一行,橫跨四列
E.grid(row=0,columnspan=4)
keys = (('7','8','9','/'),('4','5','6','*'),('1','2','3','-'),('0','.','+','='))
## 循環方式放置按鈕
for i in range(len(keys)):
for j in range(len(keys[i])):
B = tkinter.Button(window,text=keys[i][j],font=('Verdana',20),width=6)
B.grid(row=i+1,column=j)
window.mainloop()
運行效果:

Place布局管理器
Place布局就是其他GUI編程中的“絕對布局”,這種布局方式要求程序指定每個組件的絕對位置或相對於其他組件的位置
place()方法支持的選項:
- x:指定組件的X坐標,x為0代表最左邊
- y:指定組件的Y坐標,y為0代表最上邊
- relx:以百分比指定組件的X坐標,父容器總寬度為1,該值在0.0~1.0之間,其中0.0代表最左邊,0.5代表中間,1.0代表最右邊
- rely:以百分比指定組件的Y坐標,父容器總高度為1,該值在0.0~1.0之間,其中0.0代表最上邊,0.5代表中間,1.0代表最下邊
- width:指定組件的寬度,以pixel為單位
- height:指定組件的高度,以pixel為單位
- relwidth:以百分比指定組件寬度,父容器總寬度為1,該值在0.0~1.0之間,其中1.0代表父容器的全部寬度,0.5代表父容器一半寬度
- relheight:以百分比指定組件高度,父容器總高度為1,該值在0.0~1.0之間,其中1.0代表父容器的全部高度,0.5代表父容器一半高度
- bordermode:是否計算組件的寬度、高度,inside為不計算,outside為計算
- anchor:當可用空間大於組件所需求的大小是,該選項決定組件放置在容器的什么地方。該選項支持N(北,代表上)、E(東,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左下)、NE(東北,代表右上)、SW(西南,代表左下)、SE(東南,代表右下)、CENTER(中,默認值)
簡單演示一下吧:
import tkinter
window = tkinter.Tk()
window.title('Place布局')
window.geometry('500x300')
tkinter.Label(window,text='Place',font=('Arial',20)).place(x=50,y=100)
window.mainloop()
運行效果:

(番外篇)事件綁定
簡單的事件處理可以通過command選項來綁定一個函數或方法,當用戶單擊指定按鈕時,command指定的函數或方法就會被觸發。但程序很多情況需要其他事件(比如鼠標懸停,鼠標雙擊等)
為了彌補這種不足,Python提供了更靈活的事件綁定方式,所有Widget組件都提供了一個bind()方法,可以為任意組件的事件提供了處理方式
import tkinter
## 導入webbrowser模塊
import webbrowser
window = tkinter.Tk()
window.title('簡單綁定')
window.geometry('500x300')
## 定義點擊按鈕觸發的函數,event為事件的響應
def one(event):
webbrowser.open('https://www.baidu.com')
def two(event):
webbrowser.open('https://www.taobao.com')
B = tkinter.Button(window,text='單擊百度,雙擊淘寶')
B.pack()
## 按鈕單擊和one函數綁定(1為鼠標左鍵,2為鼠標中間(滾輪點擊),3為鼠標右鍵)
B.bind('<Button-1>',one)
## 按鈕雙擊和two函數綁定(1為鼠標左鍵,2為鼠標中間(滾輪點擊),3為鼠標右鍵)
B.bind('<Double-1>',two)
window.mainloop()
運行效果:
Tkinter支持的各種鼠標,鍵盤事件介紹
-
:鼠標單擊事件。1為鼠標左鍵,2為鼠標中間(滾輪點擊),3為鼠標右鍵,4為滾輪上滾(LInux),5為滾輪下滾(Linux) -
:鼠標按住移動事件。1為鼠標左鍵按住移動,2為鼠標中鍵按住移動,3為鼠標右鍵按住移動 -
:鼠標按鍵被釋放事件。1為鼠標左鍵被釋放,2為鼠標中鍵被釋放,3為鼠標右鍵被釋放 -
:鼠標雙擊事件。1為鼠標左鍵,2為鼠標中間(滾輪點擊),3為鼠標右鍵,4為滾輪上滾(LInux),5為滾輪下滾(Linux) -
:鼠標進入組件事件。鼠標懸停組件上方,什么按鍵也不點情況下 -
:鼠標移出組件事件 -
:組件及其子組件獲得焦點 -
:組件及其子組件失去焦點 -
:按下回車鍵事件。實際上所有鍵都綁定了事件, -
- a
-
-
import tkinter
window = tkinter.Tk()
window.title('鼠標事件')
window.geometry('500x130')
## 定義觸發函數
def motion(event):
鼠標位置發送到La標簽中
La['text'] = '鼠標移動到:(%s %s)' % (event.x,event.y)
def press_motion(event):
La['text'] = '按住鼠標的移動位置:(%s %s)' % (event.x, event.y)
L = tkinter.Label(window,bg='lightgreen',font=('Times',20),width=40,height=3)
L.bind('<Motion>',motion)
L.bind('<B1-Motion>',press_motion)
L.pack()
La = tkinter.Label(window,bg='white',font=('Courier New',20),width=38,height=1)
La.pack()
window.mainloop()
運行效果:

最后升級前面的計算器,使計算器可以真正計算
import tkinter
window = tkinter.Tk()
window.title('計算器')
Text = tkinter.StringVar()
## 定義變量,存放運算符前面的數
Num =None
## 定義按鈕觸發函數
def click(event):
## 引用外面定義的函數
global Num
## 判斷按鈕值是否是數字
if event.widget['text'] in ('1','2','3','4','5','6','7','8','9','0','.'):
## Text同步按鈕值
Text.set(Text.get() + event.widget['text'])
## 判斷按鈕值是否為運算符
elif event.widget['text'] in ('/','*','-','+'):
## 判斷Num變量是否為空
if Num is None:
## 把Text最新的值和運算符放到Num變量中
Num = Text.get() + event.widget['text']
## 清空Text
Text.set('')
## 否則就先把Num加Text組合的值計算出來
else:
Num = str(eval(Num + Text.get()))
Text.set('')
## 判斷按鈕值是否為等號
elif event.widget['text'] == '=':
## 這里加了異常處理,否則不該點等號時候點了等號,還報錯,看着煩
try:
## Num加Text組合的值計算出來交給Text
Text.set(str(eval(Num + Text.get())))
except:
pass
## 定義雙擊等號觸發函數(這里沒有歸零,也不想在加個按鈕了,就把雙擊等於定義為歸零了)
def Clean(event):
global Num
Text.set('')
Num = None
## 創建文本框,內容為Text值
E = tkinter.Entry(window,textvariable=Text,font=('Courier New',24),width=23)
E.grid(row=0,columnspan=4)
keys = (('7','8','9','/'),('4','5','6','*'),('1','2','3','-'),('0','.','+','='))
for i in range(len(keys)):
for j in range(len(keys[i])):
B = tkinter.Button(window,text=keys[i][j],font=('Verdana',20),width=6)
## 按鈕單擊綁定click函數
B.bind('<Button-1>',click)
B.grid(row=i+1,column=j)
## 判斷按鈕值是否為等號
if B['text'] == '=':
## 按鈕雙擊綁定Clean函數
B.bind('<Double-1>',Clean)
window.mainloop()
