天天看點

[wxWidget系列] wxWidget的事件機制

wxWidget 的事件機制

wxWidget 通過在編譯期生成靜态的事件表來實作事件類的事件處理。所有想要使用事件處理機制的地方都需要繼承wxEvtHandler 類(直接或間接)。

由于 window 控件需要處理自身的 UI 時間,故 wxWidget 将實作為 exEvtHandler 的基類,這就意味着所有的 wxWidget的控件均是事件類,可以直接定義事件表。

a) 定義事件表的基本步驟:

1) 定義一個直接或間接繼承自 wxEvtHandler 的類;

2) 定義所需的事件處理函數,函數格式: void Func(wx***Event& evt);

3) 在類的定義中使用 DECLARE_EVENT_TABLE() 聲明事件表;

4) 在 cpp 檔案中使用 BEGIN_EVENT_TABLE( SourcePanel, wxPanel ) 和 END_EVENT_TABLE() 定義事件表。

5) 使用相應的事件宏在事件表中建立事件與處理函數的映射。

b) 視窗的事件查找流程:

目前視窗事件類 ->1 級繼承的事件類 ->2 級繼承事件類 ->….-> 父視窗事件類 …

上述的事件查找過程是建立在事件沒有被處理的前提下。如果某個事件類處理了該事件,并沒有調用 skip() ,那麼該事件将被認為已經處理完畢,查找終止。

熟悉視窗的事件查找流程,對于有效處理事件極為關鍵。

使用者可以通過提前截獲某事件,進而阻斷後續的處理流程;使用者也可以提前截獲事件,增加額外的事件處理邏輯 ( 需調用 skip) 。

c) wxWindow 類的内部事件處理機制:

每個視窗類的内部均維護一個事件表棧,在事件傳遞給某個視窗類時,視窗類将事件逐一比對事件表棧中事件表,也就是說最後放入的事件表将最先被比對。使用者可以通過 wxWindow::PushEvnetHandler 來壓入事件表,通過 PopEventHandler 彈出事件表。一定要確定事件表中的事件處理對象的生存期大于視窗對象的生存期,除非 PopEventHandler ,并删除了該事件對象。記住:視窗本身也是一個事件處理對象,并作為第一個 EventHandler 被壓入事件表棧。

wxWindow 的處理機制決定了使用者可以通過,改變事件表棧的順序臨時或永久的改變圖形界面的行為。使用者可以通過自定義的 wxEvtHandler 類,截獲視窗事件,進而實作增加處理邏輯或過濾視窗事件的行為。

d) 下述事件不會傳給事件源控件的父視窗,即目前視窗有效:

wxActivate, wxCloseEvent, wxEraseEvent, wxFocusEvent, wxKeyEvent, wxIdleEvent, wxInitDialogEvent, wxJoystickEvent, wxMenuEvent, wxMouseEvent, wxMoveEvent, wxPaintEvent, wxQueryLayoutInfoEvent, wxSizeEvent, wxScrollWinEvent, wxSysColourChangedEvent 。

這麼設計是因為這些控件僅對目前視窗有意義。當然這是 wxWidget 設計者的想法。在實際程式設計中,可能需要截獲wxMouseEvent , wxKeyEvent 等事件,并增加其他的處理邏輯,比如過濾相應的 key 事件等。有三種常見辦法:

1) 重載視窗類,在新類中截獲事件;

2) 重載 wxEvtHandler ,在新 Handler 中截獲事件;見 wxWindow 類事件處理機制。

3) 使用動态的事件處理函數 Connect 。不常用,不介紹。

問題:如何在父視窗中截獲子視窗的内部事件,如 wxMouseEvent ?

這個問題不難。根據上面的分析, 1) 重載視窗類并不現實,因為 wxMouseEvent 不會傳給父視窗。 3) 動态事件處理機制可以實作,但是需要重載視窗類,重新 connect 事件與事件處理函數。 2) 重載 wxEvtHandler ,實作所需邏輯,并将該對象壓入子視窗,進而達到事先截獲子視窗事件的目的。個人感覺 2) 最為友善。

那麼有個問題:可否直接将父視窗 Push 給子視窗呢?答案是:不行,雖然所有視窗均是 wxEvthandler 對象。為什麼呢?因為所有的 wxWindow 對象對事件的處理都是極其複雜的,使用父視窗去攔截子視窗事件,會引起混亂,因為所有子視窗的事件都會首先傳給父視窗。實踐證明:将父視窗 Push 給子視窗會引發異常。

e) 視窗辨別符

所有的 wxCommandEvent 均可以在多個視窗之間傳遞,如何實作特定視窗處理特定事件呢? wxWidget 使用視窗辨別符來辨別視窗,進而在事件系統中實作定位特定視窗的作用。視窗辨別符并不要求系統唯一,僅僅需要在特定的上下文中唯一即可。

視窗辨別符僅僅是要使事件處理機制能夠定位特定視窗,進而實作事件與視窗的映射。這意味着并不是所有的事件都關心視窗 ID ,尤其是那些不傳遞給父視窗的事件。

假設 wxWidget 的事件處理機制是完備的,那麼通過事件宏,即可判斷該事件是否會傳遞給父視窗:需要映射視窗 ID 的事件宏意味着該事件會傳遞給父視窗,反之,不然。

f) 事件對象的常用接口

1) skip()   是否繼續傳遞該事件 ( 繼續處理 )

2) 傳遞參數:如 int , long , string , void* 等變量

3) wxNotifyEvent::veto()  使本次事件失效,相當于未發生

本文轉自 zhenjing 部落格園部落格,原文連結:  http://www.cnblogs.com/zhenjing/archive/2011/04/20/2021821.html ,如需轉載請自行聯系原作者

繼續閱讀