• 6、wxWidgets 事件处理


    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,不知为什么,没有图标显示)

    效果展示:

  • 相关阅读:
    疲劳原理
    golang中的 time 常用操作
    access与excel
    数据结构正式篇!初探!!
    数据结构复习之C语言malloc()动态分配内存概述
    C语言字符数组与字符串
    数据结构复习之C语言指针与结构体
    c语言数组
    数据结构
    C语言腾讯课堂(一)
  • 原文地址:https://www.cnblogs.com/Long-w/p/9591929.html
Copyright © 2020-2023  润新知