#

這一篇文章介紹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 個意見

eda | 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也可以的

| 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

Hu Mike | 2014年12月19日 下午7:37

又來麻煩Dai大大您,想要請問一下,Bind可以抓到Canvas裡面的貼上去圖片或文字的物件嗎?
還是我只能用滑鼠座標來判斷?

dai | 2014年12月20日 下午6:36

可以bind到canvas裡的圖片或文字沒問題

匿名 | 2014年12月22日 下午2:49

Dear Terry,

我是TCL新手,想用TCL控制TERA TERM CONSOLE。
但是不知如何開始,看到你有相關的經驗,不知道可以Sharer你的經驗 謝謝。

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates