[ZETCODE]wxWidgets教程六:事件处理
本教程原文链接:http://zetcode.com/gui/wxwidgets/events/
翻译:瓶哥
日期:2013年12月7号星期六
主页: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()
};
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(, ))
{
wxPanel * panel = new wxPanel(this, wxID_ANY); wxButton * button = new wxButton(panel, wxID_EXIT, _T("Quit"), wxPoint(, )); Centre();
} void MyButton::OnQuit(wxCommandEvent & WXUNUSED(event))
{
Close(true);
}
main.h
#include <wx/wx.h> class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
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;
}
在我们的程序中,我们创建了一个简单的按钮,当我们点击按钮时,应用程序关闭。
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;
};
move.cpp
#include "move.h" Move::Move(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(, ))
{
wxPanel * panel = new wxPanel(this, -); st1 = new wxStaticText(panel, wxID_ANY, _T(""), wxPoint(, ));
st2 = new wxStaticText(panel, wxID_ANY, _T(""), wxPoint(, )); 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));
}
main.h
#include <wx/wx.h> class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
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;
}
在这个例子中我们显示了当前窗口的坐标
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);
};
propagate.cpp
#include "propagate.h" const int ID_BUTTON = ; Propagate::Propagate(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(, ))
{
MyPanel * panel = new MyPanel(this, -); 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(, ))
{
Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(MyButton::OnClick));
} void MyButton::OnClick(wxCommandEvent & event)
{
wxMessageBox(_T("Event reach the button class"));
event.Skip();
}
main.h
#include <wx/wx.h> class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
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;
}
在我们的例子中,我们把一个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);
};
veto.cpp
#include "veto.h" Veto::Veto(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(, ))
{
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();
}
}
main.h
#include <wx/wx.h> class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
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;
}
在我们的例子中,我们处理一个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()方法会让我们陷入一个无穷的循环。
窗口标识符
窗口标识符是在事件系统中指定的唯一一个整数,有三种方法可以建立一个标识符。
- 让系统自动创建一个ID
- 使用wxWidgets自带的标准ID
- 使用你自己的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);
};
ident.cpp
#include "ident.h" Ident::Ident(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(, ))
{
wxPanel * panel = new wxPanel(this, -); wxGridSizer * grid = new wxGridSizer(, ); grid->Add(new wxButton(panel, wxID_CANCEL), , wxTOP | wxLEFT, );
grid->Add(new wxButton(panel, wxID_DELETE), , wxTOP, );
grid->Add(new wxButton(panel, wxID_SAVE), , wxLEFT, );
grid->Add(new wxButton(panel, wxID_EXIT));
grid->Add(new wxButton(panel, wxID_STOP), , wxLEFT, );
grid->Add(new wxButton(panel, wxID_NEW)); panel->SetSizer(grid);
Centre();
}
main.h
#include <wx/wx.h> class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
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;
}
在我们的例子中,我们在按钮上使用了标准标识符。在Linux上,按钮上会显示一个小图标。
PS:我是win7所以没有图标,但是你仍可以使用bitmapbutton去指定图标。
在这一章中我们讨论了wxWidgets中的事件。
[ZETCODE]wxWidgets教程六:事件处理的更多相关文章
- [ZETCODE]wxWidgets教程八:组件专题1
本教程原文链接:http://zetcode.com/gui/wxwidgets/widgets/ 翻译:瓶哥 日期:2013年12月12日星期四 邮箱:414236069@qq.com 主页:htt ...
- [ZETCODE]wxWidgets教程七:对话框
本教程原文链接:http://zetcode.com/gui/wxwidgets/dialogs/ 翻译:瓶哥 日期:2013年12月9日星期一 邮箱:414236069@qq.com 主页:http ...
- [ZETCODE]wxWidgets教程五:布局管理
本教程原文链接:http://zetcode.com/gui/wxwidgets/layoutmanagement/ 翻译:瓶哥 日期:2013年12月4日星期三 邮箱:414236069@qq.co ...
- [ZETCODE]wxWidgets教程四:菜单栏和工具栏
本教程原文链接:http://zetcode.com/gui/wxwidgets/menustoolbars/ 翻译:瓶哥 日期:2013年11月28日星期四 邮箱:414236069@qq.com ...
- [ZETCODE]wxWidgets教程三:第一个窗体程序
本教程原文链接:http://zetcode.com/gui/wxwidgets/firstprograms/ 翻译:瓶哥 日期:2013年11月27日星期三 邮箱:414236069@qq.com ...
- [ZETCODE]wxWidgets教程九:组件专题2
本教程原文链接:http://zetcode.com/gui/wxwidgets/widgetsII/ 翻译:瓶哥 日期:2013年12月15日星期日 邮箱:414236069@qq.com 主页:h ...
- [ZETCODE]wxWidgets教程二:辅助类
本教程原文链接:http://zetcode.com/gui/wxwidgets/helperclasses/ 翻译:瓶哥 日期:2013年11月27日星期三 邮箱:414236069@qq.com ...
- [ZETCODE]wxWidgets教程一:介紹
本教程原文链接:http://zetcode.com/gui/wxwidgets/introduction/ 翻译:瓶哥 日期:2013年11月26日星期二 邮箱: 414236069@qq.com ...
- CRL快速开发框架系列教程六(分布式缓存解决方案)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
随机推荐
- php hook 之简单例子
<?php// 应用单例模式// 建立相应的 plugins 文件夹,并建立 .php 文件放在里面class plugin{ public $actions; public $fi ...
- python开发-web框架之diango-----Models
这篇博客是紧连上一篇博客的,因为内容较多,这里介绍的是Models这一部分的内容 七:Models 数据库的配置 1 django默认支持sqlite,mysql, oracle,postgre ...
- NOIP2015 普及组(Junior) 解题报告
1. 金币 (coin.cpp/c/pas) 国王将金币作为工资,发放给忠诚的骑士.第一天,骑士收到一枚金币:之后两天(第二天和第三天),每天收到两枚金币:之后三天(第四.五.六天),每天收到三枚金币 ...
- Linux Makefile analysis for plain usr
一.本文主旨 笔者写了一篇linux内核Makefile整体分析 ,测重于理论分析,对于实际应用不算对头,所以需要写一篇实用性较强的文章,为以后内核.驱动移植做好铺垫. 二.本文内容概要 1.编译哪些 ...
- CocoaPods简单使用
CocoaPods的原理 CocoaPods的原理是将所有的依赖库都放到另一个名为Pods的项目中,然后让主项目依赖Pods项目,这样,源码管理工作都从主项目移到了Pods项目中.Pods项目最终会编 ...
- FFMPEG视音频编解码零基础学习方法-b
感谢大神分享,虽然现在还看不懂,留着大家一起看啦 PS:有不少人不清楚“FFmpeg”应该怎么读.它读作“ef ef em peg” 0. 背景知识 本章主要介绍一下FFMPEG都用在了哪里(在这里仅 ...
- CocoaPods安装和使用及问题:Setting up CocoaPods master repo-b
目录 CocoaPods是什么? 如何下载和安装CocoaPods? 如何使用CocoaPods? 场景1:利用CocoaPods,在项目中导入AFNetworking类库 场景2:如何正确编译运行一 ...
- SQL Express几个版本的区别
对于这三个文件:SQLEXPR32_x86_CHS.exe.SQLEXPR_x86_CHS.exe. SQLEXPR_x64_CHS.exe,大家一看就知道是sqlserver的express版本,但 ...
- ajax 同步异步调用
- BZOJ 1231: [Usaco2008 Nov]mixup2 混乱的奶牛
Description 混乱的奶牛 [Don Piele, 2007] Farmer John的N(4 <= N <= 16)头奶牛中的每一头都有一个唯一的编号S_i (1 <= S ...