天天看點

wxwidgets自定義事件+調試

自定義事件

大體方法

就像每個事件被其事件類型所唯一确定一樣,定義一個自定義事件從為它定義一個新事件類型開始。這通過使用wxDEFINE_EVENT()宏來完成。就像事件類型是可變的,如果有必要它也可以通過使用wxDECLARE_EVENT()宏來聲明。

另一件要做的事情就是決定你是否需要為事件定義一個自定義事件類或使用已經存在的類,代表性的有wxEvent(它不會提供任何額外的資訊)或wxCommandEvent(它包含幾種額外的域,并且預設把事件向上傳遞)。這兩種方式的細節都将都将在下面描述。代碼詳細說明和使用自定義事件類型的完整例子也可以在事件示例中檢視。

最後你将需要産生并發送你的自定義事件。産生事件就像執行個體化你的自定義事件類并初始化其内部值那樣簡單。為了發送事件到一個特定的事件句柄,這裡有兩種可選:使用wxEvtHandler::AddPendingEvent或wxEvtHandler::QueueEvent。當進行内部線程通信時,你基本上隻需要使用後者。當你隻使用主線程時,你也可以安全的使用前者。

最後需要注意的是,還有兩個簡單的全局封裝函數關聯到提到的那兩個wxEvtHandler函數,即wxPostEvent() 和 wxQueueEvent()。

使用存在的事件類

如果你僅打算使用wxCommandEvent和一個新事件類型,可以使用下面列出的事件表宏之一而不用自己定義一個新事件類。

Example:

wxDECLARE_EVENT(MY_EVENT, wxCommandEvent);//這句很明顯應該位于頭檔案中:它僅僅聲明了MY_EVENT這個事件類型

wxDEFINE_EVENT(MY_EVENT, wxCommandEvent);//這是事件類型定義,是以不能位于頭檔案

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)//用事件表處理事件的示例代碼
EVT_MENU (wxID_EXIT, MyFrame::OnExit)
...
EVT_COMMAND (ID_MY_WINDOW, MY_EVENT, MyFrame::OnMyEvent)
wxEND_EVENT_TABLE()
void MyFrame::OnMyEvent(wxCommandEvent& event)//事件處理函數
{
// do something
wxString text = event.GetString();
}

MyFrame::MyFrame()// 使用Bind<>()處理事件的示例代碼:
{
Bind(MY_EVENT, &MyFrame::OnMyEvent, this, ID_MY_WINDOW);
}

void MyWindow::SendEvent()// 産生事件的示例代碼
{
wxCommandEvent event(MY_EVENT, GetId());
event.SetEventObject(this);
event.SetString("Hello");// 給它一些包含的資訊
ProcessWindowEvent(event);//發送它
}
           

實驗圖:

wxwidgets自定義事件+調試

定義你自己的事件類

在一些環境下,你必須定義你自己的事件類,例如從一個地方發送一些很複雜的資料到另一個地方。除了定義你的事件類,如果你打算事件表來處理這種類型的事件,你還需要定義你自己的事件表宏。

Here is anexample:

class MyPlotEvent: public wxEvent// 定義新事件類
{
public:
MyPlotEvent(wxEventType eventType, int winid, const wxPoint& pos)
: wxEvent(winid, eventType),
m_pos(pos)
{
}
wxPoint GetPoint() const { return m_pos; }// 擷取成員變量
virtual wxEvent *Clone() const { return new MyPlotEvent(*this); }//實作純虛函數
private:
const wxPoint m_pos;
};

//我們定義了一個關聯到上面的類的單獨的事件類型 MY_PLOT_CLICKED ,但很明顯你需要不止一個事件類型,例如,
//你還可以擁有MY_PLOT_ZOOMED或MY_PLOT_PANNED,在這種情況下你隻需要在這添加更多相似的内容。
wxDEFINE_EVENT(MY_PLOT_CLICKED, MyPlotEvent);

/**************************************************************************************************/
// 如果你想支援舊編譯器你需要使用下面這些繁瑣的宏:
typedef void (wxEvtHandler::*MyPlotEventFunction)(MyPlotEvent&);
#define MyPlotEventHandler(func) wxEVENT_HANDLER_CAST(MyPlotEventFunction, func)

//如果你的代碼使用現在流行的編譯器編譯,你就可以這樣做:
#define MyPlotEventHandler(func) (&func)

//最後定義一個宏為新的事件類型建立一個完整的事件表
//記住如果你使用Bind<>() 你就完全不需要這些,你可以僅使用 &func來代替MyPlotEventHandler(func),除非你用的是非常老的編譯器。

#define MY_EVT_PLOT_CLICK(id, func)  \
wx__DECLARE_EVT1(MY_PLOT_CLICKED, id, MyPlotEventHandler(func))

//處理事件的示例代碼(你将使用兩者之一,而不是兩個都用)
事件表:
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_PLOT(ID_MY_WINDOW, MyFrame::OnPlot)
wxEND_EVENT_TABLE()

動态綁定:
MyFrame::MyFrame()
{
Bind(MY_PLOT_CLICKED, &MyFrame::OnPlot, this, ID_MY_WINDOW);
}

void MyFrame::OnPlot(MyPlotEvent& event)
{
... do something with event.GetPoint() ...
}

// 産生事件的示例代碼:
void MyWindow::SendEvent()
{
MyPlotEvent event(MY_PLOT_CLICKED, GetId(), wxPoint(...));
event.SetEventObject(this);
ProcessWindowEvent(event);
}
           

實驗圖如下:

wxwidgets自定義事件+調試

需要注意的是這句:

#define MY_EVT_PLOT_CLICK(id, func)  \
wx__DECLARE_EVT1(MY_PLOT_CLICKED, id, MyPlotEventHandler(func))
           

\是繼續符,代表下一行接本行,我本來以為上面那個是說明文檔裡出錯了,實際上是我錯了。。還有裡面有兩個_接起來,我本來以為怎麼可能起這種宏名,沒想到真的是兩個。。還有一個就是辨別id是視窗id,我原來了解不到位,把觸發事件的按鈕的id當多了辨別id,結果事件找不到對應的處理函數了。。。。

再一個,這句:

#define MyPlotEventHandler(func) (&func)
           

不知道幹嘛用的,我不用這句,使用事件表依舊工作正常。。。

事件表的向上傳遞尋找之類的我現在也沒怎麼搞清楚,我從一個線程傳遞事件到非父視窗的視窗時大概如下:

自定義事件類:

class MyThreadEvent: public wxThreadEvent
{
public:
    MyThreadEvent(wxEventType eventType, int winid,unsigned char* pdate);

    virtual wxEvent *Clone() const {return new MyThreadEvent(*this);}

    unsigned char* GetPointer() const {return p;};
private:
    unsigned char* p;
};
           

事件類型也定義聲明Ok:

頭檔案中:

wxDECLARE_EVENT(WX_POINTER_ARR,MyThreadEvent);
           

cpp中:

wxDEFINE_EVENT(WX_POINTER_ARR,MyThreadEvent);
           

然後寫好事件映射宏:

#define EVT_POINTER_ARR(id,fn) DECLARE_EVENT_TABLE_ENTRY(WX_POINTER_ARR\
                                  ,id\
                                  ,-1\
                                  ,&fn\
                                  ,(wxObject*)NULL\
                                  ),
           

注意最後的逗号不要丢,而\是繼續符。

事件表:

BEGIN_EVENT_TABLE(showpic,wxFrame)
	EVT_POINTER_ARR(wxID_ANY,showpic::OnProcessThreadEvent)
END_EVENT_TABLE()
           

事件處理函數

void showpic::OnProcessThreadEvent(MyThreadEvent& event)
{
    image.Destroy();//清除原來的圖檔
    image=wxImage(col,row,event.GetPointer());//生成新的圖檔
    bitmap = wxBitmap(image.Scale(800,600));//調整大小
    Refresh(false);
}
           

最後是産生事件:

wxQueueEvent(windows,new MyThreadEvent(WX_POINTER_ARR,wxID_ANY,imgdate));
           

其中windows是處理事件的視窗的指針。

關于事件的産生

定義好事件類後,有下面幾個函數可選:(下面是機翻的,哈哈)

virtual void wxEvtHandler :: QueueEvent wxEvent *  事件
虛拟

事件放入隊列以便進行後續處理。

該方法類似于ProcessEvent(),但是後者是同步的,即事件在函數傳回之前立即被處理,這一個是異步的,并且将在稍後的時間(通常在下一個事件期間)處理事件循環疊代)。

另一個重要的差別是這個方法會擷取事件參數的所有權,即它會自動删除它。這意味着事件應該在堆上配置設定,并且在函數傳回之後指針不能再被使用(因為它可以随時被删除)。

QueueEvent()可用于從工作線程到主線程的線程間通信,它在内部使用鎖定,并通過確定正在調用的線程不會再使用事件對象來避免AddPendingEvent()文檔中提到的問題。應該注意避免該對象的某些字段被它使用,特别是事件對象的任何wxString成員不能是另一個wxString對象的淺層副本,因為這将導緻它們在背景仍然使用相同的字元串緩沖區。例如:

void FunctionInAWorkerThread( const wxString&str) { wxCommandEvent * evt = new wxCommandEvent ; // NOT evt-> SetString(str),因為這将是一個淺的副本 EVT-> 的SetString(STR。 c_str()); //做一個深層次的副本 wxTheApp - > QueueEvent(evt); }

請注意,您可以使用wxThreadEvent而不是wxCommandEvent來避免此問題:

void FunctionInAWorkerThread( const wxString&str) { wxThreadEvent evt; EVT。 SetString(str); // wxThreadEvent :: Clone()確定内部的wxString //其他wxString執行個體不共享成員: wxTheApp - > QueueEvent(EVT。 克隆()); }

最後注意到,如果通過調用wxWakeUpIdle()目前處于空閑狀态,則此方法會自動喚醒事件循環,是以在使用它時不需要手動執行。

以來
2.9.0
參數
事件 要排隊的堆配置設定事件,QueueEvent()擁有它的所有權。該參數不應該

NULL

在wxWindow中重新實作。

virtual void wxEvtHandler :: AddPendingEvent const wxEvent&  事件
虛拟

釋出事件稍後處理。

這個功能類似于QueueEvent() ,但不能用于釋出來自工作線程事件與事件對象wxString場(即在實踐中大多數),因為不安全使用相同的wxString對象,是因為該wxString原始事件對象中的字段及其由該函數内部建立的副本在内部共享相同的字元串緩沖區。使用QueueEvent()來避免這種情況。

一個事件副本由函數進行,是以一旦函數傳回(原來是在堆棧上建立的),原來的代碼就可以被删除。這要求wxEvent :: Clone()方法由事件實作,以便可以将其複制并存儲直到被處理。

參數
事件 事件添加到挂起的事件隊列。

在wxWindow中重新實作。

virtual bool wxEvtHandler :: ProcessEvent wxEvent&  事件
虛拟

處理事件,搜尋事件表并調用零個或多個合适的事件處理函數。

通常,您的應用程式不會調用此函數:它在wxWidgets實作中調用,以将傳入的使用者界面事件分派到架構(和應用程式)。

但是,如果實作定義新事件類型的新功能(例如新控件),則可能需要調用它,而不是允許使用者覆寫虛拟函數。

請注意,您通常不需要重寫ProcessEvent()來自定義事件處理,覆寫特别提供的TryBefore()和TryAfter()函數通常就足夠了。例如,wxMDIParentFrame可以覆寫TryBefore(),以確定在父架構本身處理之前在活動子幀中處理菜單事件。

事件表搜尋的正常順序如下:

  1. wxApp :: FilterEvent()被調用。如果它傳回任何東西

    -1

    (預設),處理在這裡停止。
  2. TryBefore()被調用(這是wxWalidator被考慮在wxWindow對象的地方)。如果傳回true,則退出該函數。
  3. 如果對象被禁用(通過調用wxEvtHandler :: SetEvtHandlerEnabled),該函數跳到步驟(7)。
  4. 使用Bind <>()綁定的處理程式的動态事件表在最近期綁定到最早綁定的順序中進行搜尋。如果找到一個處理程式,它将被執行,并且該函數傳回true,除非使用wxEvent :: Skip()處理程式來表示它沒有處理事件,在這種情況下搜尋繼續。
  5. 使用事件表宏綁定的處理程式的靜态事件表按源代碼中事件表宏的出現順序搜尋該事件處理程式。如果失敗,則會嘗試使用基類事件表,直到不再存在表或找到适當的函數為止。如果找到一個處理程式,則與上一步相同的邏輯适用。
  6. 搜尋被應用在整個事件處理程式鍊上(通常鍊的長度為1)。這個鍊可以使用wxEvtHandler :: SetNextHandler()形成: (指圖像,如果

    A->ProcessEvent

    被調用,并且不處理事件,

    B->ProcessEvent

    将被調用等等)。請注意,在wxWindow的情況下,您可以建構一堆事件處理程式(有關更多資訊,請參閱wxWindow :: PushEventHandler())。如果任何連結的處理程式傳回true,則該函數退出。
  7. TryAfter()被調用:對于wxWindow對象,這可能會将事件傳播到視窗父(遞歸)。如果仍未處理該事件,則将wxTheApp對象上的ProcessEvent()作為最後一步進行調用。

注意步驟(2) - (6)在該函數調用的ProcessEventLocally()中執行。

參數
事件 事件處理。
傳回
如果發現并執行了一個合适的事件處理函數,則該 值為true,該函數未調用 wxEvent :: Skip。
也可以看看
SearchEventTable()

在wxWindow中重新實作。

還有這兩個,它們是全局函數:

void wxQueueEvent wxEvtHandler *  dest,
wxEvent *  事件 

對給定對象進行處理排隊事件。

這是圍繞wxEvtHandler :: QueueEvent()的包裝器,有關更多詳細資訊,請參閱其文檔。

包含檔案:

#include <wx / event.h>       
參數
DEST 對象将事件排隊,不能

NULL

事件 堆配置設定和非

NULL

事件隊列,該函數擁有它的所有權。
void wxPostEvent wxEvtHandler *  dest,
const wxEvent&  事件 

在GUI應用程式中,此函數使用wxEvtHandler :: AddPendingEvent()将事件釋出到指定的dest對象。

否則,它使用wxEvtHandler :: ProcessEvent()立即排程事件。有關詳細資訊,請參閱各自的文檔(和警告)。由于限制wxEvtHandler :: AddPendingEvent()這個函數是不是線程安全具有事件對象wxString字段,使用wxQueueEvent()來代替。

包含檔案:

#include <wx / event.h>       

按需調用