Python升級之路( Lv21 ) GUI圖形界面編程之布局&事件管理
今天我們將深入學(xué)習(xí)GUI圖形界面編程tkinter, 了解布局管理和事件管理。
今日冒險(xiǎn)片段1
隨著冒險(xiǎn)的深入, 了不起來到了天帷巨獸的頸部地區(qū). 聽奧菲利亞講, 這里位于天帷巨獸的頸部區(qū)域, 名為煉獄. 死后的信徒們都會被送到這里. 久而久之這里便樹木林立, 人煙罕至, 暗無天日. 因此誕生了諸如僵尸, 夜叉一樣的怪物. 好在現(xiàn)在的了不起現(xiàn)在也是有兩把刷子的, 在其護(hù)送下, 路上的小怪雖然不少, 但是沒有影響他們的趕路計(jì)劃. 但是在走至一半時(shí), 一個(gè)黑袍祭祀阻擋到了他們前進(jìn)的去處. 奧菲利亞震驚的張開紅唇說到: 這個(gè)難道是傳說中的暗之傳道士嗎?
grid布局管理器
grid 表格布局,采用表格結(jié)構(gòu)組織組件. 子組件的位置由行和列的單元格來確定,并且可以跨行和跨列,從而實(shí)現(xiàn)復(fù)雜的布局。
grid()方法提供的屬性參數(shù)選項(xiàng)如下:
選項(xiàng) | 說明 | 取值范圍 |
column | 單元格的列號 | 從 0 開始的正整數(shù) |
columnspan | 跨列,跨越的列數(shù) | 正整數(shù) |
row | 單元格的行號 從 | 0 開始的正整數(shù) |
rowspan | 跨行,跨越的行數(shù) | 正整數(shù) |
ipadx ipady | 設(shè)置子組件之間的間隔,x 方向或者 y 方向, 默認(rèn)單位為像素 | 非負(fù)浮點(diǎn)數(shù),默認(rèn) 0.0 |
padx pady | 與之并列的組件之間的間隔,x 方向或者 y 方 向,默認(rèn)單位是像素 | 非負(fù)浮點(diǎn)數(shù),默認(rèn) 0.0 |
sticky | 組件緊貼所在單元格的某一角,對應(yīng)于東南西 北中以及 4 個(gè)角 | “n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默認(rèn)) |
實(shí)操代碼:
from tkinter import *
class Application(Frame):
def __init__(self, master=None):
super(Application, self).__init__(master)
self.pack()
self.createWidget()
def createWidget(self):
self.label1 = Label(self, text="用戶名")
self.label1.grid(row=0, column=0)
self.entry1 = Entry(self)
self.entry1.grid(row=0, column=1)
Label(self, text="用戶名為手機(jī)號").grid(row=0, column=2)
Label(self, text="密碼").grid(row=1, column=0)
Entry(self, show="*").grid(row=1, column=1)
Button(self, text="登錄").grid(row=2, column=1, sticky=SW)
Button(self, text="取消").grid(row=2, column=2, sticky=S)
if __name__ == '__main__':
root = Tk()
root.geometry("400x90+200+300")
app = Application(master=root)
root.mainloop()
pack 布局管理器
pack 按照組件的創(chuàng)建順序?qū)⒆咏M件添加到父組件中,按照垂直或者水平的方向自然排布 如果不指定任何選項(xiàng),默認(rèn)在父組件中自頂向下垂直添加組件. pack 是代碼量最少,最簡單, 最常用的一種,可以用于快速生成界面。
pack()方法提供的方法參數(shù)選項(xiàng)
如上列出了 pack 布局所有的屬性,但是不需要挨個(gè)熟悉,了解基本的即可 pack 適用于簡單的垂直或水平排布,如果需要復(fù)雜的布局可以使用 grid 或 place。
選項(xiàng) | 說明 | 取值范圍 |
expand | 當(dāng)值為“yes”時(shí),side 選項(xiàng)無效。組件顯示在父配件中心位置;若 fill 選項(xiàng)為”both”,則填充父組件的剩余空間 | “yes”, 自然數(shù),”no”, 0 (默認(rèn)值”no”或 0) |
fill | 填充 x(y)方向上的空間,當(dāng)屬性 side=”top”或” bottom”時(shí),填充 x 方向;當(dāng)屬性 side=”left”或” right”時(shí),填充”y”方向;當(dāng) expand 選項(xiàng)為”yes” 時(shí),填充父組件的剩余空間 | “x”, “y”, “both”, “none” (默認(rèn)值為 none) |
ipadx ipady | 設(shè)置子組件之間的間隔,x 方向或者 y 方向,默認(rèn)單位為像素 | 非負(fù)浮點(diǎn)數(shù),默認(rèn) 0.0 |
padx pady | 與之并列的組件之間的間隔,x 方向或者 y 方向,默認(rèn)單位是像素 | 非負(fù)浮點(diǎn)數(shù),默認(rèn) 0.0 |
side | 定義??吭诟附M件的哪一邊上 | “ top ” , “ bottom ” , “l(fā)eft”, “right” (默認(rèn)為”top”) |
before | 將本組件于所選組建對象之前 pack,類似于先創(chuàng)建本組件再創(chuàng)建選定組件 | 已經(jīng) pack 后的組件對象 |
after | 將本組件于所選組建對象之后 pack,類似于先創(chuàng)建選定組件再本組件 | 已經(jīng) pack 后的組件對象 |
in_ | 將本組件作為所選組建對象的子組件,類似于指定本組件的 master 為選定組件 | 已經(jīng) pack 后的組件對象 |
anchor | 對齊方式,左對齊”w”,右對齊”e”,頂對齊”n”, 底對齊 | ”s” “n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默認(rèn)) |
實(shí)操代碼:
from tkinter import *
root = Tk()
root.geometry("700x220")
# Frame是個(gè)矩形區(qū)域, 用來存放其他組件
f1 = Frame(root)
f1.pack()
f2 = Frame(root)
f2.pack()
# 聲明一個(gè)元組
btnText = ("流行風(fēng)", "中國風(fēng)", "日本風(fēng)", "重金屬", "輕音樂")
# 遍歷元組, 為Button命令
for txt in btnText:
Button(f1, text=txt).pack(side="left", padx="10")
# 遍歷一個(gè)range序列
for i in range(1, 20):
Button(f2, width=5, height=10, bg="black" if i % 2 == 0 else "white").pack(side="left")
root.mainloop()
place 布局管理器
place 布局管理器可以通過坐標(biāo)精確控制組件的位置,適用于一些布局更加靈活的場景。
place()方法的屬性參數(shù)選項(xiàng):
選項(xiàng) | 說明 | 取值范圍 |
x,y | 組件左上角的絕對 坐標(biāo)(相對于窗口) | 非負(fù)整數(shù) x 和 y 選項(xiàng)用于設(shè)置偏移(像素),如果同時(shí)設(shè)置 relx(rely) 和 x(y),那么 place 將優(yōu)先計(jì)算 relx 和 rely,然后再實(shí)現(xiàn) x 和 y 指定的偏移值 |
relx rely | 組件左上角的坐標(biāo) (相對于父容器) | relx 是相對父組件的位置。0 是最左邊,0.5 是正中間,1 是最右邊;rely 是相對父組件的位置。0 是最上邊,0.5 是正中間,1 是最下邊; |
width, height | 組件的寬度和高度 | 非負(fù)整數(shù) |
relwidth, relheight | 組件的寬度和高度 (相對于父容器) | 與 relx、rely 取值類似,但是相對于父組件的尺寸 |
anchor | 對齊方式,左對齊” w”,右對齊”e”, 頂對齊”n”,底對 齊”s” | “n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默認(rèn)) |
事件管理
一個(gè) GUI 應(yīng)用整個(gè)生命周期都處在一個(gè)消息循環(huán) (event loop) 中。它等待事件的發(fā)生,并作出相應(yīng)的處理. Tkinter 提供了用以處理相關(guān)事件的機(jī)制. 處理函數(shù)可被綁定給各個(gè)控件的各種事件. widget.bind(event, handler) 如果相關(guān)事件發(fā)生, handler 函數(shù)會被觸發(fā), 事件對象 event 會傳遞給 handler 函數(shù).
今日冒險(xiǎn)片段2
原來, 在大主教, 大祭司和教主之間. 還有四個(gè)特殊的存在, 那就是黎明, 黃昏, 暗, 光之傳道師. 由于他們四個(gè)比較特殊, 因此直接受教主管轄, 但自從天帷巨獸上面的人被控制之后, 他們四位便杳無音信. 因此. 黎明和黃昏已在之前的冒險(xiǎn)中被了不起解救. 但是相較于之前兩位, 安之傳道師明顯要更強(qiáng)一些. 于是了不起趕忙把武器切換成光屬性. 只見自己的長劍在黑暗中散發(fā)著點(diǎn)點(diǎn)光芒. 正在了不起握緊武器準(zhǔn)備進(jìn)攻的時(shí)候. 對方先開口說話了: "尊敬的教主大人, 暗之傳道師-扎西特參上!". 原來, 暗之傳道師由于本人對黑暗環(huán)境的契合度極高, 因此僥幸躲過了精神控制. 但是由于他是唯一一位在此地?cái)[脫控制的人, 因此每天都會同被控制的怪物做斗爭. 好在遇到了我們, 他才能將緊繃的心態(tài)方式下來. 就這樣了不起的小隊(duì)便又多了一人.
鼠標(biāo)和鍵盤事件
代碼 | 說明 |
| 鼠標(biāo)左鍵按下。2 表示右鍵,3 表示中鍵; |
| 鼠標(biāo)左鍵釋放 |
| 按住鼠標(biāo)左鍵移動(dòng) |
| 雙擊左鍵 |
| 鼠標(biāo)指針進(jìn)入某一組件區(qū)域 |
| 鼠標(biāo)指針離開某一組件區(qū)域 |
| 滾動(dòng)滾輪; |
| 按下 a 鍵,a 可用其他鍵替代 |
| 釋放 a 鍵。 |
| 按下 A 鍵(大寫的 A) |
| 同時(shí)按下 alt 和 a;alt 可用 ctrl 和 shift 替代 |
| 快速按兩下 a |
| CTRL 和 V 鍵被同時(shí)按下,V 可以換成其它鍵位 |
event 對象常用屬性:
名稱 | 說明 |
char | 按鍵字符,僅對鍵盤事件有效 |
keycode | 按鍵編碼,僅對鍵盤事件有效 |
keysym | 按鍵名稱,僅對鍵盤事件有效 比如按下空格鍵:鍵的 char:鍵的 keycode, 32 鍵的 keysym:space ; 再比如按下 a 鍵:鍵的 char:a 鍵的 keycode:65 鍵的 keysym:a |
num | 鼠標(biāo)按鍵,僅對鼠標(biāo)事件有效 |
type | 所觸發(fā)的事件類型 |
widget | 引起事件的組件 |
width,height | 組件改變后的大小,僅 Configure 有效 |
x,y | 鼠標(biāo)當(dāng)前位置,相對于父容器 |
x_root,y_root | 鼠標(biāo)當(dāng)前位置,相對于整個(gè)屏幕 |
實(shí)操代碼:
from tkinter import *
root = Tk();
root.geometry("530x300")
c1 = Canvas(root, width=200, height=200, bg="green")
c1.pack()
def mouseTest(event):
"""鼠標(biāo)事件測試"""
print("鼠標(biāo)左鍵單擊位置(相對于父容器):{0},{1}".format(event.x, event.y))
print("鼠標(biāo)左鍵單擊位置(相對于屏幕): {0},{1}".format(event.x_root, event.y_root))
print("事件綁定的組件:{0}".format(event.widget))
def testDrag(event):
c1.create_oval(event.x, event.y, event.x + 1, event.y + 1)
def keyboardTest(event):
"""
char 按鍵字符,僅對鍵盤事件有效
keycode 按鍵編碼,僅對鍵盤事件有效
keysym 按鍵名稱,僅對鍵盤事件有效 比如按下空格鍵: 鍵的 char: 鍵的 keycode:32 鍵的 keysym:space
num 鼠標(biāo)按鍵,僅對鼠標(biāo)事件有效
type 所觸發(fā)的事件類型
widget 引起事件的組件
width,height 組件改變后的大小,僅 Configure 有效
x,y 鼠標(biāo)當(dāng)前位置,相對于父容器
x_root,y_root 鼠標(biāo)當(dāng)前位置,相對于整個(gè)屏幕
"""
print("鍵的 keycode:{0},鍵的 char:{1},鍵的 keysym:{2}".format(event.keycode, event.char, event.keysym))
def press_a_test(event):
print("press a")
def release_a_test(event):
print("release a")
c1.bind("<Button-1>", mouseTest) # 鼠標(biāo)左鍵按下
c1.bind("<B1-Motion>", testDrag) # 按住鼠標(biāo)左鍵移動(dòng)
root.bind("<KeyPress>", keyboardTest) # 鍵盤按壓事件
root.bind("<KeyPress-a>", press_a_test) # 按下 a 鍵,a 可用其他鍵替代
root.bind("<KeyRelease-a>", release_a_test) # # 釋放 a 鍵
root.mainloop()
多種事件綁定方式匯總
(1) 組件對象的綁定:
- 通過 command 屬性綁定(適合簡單不需獲取 event 對象)Button(root,text=”登錄”,command=login)
- 通過 bind()方法綁定(適合需要獲取 event 對象)c1 = Canvas(); c1.bind(“<Button-1>”,drawLine)
(2) 組件類的綁定調(diào)用對象的 bind_class 函數(shù),將該組件類所有的組件綁定事件:w.bind_class(“Widget”,”event”,eventhanler) 比如:btn01.bind_class(“Button”,”<Button-1>”,func)
實(shí)操代碼:
from tkinter import Tk, Button
root = Tk()
root.geometry("270x30")
def mouseTest1(event):
print("bind()方式綁定,可以獲取 event 對象")
print(event.widget)
def mouseTest2(a, b):
print("a={0},b={1}".format(a, b))
print("command 方式綁定,不能直接獲取 event 對象")
def mouseTest3(event):
print("右鍵單擊事件,綁定給所有按鈕啦?。?)
print(event.widget)
b1 = Button(root, text="測試 bind()綁定")
b1.pack(side="left") # bind 方式綁定事件
b1.bind("<Button-1>", mouseTest1)
# command 屬性直接綁定事件
b2 = Button(root, text="測試 command2", command=lambda: mouseTest2("實(shí)參1傳入", "實(shí)參2傳入"))
b2.pack(side="left")
# 給所有 Button 按鈕都綁定右鍵單擊事件<Button-2>
b1.bind_class("Button", "<Button-2>", mouseTest3)
root.mainloop()
今日冒險(xiǎn)片段3
在扎西特的指引下, 了不起很快的通過了煉獄的大部分區(qū)域, 直到來到最核心的地方. 這里被領(lǐng)主夜叉王守著, 他們無法從側(cè)面突破, 只能選擇從正面突破. 在三位傳道師和一個(gè)教主的增幅下, 了不起開始與夜叉王開始戰(zhàn)斗. 雖然享受著幾人的增幅與武器的屬性克制, 但是雙方仍不相上下, 由此可見夜叉王的強(qiáng)大. 不一會, 了不起身上也有多出掛彩. 好在有奧菲利亞的治愈魔法, 這才讓他不那么快敗下陣來. ??在戰(zhàn)斗僵持不下時(shí), 扎西特開始聯(lián)合其他兩位傳道師釋放他們教的特有技能, 聯(lián)合契約召喚-精靈王伊莎貝拉的殘念(因?yàn)樯倭艘粋€(gè)人). 在精靈王和了不起的強(qiáng)烈攻勢下, 夜叉王終于敗下陣來. 在感慨傳道師力量的強(qiáng)大之時(shí), 了不起也順利的晉升到了lv22.