6.事件和綁定
正如前幾章提到的,Tkinter應用程序大部分事件都在事件循環中(通過mainloop方法進入事件循環)
事件來自於多個來源,比如用戶的鍵盤的輸入和鼠標操作,和window manager的重繪事件(大多數情況下不是有用戶直接調用的)
Tkinter提供強大的機制讓您自己處理事件,每個組件你都可以爲各種事件綁定python的函數和方法
widget.bind(event, handler)
如果組件中發生了與event描述匹配的事件,將調用handler指定的處理程序,
例子,在窗口中捕獲點擊次數
from Tkinter import *
root = Tk()
def callback(event):
print "clicked at", event.x, event.y
frame = Frame(root, width=100, height=100)
frame.bind("<Button-1>", callback)
frame.pack()
root.mainloop()
在這裏例子中,我們用bind方法把frame的鼠標左鍵點擊事件綁定到callback函數
當控件擁有鍵盤焦點後,鍵盤事件將發送到對應的控件,你可以使用focus_set
方法來獲得鍵盤焦點,獲取鍵盤事件的代碼:
from Tkinter import *
root = Tk()
def key(event):
print "pressed", repr(event.char)
def callback(event):
frame.focus_set()
print "clicked at", event.x, event.y
frame = Frame(root, width=100, height=100)
frame.bind("<Key>", key)
frame.bind("<Button-1>", callback)
frame.pack()
root.mainloop()
事件
事件用格式化的字符串來表示
<modifier-type-detail>
type區域是事件說明符中最重要的部分,它說明我們想綁定哪一類的事件
例如Button\Key或者如進入、配置之類的窗口管理事件。Modifier和detail區域用於額外的信息
在許多情況下可以省略,還有各種方法來簡化事件字符串,例如爲了匹配鍵盤鍵,您可以省略
尖括號,只需使用鍵,當然要包含在尖括號內讓我們來看看最常見的事件格式
事件格式
<Button-1>鼠標左鍵按下事件,1表示左鍵;2-鼠標中鍵;3-鼠標右鍵
當您在組件上按下鼠標左鍵,隨後的鼠標事件(移動、釋放)就會發送到當前組件,即使
鼠標移動到當前組件之外,鼠標指針的的當前位置在傳遞給回調函數的event對象的x,y中
<Button-1>、<ButtonPress-1>、<1>都是同義詞
<B1-Motion>按住鼠標左鍵移動鼠標,鼠標指針的當前位置在傳遞給回調函數的event中的x\y提供
<ButtonRelease-1>鼠標左鍵被釋放
<Double-Button-1>鼠標左鍵被雙擊,你可以用Double表示雙擊,還可以用Triple表示三擊
注意,如果你綁定了<Button-1>和<Double-Button-1>,那麼2個事件都被觸發
<Enter>當鼠標進入組件區域被觸發
<Leave>當鼠標離開組件區域被觸發
<FocusIn>當鍵盤焦點移到這個組件,或者移到這個組件的子組件
<FocusOut>失去焦點
<Return>用戶按下回車鍵,你可以綁定鍵盤上幾乎所有鍵,Shift_L(左邊的shift鍵)
Delete\F1\F5\Num_Lock都可以
<Key>用戶按下任意鍵
<Shift-Up>用戶按下shift鍵同時按下向上鍵觸發,也可以用Alt,shift,control
frame1.bind("<Shift M>",shiftm) 當同時按下shift 和m 後回調函數shiftm
<Configure>如果組件的尺寸變化那麼被觸發
Event對象
event對象是標準的Python對象實例,有許多屬性
事件屬性
widget:產生這個事件的組件,這個一個合法Tkinter組件實例而不是名字,所有的事件都有這個屬性
x,y:當前鼠標的位置,像素爲單位
x_root,y_root:當前鼠標相對於上層框架的位置
char:字符碼 只有鍵盤事件纔有,string類型
keysym:鍵盤符號只有鍵盤事件纔有
keycode:鍵盤代碼
num:鼠標的編號,左鍵1,中鍵2,右鍵3....
width,height:組件新尺寸,只有Configure 事件纔有,像素
type:事件類型
實例和類的綁定
以上的例子中我們用的都是在實例上使用bind方法,這意味着這樣只能bind在一個組件上,
如果我們創建一個新的組件,他們不會繼承這些綁定關係。
不過Tkinter也提供了類級別和應用級別的bind,實際上,你可以使用以下級別的binding
1.組件實例
2.組件的頂層窗體(Toplevel 或者 root)
3.組件類,用bind_class方法
4.整個應用,用bind_all方法
比如,你可以用bind_all來綁定F1按鈕的點擊,這樣你能在這個應用的如何地方點擊都可以彈出幫助框
但如果同一個鍵你在多處綁定了怎麼辦?
首先,在以上4個層次之內,Tkinter選擇最接近匹配的方式。比如爲<Key>
和<Return>事件創建實例綁定,那麼只有按下Enter鍵之後纔會調用<Return>的回調函數
但是,如果你如果在以上4個層次間,比如你同時向toplevel組件添加<Return>綁定,那麼將調用2個綁定
Tkinter首先調用實例級別的最佳綁定,最後在應用程序級別調用最佳可用綁定,因此,
在極端情況下,單個事件可以調用4個事件處理程序。
常見的混亂原因是當您嘗試使用綁定來覆蓋標準組件的默認行爲。例如,假設你想在
文本框內禁止輸入回車鍵,這樣用戶就無法輸入多行數據,也行你會用下面的小伎倆
def ignore(event):
pass
text.bind("<Return>",ignore)
或者,你喜歡一行的簡潔代碼
text.bind("<Return>",lamdba e:None)
不幸的是,新的一行依然會插入,因爲,以上的綁定僅僅應用在應用級別,
而標準的行爲依然有類級別的綁定實現了。
你可以使用bind_class方法來改變類級別的綁定,但這將更改應用程序中所有文本組件的行爲。下面是比較合理的解決辦法
def ignore(event):
return "break"
text.bind("<Return>", ignore)
順便說一句,如果你真的想改變所有文本組件的默認行爲,你可以用以下bind_class方法
top.bind_class("Text", "<Return>", lambda e: None)
真的不建議這麼做,不要改變組件的默認行爲。
協議
除了事件綁定,Tkinter還提供了協議處理的機制,這裏的協議指的是應用程序和windows manager之間的互動
最常見的是WM_DELETE_WINDOW,用於定義當用戶使用窗口管理器顯式關閉窗口是的事件。
你可以用protocol方法來安裝這個協議的回調函數(這個組件必須是root或者Toplevel組件)
widget.protocol("WM_DELETE_WINDOW", handler)
一旦你註冊了自己的處理函數,Tkinter將不再自動的關閉程序,比如下面這個例子
Capturing destroy events
from Tkinter import *
import tkMessageBox
def callback():
if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
root = Tk()
root.protocol("WM_DELETE_WINDOW", callback)
root.mainloop()
注意,即使你沒有在頂層窗口註冊WM_DELETE_WINDOW的處理程序,窗口還是會被銷燬的。最好還是自己註冊一個處理程序
top = Toplevel(...)#確保窗口小部件實例被刪除
top.protocol(“WM_DELETE_WINDOW”,top.destroy)