• [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中的事件。

  • 相关阅读:
    面试总结
    回溯-递归练习题集
    2018年度业余学习总结
    12306余票高效查询--clojure实现
    Mac中wireshark如何抓取HTTPS流量?
    2017年学习总结
    Clojure——学习迷宫生成
    新生帝之2016年的计划,努力去做好!
    EmitMapper
    ASP.NET 修改地址返回图片缩率图
  • 原文地址:https://www.cnblogs.com/pingge/p/3462739.html
Copyright © 2020-2023  润新知