目录
- 自定义事件
- AddPendingEvent()
- QueueEvent()
- PushEventHandler()
- ProcessEvent()
- wxCommandEvent与新的事件类型一起使用
自定义事件
AddPendingEvent()
virtual void wxEvtHandler::AddPendingEvent ( const wxEvent & event )
发布要稍后处理的事件。
此函数类似于QueueEvent(),但不能用于从工作线程中发布具有wxString字段的事件对象(即实际上大多数事件对象)的事件,因为不安全地使用了相同的wxString对象,这是因为原始事件对象中的wxString字段及其内部由此函数生成的副本在内部共享相同的字符串缓冲区。使用QueueEvent()可以避免这种情况。
事件的副本由函数生成,因此一旦函数返回,就可以删除原始事件(通常在堆栈上创建原始事件)。这要求wxEvent::Clone()方法由事件实现,以便在处理之前可以复制和存储它。
QueueEvent()
virtual void wxEvtHandler::QueueEvent ( wxEvent * event )
队列事件以供稍后处理。
此方法类似于ProcessEvent(),但后者是同步的,即在函数返回之前立即处理事件,此方法是异步的,并立即返回,而事件将在稍后的时间(通常在下一个事件循环迭代期间)处理。
另一个重要的区别是,该方法拥有事件参数的所有权,即它将自己删除它。这意味着应该在堆上分配事件,并且在函数返回后不能再使用指针(因为它可以随时删除)。
QueueEvent()可用于从工作线程到主线程的线程间通信,它在内部使用锁定,并通过确保调用线程不再使用事件对象来避免AddPendingEvent()文档中提到的问题,这是安全的。仍应注意避免此对象的某些字段被它使用,特别是事件对象的任何wxString成员都不能是另一个wxString对象的浅层副本,因为这将导致它们在幕后仍然使用相同的字符串缓冲区。例如:
void FunctionInAWorkerThread(const wxString& str)
{
wxCommandEvent* evt = new wxCommandEvent;
// NOT evt->SetString(str) as this would be a shallow copy
evt->SetString(str.c_str()); // make a deep copy
wxTheApp->QueueEvent( evt );
}
请注意,可以使用wxThreadEvent而不是wxCommandEvent来避免此问题:
void FunctionInAWorkerThread(const wxString& str)
{
wxThreadEvent evt;
evt.SetString(str);
// wxThreadEvent::Clone() makes sure that the internal wxString
// member is not shared by other wxString instances:
wxTheApp->QueueEvent( evt.Clone() );
}
最后注意,如果事件循环当前处于空闲状态,该方法会通过调用wxWakeUpIdle()自动唤醒事件循环,因此在使用它时无需手动执行。
参数
要添加到挂起事件队列的事件事件。
PushEventHandler()
void wxWindow::PushEventHandler ( wxEvtHandler * handler )
将此事件处理程序推送到窗口的事件堆栈上。
事件处理程序是能够处理发送到窗口的事件的对象。默认情况下,窗口是它自己的事件处理程序,但应用程序可能希望替换另一个,例如,以允许对各种不同的窗口类集中实现事件处理。
wxWindow::PushEventHandler允许应用程序设置事件处理程序堆栈,其中未由一个事件处理程序处理的事件被传递给链中的下一个事件。
如果您有两个事件处理程序A和B以及一个wxWindow实例W,并且您调用:
W->PushEventHandler(A);
W->PushEventHandler(B);
您将遇到以下情况:
注意,可以使用wxWindow::PopEventHandler删除事件处理程序。
参数
handler指定要推送的处理程序。它不能是wxEvtHandler链的一部分;如果未取消链接,断言将失败(请参阅wxEvtHandler::IsUnlinked)。
ProcessEvent()
virtual bool wxEvtHandler::ProcessEvent ( wxEvent & event )
此函数在wxEvtHandler中是公共的,但在wxWindow中受到保护,因为对于wxWindows,您应该始终对GetEventHandler()返回的指针调用ProcessEvent(),而不是对wxWindow对象本身调用。
为方便起见,提供了ProcessWindowEvent()方法作为
GetEventHandler()->ProcessEvent()
请注意,仍然可以直接在wxWindow对象上调用这些函数(例如,将其转换为wxEvtHandler),但当涉及推送事件处理程序的窗口时,这样做会产生微妙的错误。
这也适用于所有其他wxEvtHandler函数。
从wxEvtHandler重新实现。
处理事件,搜索事件表并调用零个或多个合适的事件处理程序函数。
通常,您的应用程序不会调用此函数:它在wxWidgets实现中被调用,以将传入的用户界面事件分派给框架(和应用程序)。
但是,如果在定义新事件类型的地方实现新功能(例如新控件),而不是允许用户重写虚拟函数,则可能需要调用它。
请注意,通常不需要重写ProcessEvent()来定制事件处理,重写专门提供的TryBefore()和TryAfter()函数通常就足够了。例如,wxMDIParentFrame可以重写TryBefore(),以确保菜单事件在父帧本身处理之前在活动子帧中处理。
事件表搜索的正常顺序如下:
1.wxApp::FilterEvent()被调用。如果它返回除-1(默认值)以外的任何值,则处理在此停止。
2.TryBefore()被调用(这是wxWindow对象考虑wxValidator的地方)。如果返回true,则函数退出。
3.如果对象被禁用(通过调用wxEvtHandler::SetEvtHandlerEnabled),则函数跳到步骤(7)。
4.使用Bind<>()绑定的处理程序的动态事件表按照最近绑定到最早绑定的顺序进行搜索。如果找到了一个处理程序,则执行该处理程序,并且函数返回true,除非处理程序使用wxEvent::Skip()表示它没有处理该事件,在这种情况下,搜索将继续。
5.使用事件表宏绑定的处理程序的静态事件表将按照源代码中事件表宏的出现顺序搜索此事件处理程序。如果失败,则尝试基类事件表,依此类推,直到不再存在表或找到合适的函数。如果找到了处理程序,则应用与上一步相同的逻辑。
6.搜索应用于整个事件处理程序链(通常链的长度为1)。此链可以使用wxEvtHandler::SetNextHandler()形成:
(参考图片,如果调用了A->ProcessEvent,但它不处理该事件,则会调用B->ProcessEvent等等…)。注意,在wxWindow的情况下,您可以构建一个事件处理程序堆栈(有关详细信息,请参阅wxWindow::PushEventHandler())。如果链的任何处理程序返回true,则函数退出。
7.TryAfter()被调用:对于wxWindow对象,这可能会将事件传播到窗口父级(递归)。如果事件仍然未处理,则wxTheApp对象上的ProcessEvent()将作为最后一步调用。
注意,步骤(2)-(6)是在这个函数调用的ProcessEventLocally()中执行的。
参数
event要处理的事件。
退换商品
如果找到并执行了合适的事件处理程序函数,并且该函数未调用wxEvent::Skip,则为true。
wxCommandEvent与新的事件类型一起使用
如果您只想将wxCommandEvent与新的事件类型一起使用,请使用下面列出的通用事件表宏之一,而不必自己定义新的事件类。
// this is typically in a header: it just declares MY_EVENT event type
wxDECLARE_EVENT(MY_EVENT, wxCommandEvent);
// this is a definition so can't be in a header
wxDEFINE_EVENT(MY_EVENT, wxCommandEvent);
// example of code handling the event with event tables
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU (wxID_EXIT, MyFrame::OnExit)
...
EVT_COMMAND (ID_MY_WINDOW, MY_EVENT, MyFrame::OnMyEvent)
wxEND_EVENT_TABLE()
void MyFrame::OnMyEvent(wxCommandEvent& event)
{
// do something
wxString text = event.GetString();
}
// example of code handling the event with Bind<>():
MyFrame::MyFrame()
{
Bind(MY_EVENT, &MyFrame::OnMyEvent, this, ID_MY_WINDOW);
}
// example of code generating the event
void MyWindow::SendEvent()
{
wxCommandEvent event(MY_EVENT, GetId());
event.SetEventObject(this);
// Give it some contents
event.SetString("Hello");
// Do send it
ProcessWindowEvent(event);
}