[ZETCODE]wxWidgets教程六:事件處理


本教程原文鏈接:http://zetcode.com/gui/wxwidgets/events/

翻譯:瓶哥

日期:2013年12月7號星期六

郵箱:414236069@qq.com

主頁:http://www.cnblogs.com/pingge/

若有翻譯錯誤或者歧義請聯系我!

 

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

 

定義

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

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

 

一個簡單的事件處理例子

在wxWidgets里面使用事件的傳統方法是使用一個靜態事件表,這是受MFC的影響。一個更加靈活和現代的方法是使用Connect()方法,因為這種方式優於事件表,我在wxWidgets教程中全部使用第二種方法。

 

靜態事件表

在接下的一個例子中,我們會教你如何使用一個靜態事件表。

button.h

#include <wx/wx.h>

class MyButton : public wxFrame
{
public :
    MyButton(const wxString & title);

    void OnQuit(wxCommandEvent & event);

private:
    DECLARE_EVENT_TABLE()
};
View Code

button.cpp

#include "button.h"

BEGIN_EVENT_TABLE(MyButton, wxFrame)
    EVT_BUTTON(wxID_EXIT, MyButton::OnQuit)
END_EVENT_TABLE()

MyButton::MyButton(const wxString & title)
        : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 150))
{
    wxPanel * panel = new wxPanel(this, wxID_ANY);

    wxButton * button = new wxButton(panel, wxID_EXIT, _T("Quit"), wxPoint(20, 20));

    Centre();
}

void MyButton::OnQuit(wxCommandEvent & WXUNUSED(event))
{
    Close(true);
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "button.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    MyButton * button = new MyButton(_T("Button"));
    button->Show(true);

    return true;
}
View Code

 

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

private:

    DECLARE_EVENT_TABLE()

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

BEGIN_EVENT_TABLE(MyButton, wxFrame)

     EVT_BUTTON(wxID_EXIT, MyButton::OnQuit)

END_EVENT_TABLE()

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

 

一個使用Connect()的例子

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

move.h

#include <wx/wx.h>

class Move : public wxFrame
{
public:
    Move(const wxString & title);

    void OnMove(wxMoveEvent & event);

    wxStaticText * st1;
    wxStaticText * st2;
};
View Code

move.cpp

#include "move.h"

Move::Move(const wxString & title)
    : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
    wxPanel * panel = new wxPanel(this, -1);

    st1 = new wxStaticText(panel, wxID_ANY, _T(""), wxPoint(10, 10));
    st2 = new wxStaticText(panel, wxID_ANY, _T(""), wxPoint(10, 30));

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

    Centre();
}

void Move::OnMove(wxMoveEvent & event)
{
    wxPoint size = event.GetPosition();

    st1->SetLabel(wxString::Format(_T("x: %d"), size.x));
    st2->SetLabel(wxString::Format(_T("y: %d"), size.y));
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "move.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Move * move = new Move(_T("Move"));
    move->Show(true);

    return true;
}
View Code

 

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

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()方法。

propagate.h

#include <wx/wx.h>

class Propagate : public wxFrame
{
public:
    Propagate(const wxString & title);

    void OnClick(wxCommandEvent & event);
};

class MyPanel : public wxPanel
{
public:
    MyPanel(wxFrame * frame, wxWindowID id);

    void OnClick(wxCommandEvent & event);
};

class MyButton : wxButton
{
public:
    MyButton(MyPanel * panel, wxWindowID id, const wxString & label);

    void OnClick(wxCommandEvent & event);
};
View Code

propagate.cpp

#include "propagate.h"

const int ID_BUTTON = 1;

Propagate::Propagate(const wxString & title)
         : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
    MyPanel * panel = new MyPanel(this, -1);

    new MyButton(panel, ID_BUTTON, _T("OK"));

    Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(Propagate::OnClick));

    Centre();
}

void Propagate::OnClick(wxCommandEvent & event)
{
    wxMessageBox(_T("Event reach the frame class"));
    event.Skip();
}

MyPanel::MyPanel(wxFrame * frame, wxWindowID id)
       : wxPanel(frame, id)
{
    Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(MyPanel::OnClick));
}

void MyPanel::OnClick(wxCommandEvent & event)
{
    wxMessageBox(_T("Event reach the panel class"));
    event.Skip();
}

MyButton::MyButton(MyPanel * panel, wxWindowID id, const wxString & label)
        : wxButton(panel, id, label, wxPoint(15, 15))
{
    Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(MyButton::OnClick));
}

void MyButton::OnClick(wxCommandEvent & event)
{
    wxMessageBox(_T("Event reach the button class"));
    event.Skip();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "propagate.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Propagate * prop = new Propagate(_T("Propagate"));
    prop->Show(true);

    return true;
}
View Code

 

在我們的例子中,我們把一個button放在panel上,然后把panel放在一個frame控件上,我們為每一個控件都定義了一個事件處理函數。

當我們單機按鈕時,這個事件從button一直傳遞到了frame。

嘗試去掉Skip()方法,看看會怎樣。

 

否決一個事件

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

veto.h

#include <wx/wx.h>

class Veto : public wxFrame
{
public:
    Veto(const wxString & title);

    void OnClose(wxCloseEvent & event);
};
View Code

veto.cpp

#include "veto.h"

Veto::Veto(const wxString & title)
    : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
    Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(Veto::OnClose));

    Centre();
}

void Veto::OnClose(wxCloseEvent & event)
{
    wxMessageDialog * dial = new wxMessageDialog(NULL, _T("Are you sure to quit?"),
                                                 _T("Question"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
    int ret = dial->ShowModal();
    dial->Destroy();

    if(ret == wxID_YES)
    {
        Destroy();
    }
    else
    {
        event.Veto();
    }
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "veto.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Veto * veto = new Veto(_T("Veto"));
    veto->Show(true);

    return true;
}
View Code

 

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

wxMessageDialog * dial = new wxMessageDialog(NULL, _T("Are you sure to quit?"),

_T("Question"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);

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

if(ret == wxID_YES)

    Destroy();

else

    event.Veto();

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

 

窗口標識符

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

  1. 讓系統自動創建一個ID
  2. 使用wxWidgets自帶的標准ID
  3. 使用你自己的ID

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

wxButton(parent, -1);

wxButton(parent, wxID_ANY);

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

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

ident.h

#include <wx/wx.h>

class Ident : public wxFrame
{
public:
    Ident(const wxString & title);
};
View Code

ident.cpp

#include "ident.h"

Ident::Ident(const wxString & title)
     : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(200, 150))
{
    wxPanel * panel = new wxPanel(this, -1);

    wxGridSizer * grid = new wxGridSizer(2, 3);

    grid->Add(new wxButton(panel, wxID_CANCEL), 0, wxTOP | wxLEFT, 9);
    grid->Add(new wxButton(panel, wxID_DELETE), 0, wxTOP, 9);
    grid->Add(new wxButton(panel, wxID_SAVE), 0, wxLEFT, 9);
    grid->Add(new wxButton(panel, wxID_EXIT));
    grid->Add(new wxButton(panel, wxID_STOP), 0, wxLEFT, 9);
    grid->Add(new wxButton(panel, wxID_NEW));

    panel->SetSizer(grid);
    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "ident.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Ident * ident = new Ident(_T("Ident"));
    ident->Show(true);

    return true;
}
View Code

 

在我們的例子中,我們在按鈕上使用了標准標識符。在Linux上,按鈕上會顯示一個小圖標。

PS:我是win7所以沒有圖標,但是你仍可以使用bitmapbutton去指定圖標。

 

在這一章中我們討論了wxWidgets中的事件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM