1. 程式人生 > >6、wxWidgets 事件處理

6、wxWidgets 事件處理

focus code creat 一個 靜態 image 自動創建 del parent

wxWidgets事件處理  

  事件處理是所有GUI程序重要的組成部分,所有GUI程序都是由事件驅動的。一個應用程序對其運行周期內產生的不同事件類型做出不同反應。事件主要由應用程序的用戶產生,但是它們也能以其它方法產生,例如:一個網絡請求、窗口管理器、定時器,當一個應用程序開始運行時,一個主循環開始啟動,程序被設置在這個主循環內執行,同時等待事件的產生,當退出這個程序時,主循環也就同時停止。

定義

  事件是一個底層框架下面的程序級別的信息,被封裝成一個GUI工具包。事件循環是一個等待和派遣事件或消息的編程結構,事件循環反復的尋找事件並處理它們,事件句柄和方法對事件做出反應

  事件對象是一個和事件本身有關聯的對象,它通常是一個窗體,事件類型是一個剛產生的獨一無二的事件。

  在wxWidgets裏面使用事件的傳統方法是使用一個靜態事件表,這是受MFC的影響。一個更加靈活和現代的方法是使用Connect()方法。

靜態事件表

下面是一個簡單的使用靜態事件表的例子:

main.h

 1 #include <wx/wx.h>
 2 //定義主窗口類
 3 class MyFrame : public wxFrame
 4 {
 5 public:
 6     MyFrame(const wxString& title);
 7 
 8     //定義事件處理函數
 9     void OnQuit(wxCommandEvent& event
); 10 private: 11 //聲明事件表 12 DECLARE_EVENT_TABLE() 13 14 }; 15 //定義應用程序類 16 class MyApp : public wxApp 17 { 18 public: 19 virtual bool OnInit(); 20 };

main.cpp

 1 #include "main.h"
 2 
 3 //定義事件表,完成事件和處理函數的映射
 4 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
 5     EVT_BUTTON(wxID_EXIT, MyFrame::OnQuit)
6 END_EVENT_TABLE() 7 8 MyFrame::MyFrame(const wxString& title) 9 : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 150)) 10 { 11 //添加狀態欄 12 CreateStatusBar(); 13 //將狀態欄分為兩欄 14 //CreateStatusBar(2); 15 //添加狀態欄顯示內容 16 SetStatusText(wxT("Welcome to wxWidgets!")); 17 18 //在wxFrame組件中定義了一個Panel容器,用於放置Button按鈕 19 wxPanel * panel = new wxPanel(this, wxID_ANY); 20 //添加一個按鈕 21 wxButton * button = new wxButton(panel, wxID_EXIT, wxT("Quit"), wxPoint(20, 20)); 22 button->SetFocus();//按鈕自動獲取焦點 23 24 //使整個wxFrame框架位於屏幕中間 25 Centre(); 26 } 27 void MyFrame::OnQuit(wxCommandEvent& event) 28 { 29 Close(true); 30 } 31 32 //聲明應用程序 33 IMPLEMENT_APP(MyApp) 34 35 //初始化應用程序 36 bool MyApp::OnInit() 37 { 38 MyFrame *myframe = new MyFrame(wxT("MyFrame")); 39 myframe->Show(true); 40 41 return true; 42 }

在我們的程序中,我們創建了一個簡單的按鈕,當我們點擊按鈕時,應用程序關閉。

1 private:
2     DECLARE_EVENT_TABLE()

  在我們的頭文件中,我們通過DECLARE_EVENT_TABLE()宏定義了一個靜態事件表。

1 BEGIN_EVENT_TABLE(MyButton, wxFrame)
2      EVT_BUTTON(wxID_EXIT, MyButton::OnQuit)
3 END_EVENT_TABLE()

  我們通過把事件和相應的處理函數對應起來實現了這個靜態時間表。

Connect()方法

  下面將討論一個移動事件,一個移動事件包含了運動狀態變化事件。當我們移動一個窗口時,一個移動事件相應產生。代表移動事件的類是wxMoveEvent,wxEVT_MOVE是這個事件的類型。

main.h

 1 //事件處理函數動態關聯
 2 #include <wx/wx.h>
 3 
 4 //定義主框架類
 5 class MyFrame : public wxFrame
 6 {
 7 public:
 8     MyFrame(const wxString & title);
 9 
10     //窗口移動事件的處理函數
11     void OnMove(wxMoveEvent & event);
12     //兩個靜態文本
13     wxStaticText * st1;
14     wxStaticText * st2;
15 };
16 
17 //定義應用程序類
18 class MyApp : public wxApp
19 {
20 public:
21     virtual bool OnInit();
22 };

main.cpp

 1 #include "main.h"
 2 
 3 MyFrame::MyFrame(const wxString & title)
 4     : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
 5 {
 6     //定義一個面板容器
 7     wxPanel * panel = new wxPanel(this, -1);
 8     //定義了兩個靜態文本組件
 9     st1 = new wxStaticText(panel, wxID_ANY, _T(""), wxPoint(10, 10));
10     st2 = new wxStaticText(panel, wxID_ANY, _T(""), wxPoint(10, 30));
11     //事件處理函數的動態關聯
12     Connect(wxEVT_MOVE, wxMoveEventHandler(MyFrame::OnMove));
13 
14     Centre();
15 }
16 
17 void MyFrame::OnMove(wxMoveEvent & event)
18 {
19     //獲取主窗口左上角的屏幕坐標
20     wxPoint size = event.GetPosition();
21     //修改靜態文本組件的值
22     st1->SetLabel(wxString::Format(_T("x: %d"), size.x));
23     st2->SetLabel(wxString::Format(_T("y: %d"), size.y));
24 }
25 //聲明應用程序
26 IMPLEMENT_APP(MyApp)
27 
28 bool MyApp::OnInit()
29 {
30     MyFrame * myFrame = new MyFrame(_T("MyFrame"));
31     myFrame->Show(true);
32 
33     return true;
34 }

  在這個例子中我們顯示了當前窗口的坐標

Connect(wxEVT_MOVE, wxMoveEventHandler(Move::OnMove));

  這裏我們把一個wxEVT_MOVE事件類型和OnMove()方法連接起來。

wxPoint size = event.GetPosition();

  在OnMove()方法中的event參數是一個特定事件的對象,在我們的例子中它是一個wxMoveEvent類的實例,這個對象存儲了關於這個事件的信息,我們可以調用GetPosition()方法來獲得當前窗口的坐標。

事件的傳遞

  wxWidgets有兩種事件,Basic事件和Command事件,它們在傳遞性方面有所不同。事件從子控件傳遞到父控件,依次往上傳遞。Basic事件不會傳遞而Command事件會傳遞。例如wxCloseEvent是一個Basic事件,對於這個事件而言傳遞到父窗口是沒有意義的。

  通常情況下,被事件處理函數捕獲的事件不會再傳遞到父窗口,為了使它傳遞上去,我們必須調用Skip()方法。

main.h

 1 //事件的傳遞
 2 #include <wx/wx.h>
 3 
 4 //分別定義了自己的wxFrame wxPanel wxButton,並定義了相應的點擊事件處理函數
 5 class Propagate : public wxFrame
 6 {
 7 public:
 8     Propagate(const wxString & title);
 9 
10     void OnClick(wxCommandEvent & event);
11 };
12 
13 class MyPanel : public wxPanel
14 {
15 public:
16     MyPanel(wxFrame * frame, wxWindowID id);
17 
18     void OnClick(wxCommandEvent & event);
19 };
20 
21 class MyButton : wxButton
22 {
23 public:
24     MyButton(MyPanel * panel, wxWindowID id, const wxString & label);
25 
26     void OnClick(wxCommandEvent & event);
27 };
28 
29 //定義應用程序類
30 class MyApp : public wxApp
31 {
32 public:
33     virtual bool OnInit();
34 };

main.cpp

 1 #include "main.h"
 2 
 3 const int ID_BUTTON = 1;
 4 
 5 Propagate::Propagate(const wxString & title)
 6          : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
 7 {
 8     //新建了一個面板組件,放在wxFrame上
 9     MyPanel * panel = new MyPanel(this, -1);
10     //定義了一個Button放在面板上
11     new MyButton(panel, ID_BUTTON, _T("OK"));
12     //按鈕點擊事件處理函數的動態關聯
13     Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
14             wxCommandEventHandler(Propagate::OnClick));
15 
16     Centre();
17 }
18 //在wxFrame的點擊事件處理函數中,對點擊事件進行了處理,關閉了主窗口界面
19 void Propagate::OnClick(wxCommandEvent & event)
20 {
21     wxMessageBox(_T("Event reach the frame class"));
22     //event.Skip();
23     Close(true);
24 }
25 
26 MyPanel::MyPanel(wxFrame * frame, wxWindowID id)
27        : wxPanel(frame, id)
28 {
29     //面板上button點擊事件處理函數的動態關聯
30     Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
31             wxCommandEventHandler(MyPanel::OnClick));
32 }
33 //在Panel的點擊事件處理函數中,並沒有對點擊事件進行處理,而是將其傳遞給了他的上層組件wxFrame
34 void MyPanel::OnClick(wxCommandEvent & event)
35 {
36     wxMessageBox(_T("Event reach the panel class"));
37     event.Skip();
38 }
39 
40 MyButton::MyButton(MyPanel * panel, wxWindowID id, const wxString & label)
41         : wxButton(panel, id, label, wxPoint(15, 15))
42 {
43     //Button點擊事件處理函數的動態管理
44     Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
45             wxCommandEventHandler(MyButton::OnClick));
46 }
47 //在Button的點擊事件處理函數中,並沒有對點擊事件進行處理,而是將其傳遞給了他的上層組件Panel
48 void MyButton::OnClick(wxCommandEvent & event)
49 {
50     wxMessageBox(_T("Event reach the button class"));
51     event.Skip();
52 }
53 //聲明應用程序
54 IMPLEMENT_APP(MyApp)
55 
56 bool MyApp::OnInit()
57 {
58     Propagate * prop = new Propagate(_T("Propagate"));
59     prop->Show(true);
60 
61     return true;
62 }

  在上面的例子中,我們把一個button放在panel上,然後把panel放在一個frame控件上,我們為每一個控件都定義了一個事件處理函數。當我們單機按鈕時,這個事件從button一直傳遞到了frame。嘗試去掉Skip()方法,看看會怎樣。

否決一個事件

有些時候我們需要停止處理一個事件,我們可以調用Veto()方法

main.h

 1 //否決一個事件
 2 #include <wx/wx.h>
 3 
 4 class Veto : public wxFrame
 5 {
 6 public:
 7     Veto(const wxString & title);
 8 
 9     void OnClose(wxCloseEvent & event);
10 };
11 
12 class MyApp : public wxApp
13 {
14 public:
15     virtual bool OnInit();
16 };

main.cpp

 1 #include "main.h"
 2 //主窗口類的實現
 3 Veto::Veto(const wxString & title)
 4     : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
 5 {
 6     //窗口關閉事件處理函數的動態關聯
 7     Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(Veto::OnClose));
 8     //讓窗口在屏幕中居中顯示
 9     Centre();
10 }
11 
12 void Veto::OnClose(wxCloseEvent & event)
13 {
14     //彈出對話框
15     wxMessageDialog * dial = new wxMessageDialog(NULL, _T("Are you sure to quit?"),
16                                                  _T("Question"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
17     //彈出對話框選項值的獲取
18     int ret = dial->ShowModal();
19     //對話框關閉
20     dial->Destroy();
21 
22     //根據對話框選項值采取相應的操作
23     if(ret == wxID_YES)
24     {
25         Destroy();
26     }
27     else
28     {
29         event.Veto();
30     }
31 }
32 //聲明應用程序
33 IMPLEMENT_APP(MyApp)
34 
35 bool MyApp::OnInit()
36 {
37     Veto * veto = new Veto(_T("Veto"));
38     veto->Show(true);
39 
40     return true;
41 }

  在我們的例子中,我們處理一個wxCloseEvent事件,當我們按下窗口標題欄右邊的X、輸入Alt+F4或者從系統菜單上把程序關閉時這個事件產生。在許多應用程序中,我們希望阻止窗口意外關閉。要實現它,我們必須要連接wxEVT_CLOSE_WINDOW這個事件類型。

1 wxMessageDialog * dial = new wxMessageDialog(NULL, _T("Are you sure to quit?"),
                        
_T("Question"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);

  在關閉事件產生之後,我們顯示了一個消息對話框。

1 if(ret == wxID_YES)
2     Destroy();
3 else
4     event.Veto();

  我們通過返回值確定是銷毀窗口還是阻止這個事件,註意,我們要銷毀一個窗口,必須要調用它的Destroy()方法。通過調用Close()方法會讓我們陷入一個無窮的循環。

窗口標識符

  窗口標識符是在事件系統中指定的唯一一個整數,有三種方法可以建立一個標識符。

    1、讓系統自動創建一個ID

    2、使用wxWidgets自帶的標準ID

    3、使用你自己的ID

  每一個控件都有一個ID參數,這個ID在整個事件系統中是獨一無二的。

1 wxButton(parent, -1);
2 wxButton(parent, wxID_ANY);

  如果我們把id參數設為-1或者wxID_ANY,wxWidgets會自動為我們創建一個ID,這個自動創建的ID總是一個負數,然而用戶指定的ID必須是正數。當我們不需要改變控件的狀態時,通常使用wxID_ANY這個選項,例如一個靜態的文本控件,它在整個程序的生命周期內都不會發生改變。但是我們仍然能夠指定我們自己的ID。有一個GetID()方法會返回控件的ID。

  只要有可能,就盡量使用標準的ID,這些標準ID提供了一些獨立於平臺的小圖形或者一些行為。

main.h

 1 //窗口標識符
 2 #include <wx/wx.h>
 3 //定義主窗口類
 4 class Ident : public wxFrame
 5 {
 6 public:
 7     Ident(const wxString & title);
 8 };
 9 
10 class MyApp : public wxApp
11 {
12 public:
13     virtual bool OnInit();
14 };

main.cpp

 1 #include "main.h"
 2 
 3 Ident::Ident(const wxString & title)
 4      : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(200, 150))
 5 {
 6     //定義了一個面板放在wxFrame上
 7     wxPanel * panel = new wxPanel(this, -1);
 8     //定義了一個wxGridSizer布局控件,2行 3列
 9     wxGridSizer * grid = new wxGridSizer(2, 3);
10 
11     //向wxGridSizer布局控件中添加組件
12     grid->Add(new wxButton(panel, wxID_CANCEL), 0, wxTOP | wxLEFT, 9);
13     grid->Add(new wxButton(panel, wxID_DELETE), 0, wxTOP, 9);
14 
15     grid->Add(new wxButton(panel, wxID_SAVE), 0, wxTOP | wxLEFT, 9);
16     grid->Add(new wxButton(panel, wxID_EXIT), 0, wxTOP, 9);
17 
18     grid->Add(new wxButton(panel, wxID_STOP), 0, wxTOP | wxLEFT, 9);
19     grid->Add(new wxButton(panel, wxID_NEW), 0, wxTOP, 9);
20 
21     //將wxGridSizer布局控件加載到Panel中
22     panel->SetSizer(grid);
23     Centre();
24 }
25 //聲明應用程序
26 IMPLEMENT_APP(MyApp)
27 
28 bool MyApp::OnInit()
29 {
30     Ident * ident = new Ident(_T("Ident"));
31     ident->Show(true);
32 
33     return true;
34 }

在我們的例子中,我們在按鈕上使用了標準標識符。正常情況下,在Linux系統下,按鈕上會顯示一個小圖標。(我用的Ubuntu14.04,不知為什麽,沒有圖標顯示)

效果展示:

技術分享圖片

6、wxWidgets 事件處理