6wxWidgets 事件处理
Posted long-w
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了6wxWidgets 事件处理相关的知识,希望对你有一定的参考价值。
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,不知为什么,没有图标显示)
效果展示:
以上是关于6wxWidgets 事件处理的主要内容,如果未能解决你的问题,请参考以下文章
Android YouTube Player API Fragment无法手动处理触摸事件
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段