wx事件处理部分

 

事件处理

目录

介绍
它是如何工作的
事件跳转
禁止事件
加挂事件句柄

在上一章里已经学习了如何处理菜单事件。这章将解释事件处理是如何工作的,同时解释了如何去处理其它事件。

介绍Introduction

事件是出现在程序内部或外部的一些事情。一个事件可能是通过用户或是其它程序、操作系统等来触发的。这时需要一个机制来让程序对期望的事件产生反应。

在wxWindows的早期版本中,程序的事件处理是完全通过回调函数或者重载虚拟函数来实现的。从wxWindows2.0开始转而使用事件表了。如例3,事件表告诉wxWindows将事件映射到成员函数。事件表是在实现文件(cpp)中声明的。

例 3.1.事件表

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_EXIT, MyFrame::OnExit)
EVT_SIZE(MyFrame::OnSize)
EVT_BUTTON(BUTTON1, MyFrame::OnButton1)
END_EVENT_TABLE()

上面的事件表告诉wxWindows当它接收到一个WM_SIZE事件时调用MyFrame的成员函数OnSize。 宏 BEGIN_EVENT_TABLE 声明wxFrame和它的子类MyFrame拥有这个事件表。

处理事件的成员函数不能是虚拟的。实际上事件处理器将忽略虚拟声明。事件处理函数有类似的形式:返回类型为void并且接受一个事件参数。这个参数的类型与具体的事件相关。对于size事件,使用的类型是wxCommandEvent。当控件变的更复杂时它们使用自己的事件类。

在类定义中,必须有一个DECLARE_EVENT_TABLE宏。参见例2.11。

所有这些宏隐藏了复杂的事件处理系统。
它是如何工作的?

当接收到一个事件时,wxWindows首先调用窗口上产生事件的对象上的wxEventHandler的ProcssEvent处理器。wxWindow(和其它的窗口类)继承自wxEventHander。ProcessEvent查找事件表里的每一个事件并且调用零或多个事件处理器函数。下面是处理一个事件的步骤:

1. 当对象关闭时(包括wxEvtHandler的SetEvtHandle)函数跳转到第六步。
2. 如果对象是一个wxWindow对象,ProcessEvent在窗口的wxValidator上递归调用。如果返回真函数退出。
3. SearchEventTable是事件处理器调用的函数。当它失败时,开始在基类 上尝试直到没有更多的事件表或者发现了一个合适的函数时这个函数退出。被发现的函数开始执行。
4. 查找过程应用于整个事件处理器链,当应用成功时(表示事件处理完毕)函数退出。
5. 当对象是一个wxWindow对象时,并且事件为wxCommandEvent类型时,ProcessEvent向上递归应用于父窗口的事件处理器。当它返回真,函数退出。这可以让一个按钮的父亲来处理按钮的点击而不是让按钮自己来处理。
6. 最后ProcessEvent 在wxApp对象上调用。

事件跳转Event skipping

ProcessEvent在发现一个可以处理事件的函数后退出。这表示当你的类对一个事件起反应时,下层的类不会得到这个事件。有时我们不希望这样。这个问题可以根据基类的事件类型用wxEvent类的Skip方法来解决,使事件处理器的查找继续进行。

下面的例子展示了事件跳转的用处。例3.2是一个文本控件的定义,它只接受数字字符。

例 3.2. NumTextCtrl.h

class NumTextCtrl : public wxTextCtrl
{
public:
NumTextCtrl(wxWindow *parent);
void OnChar(wxKeyEvent& event);
protected:
DECLARE_EVENT_TABLE()
};

当NumericTextCtrl接收到一个键盘事件时,就进行keycode检查。如果输入的是数字,基类wxTextCtrl就可以处理这个事件。这就是我们要对这个事件使用跳转的原因。你必须在这调用Skip方法,否则基类不会处理任何键。

例3.3. NumTextCtrl.cpp

// For compilers that supports precompilation , includes "wx/wx.h"
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "NumTextCtrl.h"
#include <ctype.h>
NumTextCtrl::NumTextCtrl(wxWindow *parent) : wxTextCtrl(parent, -1)
{
}
void NumTextCtrl::OnChar(wxKeyEvent& event)
{
if ( isdigit(event.GetKeyCode()) )
{
    // Numeric characters are allowed, so the base class wxTextCtrl
    // is allowed to process the event. This is done using the Skip() method.
    event.Skip();
}
else
{
    // Character is not allowed.
    wxBell();
}
}
BEGIN_EVENT_TABLE(NumTextCtrl, wxTextCtrl)
EVT_CHAR(NumTextCtrl::OnChar)
END_EVENT_TABLE()

禁止事件

一些事件是可以禁止的。当你禁止一个事件时,这个事件不会被进一步处理。例3.4演示了,当一个文本控件内的文本改变后如何禁止这个简单文本编辑器的关闭事件。这表示当在用户还没有保存改变后的文本内容时这个窗口不能被关闭。

例 3.4. 禁止事件

void TextFrame::OnClose(wxCloseEvent& event)
{
   bool destroy = true;
   if ( event.CanVeto() )
   {
if ( m_pTextCtrl->IsModified() )
{
   wxMessageDialog *dlg =
            new wxMessageDialog(this, "Text is changed!\nAre you sure you want to exit?",
                                  "Text changed!!!", wxYES_NO | wxNO_DEFAULT);
   int result = dlg->ShowModal();
   if ( result == wxID_NO )
   {
       event.Veto();
       destroy = false;
   }
}
   }
   if ( destroy )
   {
Destroy();
   }

当CanVeto返回false时程序作什么呢?你将不能禁止这个事件你的程序将会退出。
阻塞事件处理器Plug an event handler

考虑下面的问题:每个菜单命令都必须被记录。一个解决方案是创建一个在每个命令事件处理函数中调用的函数。这种方法带来的问题是使维护变得十分困难。当添加一个新的菜单并且没有调用这个函数时,这个菜单命令将不被记录。

解决这个问题是去创建一个新的事件处理器并添加到一个wxWindow类。要完成它需要创建一个从wxEvtHandler派生的新类。在新类中处理事件与一个正常的窗口是相同的。

例 3.5. LogEventHandler.h -LogEventHandler的定义

#ifndef _LogEventHandler_H
#define _LogEventHandler_H
class LogEventHandler : public wxEvtHandler
{
public:
   LogEventHandler() : wxEvtHandler()
   {
   }
   virtual ~LogEventHandler()
   {
   }
protected:
   DECLARE_EVENT_TABLE()
   void OnMenu(wxMenuEvent &event);
private:
};
#endif // _LogEventHandler_H

例3.6. LogEventHandler.cpp -   LogEventHandler的实现

// For compilers that supports precompilation , includes "wx/wx.h"
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "LogEventHandler.h"
void LogEventHandler::OnMenu(wxMenuEvent &event)
{
   wxLogMessage("Someone selected a menu item");
   event.Skip();
}
BEGIN_EVENT_TABLE(LogEventHandler, wxEvtHandler)
EVT_MENU_RANGE(-1, -1, LogEventHandler::OnMenu)
END_EVENT_TABLE()

在宏EVT_MENU_RANGE里,所有的菜单事件都可以被处理。前两个参数用来指定要处理的菜单的ID范围。-1表示处理所有的菜单项。不要忘记在事件上调用Skip,否则不会有任何事件传递到类wxWindow的事件处理器。

下一步是把新的事件处理器压入处理器栈。这是使用wxWindow的PushEventHandler来完成的。要从栈上移除事件处理器使用PopEventHandler。

PushEventHandler(new LogEventHandler());

PopEventHandler 有一个布尔类型的参数,当这个参数为真时,wxWindows删除事件处理器。注意在没有事件处理器被取出时,wxWndows将在窗口撤消时尝试删除事件处理器。这在访问在栈上创建的事件处理器时会发生一个访问错误。可以在一个类撤消之前调用使用false参数的PopeventHandler来避免这个问题。

如何定义个新的事件? As following:
有两类方法:
- 定义一个全新的事件类,典型的是从wxEvent或wxCommandEvent派生出来。
- 使用现有的事件类(event class),但是给以新的类型(type)。
在这两类方法中都必须声明这个新的事件类型:
// in the header of the source file
DECLARE_EVENT_TYPE(name, value)

// in the implementation
DEFINE_EVENT_TYPE(name)

这里,value是没什么意义的,只是为了和wxWidgets 2.0.x保持兼容。

◎ 使用现有的事件类
如果你仅仅想使wxCommandEvent赋予一个新的类型,可以使用如下的方式
(这样可以不必定义一个新的事件类,也不用实现使用于线程见通信时使用的wxEvent::Clone())
DECLARE_EVENT_TYPE(wxEVT_MY_EVENT, -1)

DEFINE_EVENT_TYPE(wxEVT_MY_EVENT)

// user code intercepting the event

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
   EVT_MENU (wxID_EXIT, MyFrame::OnExit)
   // ....
   EVT_COMMAND   (ID_MY_WINDOW, wxEVT_MY_EVENT, MyFrame::OnMyEvent)
END_EVENT_TABLE()

void MyFrame::OnMyEvent( wxCommandEvent &event )
{
// do something
wxString text = event.GetText();
}


// user code sending the event

void MyWindow::SendEvent()
{
wxCommandEvent event( wxEVT_MY_EVENT, GetId() );
event.SetEventObject( this );
// Give it some contents
event.SetText( wxT("Hallo") );
// Send it
GetEventHandler()->ProcessEvent( event );
}


◎定义全新的事件类
这某些情况下,你需要定义全新的事件类以实现复杂数据的传输。例子:

// code defining event

class wxPlotEvent: public wxNotifyEvent
{
public:
wxPlotEvent( wxEventType commandType = wxEVT_NULL, int id = 0 );

// accessors
wxPlotCurve *GetCurve()
       { return m_curve; }

// required for sending with wxPostEvent()
wxEvent* Clone();

private:
wxPlotCurve *m_curve;
};

DECLARE_EVENT_MACRO( wxEVT_PLOT_ACTION, -1 )

typedef void (wxEvtHandler::*wxPlotEventFunction)(wxPlotEvent&);

#define EVT_PLOT(id, fn) \
DECLARE_EVENT_TABLE_ENTRY( wxEVT_PLOT_ACTION, id, -1, \
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) (wxNotifyEventFunction) \
wxStaticCastEvent( wxPlotEventFunction, & fn ), (wxObject *) NULL ),


// code implementing the event type and the event class

DEFINE_EVENT_TYPE( wxEVT_PLOT_ACTION )

wxPlotEvent::wxPlotEvent( ...

// user code intercepting the event

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
   EVT_PLOT   (ID_MY_WINDOW,   MyFrame::OnPlot)
END_EVENT_TABLE()

void MyFrame::OnPlot( wxPlotEvent &event )
{
wxPlotCurve *curve = event.GetCurve();
}


// user code sending the event

void MyWindow::SendEvent()
{
wxPlotEvent event( wxEVT_PLOT_ACTION, GetId() );
event.SetEventObject( this );
event.SetCurve( m_curve );
GetEventHandler()->ProcessEvent( event );
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值