處理 Event
marquee | relinq | memory | cham | entry | color_model | |
tcl 版 | v | v | v | v | v | v |
nis 版 | v | v | v | v | v | v |
其中 color_model 只是一個函式庫, 不能單獨執行。 有幾個程式用到它。
-
程式設計師自行安排的 "定時事件"
- 用 after 可安排副程式在 (a) 固定時間後 或 (b) 沒有其他事件要處理時 被叫起來執行.
- 注意: 像 marquee 範例中這樣的寫法並不是遞迴. 下一個 RotateColor 被叫起來之前, 前一個 RotateColor 早已執行完畢.
- 如果把 after 的傳回值記起來, 之後還可以用 after info 取得有關於這個事件的資訊, 或用 after cancel 將它取消. 詳見 after(n) 或 perdoc Tk::After.
-
滑鼠與鍵盤
- 每當使用者移動滑鼠, 按下按鈕, 或按下鍵盤, 都會產生一個事件 (event). 究竟那個 widget 會被叫起來處理這個事件呢? 要看目前誰擁有 mouse grab 以及誰擁有 keyboard focus 來決定.
- 一個 tk 應用程式內可以有一個 widget 佔據住所有 mouse 事件, 只有它自己或它的 children, grandchildren, ... 才收得到 mouse 事件. (在文件中叫做 grab subtree.) 要佔據/釋放 mouse 事件, 可用 grab 命令, 詳見 grab(n) 或 perldoc Tk::grab.
- 一般的 grab 稱為 local grab, 可以用來要求使用者先把這件重要的事情做完. 當然使用者還是可以自由地與 螢幕上其他應用程式 互動, 只不不能與 本應用程式 的其他部分互動而已. 例如 Dialog 這個 widget class 就是用這種方式運作.
- 有些特殊的情況需要使用 global grab, 例如你的應用程式負責改變整個系統某些非常低階的設定. 如果你設定的是 global grab, 則使用者甚至也無法與 螢幕上其他應用程式 互動. 這對會用電腦的人而言, 是一件非常不禮貌的事情, 應該盡量避免. (當然如果你心目中的使用者是不會用電腦的人, 這麼做可以避免他在與其他程式互動的過程中, 不小心試出你程式的 bugs) 不論是 local grab 或 global grab, 這類 "限制使用者選擇" 的互動方式叫做 modal interaction
- 當然如果目前沒有任何一個 widget 佔據住 mouse, 那麼
- 要改變鍵盤的掌控權, 可以用 focus 命令. 見 focus(n) 與 perldoc Tk::focus. 從這裡也可以查到如何設定 implicit focus model, 也就是讓 keyboard focus 在這個 tk 應用程式內, 立即跟著滑鼠跑, 滑鼠移到那個 widget 它立即就獲得 keyboard focus (而不需要再按滑鼠按鈕點一下).
- 在一個 tk 應用程式內, 按 tab 鍵或 shift-tab 鍵, 會讓 keyboard focus 在各個 widget 之間跳來跳去. 要把一個 widget 排除在外, 可以把 -takefocus 這個 configuration option 設成 0. 請執行 relinquish 並觀察用 tab 鍵選擇按鈕再用 space 按按鈕的效果. 詳見 options(n) 或 perldoc Tk::options. 想要知道跳動的順序, 可以用 tk_focusNext 與 tk_focusPrev. 詳見 focus 的手冊.
- 用 bind 指定處理事件的 callback 時, 往往需要為 callback 安排好「事件發生當時」的一些特殊參數, 例如當時滑鼠的 x, y 座標 (見 chameleon 範例), 或當時使用者所按下的究竟是那個鍵 (見 phoneentry 範例), 甚或是「當時事件發生在誰身上」 (見 grid_tut 範例). 這些參數不是在設定 binding 時就可以知道的, 而是每次事件發生時才臨時知道的, 要如何表示呢? 請見 bind(n) 或 perldoc Tk::bind 手冊當中的 SUBSTITUTIONS 一節說明.
- 如何寫 callback (event handler): tcl 版, perl 版 Perl 使用者: "bind" 所指定的 callback, 自動會接收到一個額外的參數 (且一定放在其他參數之前): "事件是在那一個 widget 當中發生的呢?" 但是 "-command" 所指定的 callback, 則沒有額外的參數. 請見 relinquish).
-
Event 的語法:
-
要描述一個 event, 至少要指出它的類別 (event type).
以下是比較常用的 event types:
- Button/ButtonRelease: 使用者按下/放開某個滑鼠鍵.
- Key/KeyRelease: 使用者按/放開下鍵盤上某個鍵. 見 memory 範例. 但是要記得設定 keyboard focus, 否則平常不接受鍵盤輸入的 widget class (例如 Label) 依舊沒有反應. 見 chameleon 範例.
- Motion: 滑鼠移動了. 見 chameleon 範例.
- Enter/Leave: 滑鼠進入/離開這個 widget. 見 chameleon 範例.
- FocusIn/FocusOut: 鍵盤的 focus 落到這個 widget 上/離開這個 widget.
- Map/Unmap: 這個 widget 被 geometry manager 放上來/移除掉了. (記得嗎? 一個 widget 並不是一產生就出現在螢幕上, 也並不是一被從螢幕上移除掉就不存在了.)
- Visibility: 這個 widget 在螢幕上的 "能見度" 改變. 除了被 geometry manager 放入 master 或自其中移除之外, 還可能因為其他 widgets 或其他應用程式蓋在這個 widget 上面, 或因為移動了 master 的 scrollbar, 或因為 master 的大小改變, 因而改變這個 widget 的能見度.
- Configure: 這個 widget 的任何一個屬性 (configuration option) 改變了.
- Destroy: 這個 widget 將要被毀掉了.
- 有時 type 的後面會加上一個細節描述 (detail), 例如 Button/ButtonRelease 後面加上數字, 可以指定只有在特定的滑鼠鍵按下時才反應; Key/KeyRelease 後面加上一個字元, 可以指定只對鍵盤上特定的鍵有反應. 但是如果你想對所有/很多個鍵有反應, 但是對不同的鍵處理方式稍微不同, 那麼應該不要加 detail, 而是傳參數給 callback. 見 "如何寫 callback".
- type 之前還可以加上條件 (modifier), 表示只有在這些條件成立的情況下, 發生 type 所描述的事件, 才會觸發 callback. 常用來修飾 Key 的 modifiers 有 Control, Shift, Alt, Meta; 常用來修飾 Button 的 modifiers 有 Double, Triple; 常用來修飾 Motion 的 modifiers 有 B1, B2, B3.
- 注意: <B1-Motion> 表示 "使用者在按著第一個滑鼠鍵不放的情況下, 移動了滑鼠", 屬於 <modifier-type> 的語法; 而 <Button-1> 則表示 "使用者現在按下了第一個滑鼠鍵", 屬於 <type-detail> 的語法.
- 可以把好幾個 events 寫成一串, 構成一個 multi-event sequence, 例如 <Key-s><Key-h><Key-o><Key-w> (可以簡寫成 <s><h><o><w>, 詳見手冊) 表示 "使用者按下了 s 鍵, 然後按 h 鍵, 再按 o, 再按 w" (不可以有其他事件穿插其中; 但是兩兩之間所隔的時間長短並無限制.) 這可以用來放一些 easter eggs 進你的程式, 增加趣味...
-
要描述一個 event, 至少要指出它的類別 (event type).
以下是比較常用的 event types:
-
Binding Tags: 詳見 bindtags(n) 或 perldoc Tk::bindtags
- 如果你仔細看 bind 的手冊, 會發覺其實 bind 不只可以作用在一個 widget 上, 也可以作用在一整個 widget class 上, 甚至可以作用在所有的 widgets 上.
- 事實上一個 event 可能會觸發不只一個 binding. 每個 widget 記載著一個 binding tags 的 list, 例如假設 .x.y 是一個 Button, 則它的 binding tag list 的內容通常是 .x.y Button . all 共四個元素. (注意: 在 perl/tk 中的順序是: Button .x.y . all) 一旦在一個 widget 內發生一個 event 時, tk 會把這個 widget 的 binding tags list 裡面的每個 tag 所對應到的 callback 逐一叫出來執行.
- 可以用 break 或 Tk->break 打斷這個過程. 最常用於限制 entry 內可以鍵入的字元. 在 tcl/tk 中, break 只能用在 bind 當句; 若寫成副程式則 tcl/tk 不認得. 在 perl/tk 中, 由於 class binding (如上例的 Button) 在 widget binding (如上例的 .x.y) 之前, 所以必須使用自己發明的 binding tag, 並放在最前面才有效, 詳見 perldoc Tk::bindtags 解釋為何 perl/tk 與 tcl/tk 有這點不同.
- (待續 ...)
-
其他重要事項
- after 後面只加時間參數, 不加 callback, 則表示完全不同的意思: "睡 ... 這麼久再醒過來繼續執行".
- 因為 tk 會把所有的 configure 設定集中起來以批次 (batch) 的方式執行, 所以並不是一執行完 configure 命令後就會生效. 如果你需要它立即生效, 必須呼叫 update 或 update idletasks. 詳見 update(n) 或 perldoc Tk::Widget
- 盡量不要給主視窗設定 binding, 因為它的 binding 會被所有的 children, grand-children 繼承, 造成一個事件同一個副程式被觸發許多次. 筆者只知道有一種情況值得這樣做: 要為你的程式加上 hot-key.
- 本頁最新版網址: https://frdm.cyut.edu.tw/~ckhung/b/tk/event.php; 您所看到的版本: February 14 2012 10:32:25.
- 作者: 朝陽科技大學 資訊管理系 洪朝貴
- 寶貝你我的地球, 請 減少列印, 多用背面, 丟棄時做垃圾分類。
- 本文件以 Creative Commons Attribution-ShareAlike License 或以 Free Document License 方式公開授權大眾自由複製/修改/散佈。