這一篇文章介紹Tk的事件處理方法。預設Tk已經為許多視窗元件加上了捕捉事件的選項。例如:按鈕的-command選項可以用來捕捉滑鼠的點擊(Click)事件,一但捕捉到事件,指定給-command選項後面的程式片段就會被執行。雖然Tk已經在各種視窗元件中提供了足夠的事件處理的選項,但有時候我們還是會想要處理更細節一點的事件,例如:希望滑鼠滑過某個視窗元件時做某些事,或是某個視窗元件大小被改變、顯示及隱藏時做某些事,此時我們就需要使用bind命令來幫忙,才可以完成這些較細節的事件動作。
36.1 bind命令及鍵盤、滑鼠事件
TK的bind命令可以用來設定視窗元件綁定某些事件,它的語法如下:bind tag ?sequence? ?script?
上面tag的位置可以放視窗元件的path或是某個視窗元件的類別,sequence放需要綁定的事件描述,最後的script放事件發生要執行的程式碼。以下是一個簡單的例子,我讓標籤元件,綁定「滑鼠壓下」、「滑鼠滑過」及「滑鼠放開」的事件,如果你想要做類似VB或是Visual Tcl那樣在表單裡拖放元件的功能,就會需要使用這些事件。
這個例子執行以後會在螢幕上顯示一個標籤元件,你可以試試看用滑鼠左鍵,抓住它然後移動,雖然標籤並不會真的被移動,但-text選項上會反應出正在發生的事件,這可以幫助我們查看事件被觸發的時機。
在上面的例子中ButtonPress-1及ButtonRelease-1的「1」用來表示為滑鼠左鍵,你也可以把它換成2或3,來表示滑鼠的右鍵或中鍵。注意哦!!在不同的作業系統裡2及3與滑鼠鍵的對應關係不見得是一樣的,例如在Linux下2表示為中間,但在Mac下2是表示右鍵。另外,若ButtonPress及ButtonRelease後面不加任何數字,那表示任何一個滑鼠鍵被壓下或是放開都會觸發事件。
下以是另一個例子,在這個例子中我把Ctrl+滑鼠左鍵點擊設定為要綁定的條件。
這個例子要說明Button-X表示為某個滑鼠按鈕的Click事件,當然X也可以換成2、3或是其它數字。另一個要說明的是每一個事件的描述像Button、ButtonPress的前面都可以再加上Control、Shift、Meta...等更細節的修飾。以下是描述事件的完整語法:
<modifier-modifier-type-detail>
第一個modifier欄位你可以放Double、Triple、Quadruple、Control、Shift、Alt或Meta,用來表示按鈕重覆的次數或鍵盤上對應的按鍵。第二個modifier可以放Control、Shift、Alt及Meta等描述,而每一個Control、Shift、Alt或Meta這樣的描述還可以分左右邊,例如:Alt_L表示左邊的Alt鍵,Meta_R表示右邊的Meta鍵。第3個欄位用來指定像Button或是Key的事件的類型。最後的detail欄位用來描述各種事件類型的細節。注意哦!! 在大部份的情況下,很多事件是不用modifier欄位及detail的。以下是一些事件描述的例子:
- Enter : 滑鼠滑進某個視窗元件
- Leave : 滑鼠滑出某個視窗元件
- Motion : 滑鼠在某個視窗元件上滑動
- FocusIn : 游標停駐點進入某個視窗元件
- FocusOut : 游標停駐點由某個視窗元件移出
- Button-X : 某個滑鼠鍵的點擊事件
- ButtonPress-X : 某個滑鼠鍵的下壓事件
- ButtonRelease-X : 某個滑鼠鍵的放開事件
- Double-Button-X : 某個滑鼠鍵的雙擊事件
- Control-Button-X : Ctrl+某個滑鼠鍵的點擊事件
- Shift-Button-X : Shift+某個滑鼠鍵的點擊事件
- Alt-Button-X : Alt+某個滑鼠鍵的點擊事件
- Double-Control-Button-X : Ctrl+某個滑鼠鍵的雙點擊事件
以下是另一個可以查看滑鼠事件的例子,請試試看讓你的滑鼠及游標停駐點在下面的兩個文字方塊間移動。
□ 鍵盤事件
鍵盤事件的處理方法和滑鼠事件是大同小異的,請看以下的範例:這個例子幫文字方塊.txt綁定了4個跟按鍵「a」相關的事件,當然你也可以把「a」換成鍵盤上的其它按鍵。
36.2 事件的代換符號
回顧一下bind的語法,如下:bind tag ?sequence? ?script?
我們已經知道script是事件觸發時要執行的程式片斷,而事實上在這些程式片段裡,我們還可以加上一些用%開頭再加一個字元所組成殊特符號,這些特殊的符號會被bind命令代換成有特殊意義的數值,例如:
在上面的例子中,事件觸發的程式片段裡,%W會被代換為綁定這個事件本身的視窗元件,%x及%y會被代換為滑鼠點擊在視窗元件上的x,y座標。所以當你在.lbl上點擊滑鼠左點,就會有類似下方的輸出。
元件:.lbl , 點擊的x座標: 324 , y 座標: 30 元件:.lbl , 點擊的x座標: 246 , y 座標: 195 元件:.lbl , 點擊的x座標: 282 , y 座標: 192
另一個最常用的代換例子是用來顯示右鍵功能表,例如:
這個例子把.txt綁了滑鼠右鍵的事件,並且在事件的程式片段中用%X及%Y代換出滑鼠點擊的位置,然後在這個位置顯示右鍵功能表。注意哦!! %X、%Y和%x、%y的差別在於大寫的XY是以螢幕的左上角為原點,而小寫的xy是以視窗元件的左上角為原點。以下是一些常用的代換符號,更多詳細的代換符號請參考bind命令的手冊。
- %W : 視窗元件本身的path
- %X,%Y : 相對於螢幕左上角的座標
- %x,%y : 相對於視窗元件本身左上角的座標
- %b : 用在滑鼠事件,會被代換成滑鼠按鈕的編號
- %k,%K : 用在鍵盤事件,會被代換成按鍵的keycode及keysym
36.3 其它的事件
以下列出其它常用的事件,及它們的說明,有興趣的朋友可以自己想些例子試看看。- Activate及Deactivate : 這兩個事件會分別在頂層視窗被設定為活動中或非活動中被觸發
- Configure : 這個事件會在視窗元件的大小、位置、邊框寬度及堆疊順序(stack order)被改變時觸發
- Destroy : 這個事件的在視窗元件被銷毀時觸發
- Map : 這個事件會在頂層視窗的狀態被為normal時被觸發
- Unmap : 這個事件會在頂層視窗的狀態被為withdrawn或是iconic時被觸發
- Visibility : 這個事件會在視窗元件變為可視或是被其它視窗遮住時觸發,你可以用%s來代換出目前的狀態
按右上方的「#」號切換側邊欄
24 個意見
Unknown | 2010年5月23日 晚上11:06
Hi Dai,
多謝您的新的tutorial。非常有幫助。好像有兩個小問題。
bind .lbl &lgt;Button-1> {puts "元件:%W , 點擊的x座標: %x , y 座標: %y"}
這個好像有html代碼問題,出現了亂碼。我是用chrome看的,不知道別人是否有同樣的問題。
ttk::entry .txt -textvariable ::txt
ttk::entry .txt2 -textvariable ::txt2
在這個例子中,.txt2好像沒有用到。
您的tutorial馬上就要完成了。好像您提到過您會寫一些高級的教程。您能談談package麼?
sam | 2010年5月24日 上午9:47
Hi Dai:
期待已久的新作終於出了,
bind .lbl {puts "元件:%W , 點擊的x座標: %x , y 座標: %y"}
程式的"<"被取代成"&lgt;"了,
.txt2我猜是用來方便測試游標進入/移出用的吧
看了這麼多Dai的教學,終於被我找到一個錯字了(用來表示按鈕"複"覆的次數) ^.^
dai | 2010年5月24日 下午1:25
謝謝兩位幫忙指正文章裡的錯誤~
eda package有規劃在未來要寫的文章裡
但可能還要請你多等一些時間 ~ 最近好忙哦!!
sam說得沒錯.txt2只是為了方便游標進入/移出
所以換成其它的widget也可以的
Unknown | 2010年8月31日 中午12:05
Dear Dai :
最近學TCL同時也在研讀你所寫有關TCL的教學 , 獲益良多.
只是看到 item 37.TK - 對話方塊 (dialog)時 沒有內容 ;
不知是否能增加上去.
感謝你的幫忙與無私的教學 ' 分享.
利塔 | 2010年8月31日 下午5:01
他目前去當兵了
所以要等他放假才會有空把內容填上去
只是可能需要一段時間
dai | 2010年9月12日 上午10:53
To 希:
我現在人在外島當兵....要很久很久才能回台灣一次
嗯~~我會儘量找時間把 最後一章補上的
不好意思哦
匿名 | 2011年8月10日 上午11:47
Dai您好,
下面是一個例子,我想請問一下,如果使用者選取kkk,kkk3就變成disabled;如果kkk2被選取時,kkk4就變成disabled。
要做到這種效果,應該要怎麼寫呢?
謝謝!
Coriolis
package require Tk
set onetwothree "3"
set test "3"
set kkk [radiobutton .one -value "WNBC" -text "New York" -variable onetwothree ]
set kkk2 [radiobutton .two -value "KDKA" -text "Pittsburgh" -variable onetwothree ]
set kkk3 [radiobutton .four -value "WNBC2" -text "New York2" -variable test ]
set kkk4 [radiobutton .five -value "KDKA2" -text "Pittsburgh2" -variable test ]
pack $kkk $kkk2 $kkk3 $kkk4
dai | 2011年8月15日 晚上7:13
hi,
請參考下面的程式
package require Tk
set onetwothree "3"
set test "3"
proc cb {} {
if {$::onetwothree == "WNBC" } {
.four configure -state disable
.five configure -state normal
} else {
.four configure -state normal
.five configure -state disable
}
}
set kkk [radiobutton .one -value "WNBC" -text "New York" -variable ::onetwothree -command {cb} ]
set kkk2 [radiobutton .two -value "KDKA" -text "Pittsburgh" -variable ::onetwothree -command {cb}]
set kkk3 [radiobutton .four -value "WNBC2" -text "New York2" -variable ::test ]
set kkk4 [radiobutton .five -value "KDKA2" -text "Pittsburgh2" -variable ::test ]
pack $kkk $kkk2 $kkk3 $kkk4
TerryWang | 2012年4月18日 晚上7:52
哈囉..Dai大..好久不見!!!
又來麻煩你程式的問題了...我想透過TCL寫一支類似teraterm的rs232 登陸控制程式,
我希望在text中可以直接輸入指令(目前可以成功登入com1且可以即時讀到內容,也可以輸入一些字),但目前遇到的問題如下
1.有些鍵盤按鈕我無法bind成功, 例如:
"space" {puts -nonewline $fd " "}
這樣我在透過rs232登入console後..就可以直接在text上輸入空白
"Up" {puts -nonewline $fd 這裡我就查不到正確值}
"Backspace" {puts -nonewline $fd 這裡我就查不到正確值}
2. 在text中每次輸入都會自動重複字串..不知道您有辦法解決嗎?
3. 滑鼠的光標不知道是否可以改顏色..因為預設是黑色的...當我text背景為黑的時候就看不到了!!
TerryWang | 2012年4月18日 晚上7:55
對了,說明一下..."space" {puts -nonewline $fd " "}這是我截出來的一小段
只是要說明我可以成功的點...意思就是我知道space等於" " ,但我失敗的我就不知道它到底等於什麼!!
dai | 2012年4月18日 晚上9:29
真的好久不見了呢!!
其實在寫ezdit及CrowTDE,洽巧也有遇到類似的問題!!
我的做法是 bind KeyRelease 或 KeyPress 然後再用 %k , %K 來判斷使用者的輸入 EX.
bind .txt "KeyPress" [list key_handler %k %K]
不要讓text中重複出現某個字串...可以這樣
"KeyPress" {.txt configure -state disabled}
"KeyRelease" {.txt configure -state normal}
讓滑鼠光標變色...我就沒用過了!! 如果你找到的話....也請跟我說!! 哈~
匿名 | 2012年4月18日 晚上11:46
text的游標insertion cursor可用的參數
改顏色 -insertbackground
改閃爍時間 -insertontime -insertofftime
改寬度 -insertborderwidth -insertwidth
滑鼠游標 cursor
只找到一個參數 -cursor
參數值可以帶TK內建的cursor或是直接指定滑鼠游標檔
TerryWang | 2012年4月19日 上午11:09
感謝Dai大的熱心回答..問題有得到解決了,但還有一些小問題想請教
目前發現在console中若是輸入字然後按下backspace後,text中的字不會刪掉,但實際上是有刪掉了
查了一下發現在按下backspace之後是有印出字的..所以text中沒辦法做到刪除,
舉例如下:
console > ver .....此時會dispaly version
但假設此時輸入錯誤 console > verabc ...此時想要按下backspace鍵刪除多餘的abc卻發現text中仍然顯示verabc且會滑鼠光標會往後跳
不知道Dai大是否有好方法解決此問題?
BTW...很久之前有問過Dai大關於cat.exe..想知道連接rs232也可以套用cat嗎? 可否來個舉例說明呢?
dai | 2012年4月19日 中午12:30
嗯~ 如果你偵測到按下倒退鍵,而且游標有向後,可以用下面的命令刪除一個字
.txt delete "insert" "insert + 1 c"
另外cat.exe @@ 想不起來是什麼問題了!!
dai | 2012年4月19日 中午12:31
謝謝 匿名 朋友的回答 ^^
TerryWang | 2012年4月19日 下午2:53
哈...我來幫忙喚醒一下Dai大的記憶!!
當初好像問了有關及時吐log的事情(當時是想做iperf結果及時顯示)..結果Dai推薦使用cat.exe
那個時候sam大也告知了另一個方法, 就是先暫存到一個txt然後在一行一行讀出!!!
當時Dai大給的範例如下...
set fd [open "| ping 127.0.0.1 |& cat" r]
fconfigure $fd -blocking 0
while {![eof $fd]} {
update
if {[gets $fd buf] == -1} {continue}
puts [string trim $buf]
}
close $fd
我想知道的是..透過comport連接是否也可以套用cat來使用呢?? (因為我試不出來,所以來問一下)
dai | 2012年4月19日 下午6:29
如果是comport的話...就不太需要用到這種方法了!!
Terry可以試試看直接註冊一個 readable 的 event 有資料進來,先讀到buffer裡,再處理!!
這樣應該就沒有應付不來的事。
匿名 | 2012年5月14日 晚上8:54
Hi Dai,
你好, 你的TCL教學做得很不錯, 我從你的網站學到了不少東西,
最近我在寫一個TCL程式, 其中會用到bind指令結合format指令, 問題是format指令如果
放在bind指令中, 輸出結果會有異常, 但是如果format指令是單獨執行就不會有問題..
我遇到問題的程式內容如下:
console show
text .t -width 100 -height 20
pack .t -expand y -fill both
.t yview
bind .t {puts [format "inside bind: %c" 48] ;}
puts [format "outside bind: %c" 48] ;
輸出結果如下:
outside bind: 0
inside bind: ??
inside bind: ??
inside bind: ??
inside bind: ??
inside bind: ??
請問你知道是什麼原因嗎?
謝謝!
匿名 | 2012年5月14日 晚上10:16
真有趣的例子
剛好bind指令也把"%"當特殊字元,%c先被bind處理掉了
換成%%c就可以了,bind會把 %%c 取代成 %c
匿名 | 2012年5月16日 下午6:36
Ok~謝謝你啦, 匿名朋友^^
匿名 | 2012年5月18日 上午10:46
你好~~之前有看到您似乎是位老師,冒味的想請您幫忙問問,是否有學生有意願想暑期打工寫TCL Script,公司位於新竹市科學園區,待遇為時薪150元...如果有打擾到你的地方請見諒...聯絡信箱 :connie_cheng@fiberlogic.com
Mike | 2014年12月19日 晚上7:37
又來麻煩Dai大大您,想要請問一下,Bind可以抓到Canvas裡面的貼上去圖片或文字的物件嗎?
還是我只能用滑鼠座標來判斷?
匿名 | 2014年12月20日 下午6:36
可以bind到canvas裡的圖片或文字沒問題
匿名 | 2014年12月22日 下午2:49
Dear Terry,
我是TCL新手,想用TCL控制TERA TERM CONSOLE。
但是不知如何開始,看到你有相關的經驗,不知道可以Sharer你的經驗 謝謝。
留下您的意見