Custom Events in wx2.8 and earlier

From WxWiki
Jump to navigation Jump to search

Custom Events

HopeSeekr of xMule has composed a tutorial: http://www.xmule.ws/phpnuke/modules.php?name=News&file=article&sid=76 old link.

See the Event handling overview in the official docs.

EVT_FOO(-1, SomeClass::SomeFunc) is a purely passive declaration. It says, "If a FOO happens to come my way, let me know about it." It doesn't tell any particular FOO-generating class to notify this class about FOO events. But without this, it will not respond to FOO events at all, even if explicitly sent to an object of this class.

wxButton events seem to work automatically because the EVT_BUTTON is generated in the wxButton itself, and since it's a command event it propogates to its parents automatically. Thus, no call to SetEventHandler() or similar is necessary.

wxSocket events do not exist in a chain of parents, so SetEventHandler() is necessary. This establishes the connection between an event and a particular instance of an object.

Note that there are other strategies for the event dispatching as well, except the first 2 already mentioned above and which I'm going to repeat for completeness:

  • send the event to oneself (possibly relying on it to be propagated to the parent)
  • send the event to another event handler explicitly associated with us
  • send the event to all top level windows (possibly relying on them to propagate the event *downwards* to their children recursively; example: EVT_IDLE)
  • send the event to wxTheApp only

In subclassing wxEvent, remember to associate your event (wxFooEvent) to your event type (wxEVT_FOO) in the constructor:


 wxFooEvent(int id=0)
    : wxEvent (id, wxEVT_FOO)
 {
    ...
 }


To get a unique wxEVT_FOO, use wxEVT_FOO = wxNewEventType();

Creating a custom event (with the new method)

Example of custom event creation managing a click (by Marco Cavallini)

 // Myclass.h
 const newEVT_MYCLASS_FIRST = wxEVT_FIRST + 5400;
 
 const wxEventType newEVT_MYCLASS_CLICK	= newEVT_MYCLASS_FIRST + 1;
 
 #define EVT_MYCLASS_CLICK(id, fn)	\
 	DECLARE_EVENT_TABLE_ENTRY(newEVT_MYCLASS_CLICK, id, -1, \
 	(wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)&fn, \
 	(wxObject*)NULL ),

In the class you have to manage the event as you like and send your custom one to the event table

 // Myclass.h
 
 void Myclass::OnMouse(wxMouseEvent& event)
 {
   if (event.LeftIsDown())
   {
      wxCommandEvent event(kwxEVT_BITBUTTON_CLICK, GetId());
      event.SetEventObject(this);
      ProcessCommand(event);
   }
 }

Quick & dirty custom events

This was tested under wxWidgets 2.4.2 (GTK) and seems to be less complicated than the other methods here. It was derived from the event.cpp example, and uses a wxCommandEvent to pass data.

First, the declaration (needed to scope the event) and then the definition:

 BEGIN_DECLARE_EVENT_TYPES()
 DECLARE_LOCAL_EVENT_TYPE(myEVT_SOMETHINGHAPPENED,wxNewEventType())
 END_DECLARE_EVENT_TYPES()
 
 DEFINE_LOCAL_EVENT_TYPE(myEVT_SOMETHINGHAPPENED)

The event type isn't used by the macro. (7777 appears to be an ad-hoc standard: any value works but unique is "better". My spurious wxNewEventType() argument is almost certainly incorrect because it's in the declaration not the definition. In my code the declaration is only hit once, so that's okay.)

Then in your event table (in this case, based on a wxControl)...

 BEGIN_EVENT_TABLE(myFunkyObject,wxControl)
 EVT_BUTTON(...)
 ...
 EVT_CUSTOM(myEVT_SOMETHINGHAPPENED,wxID_ANY,myFunkyObject::OnSomethingHappened)
 END_EVENT_TABLE()
 
 myFunkyObject::OnSomethingHappened(wxCommandEvent &event) {thing=event.GetInt();}

I'm not sure what the id field in EVT_CUSTOM is for (it's the starting id number) but -1 (wxID_ANY) seems to work fine.

Then, to make something happen with your event from another object, do this:

 myDifferentObject::makesomethinghappen(int what) {
     wxCommandEvent event(myEVT_SOMETHINGHAPPENED);
     event.SetInt(what);
     wxPostEvent(ptr_to_a_myFunkyObject_chain,event);
     }

(D.F. Smith, Aug2004)

Note about wxPostEvent() vs. wxEvtHandler::ProcessEvent()

Both can be used when the event handler object is known at compile time.

wxEvtHandler::ProcessEvent(wxEvent& event)

ProcessEvent will call the event handler directly and the event will be processed serially like a function call.

wxPostEvent(wxEvtHandler *evtHandler, wxEvent& event)

wxPostEvent will put the event in the event queue of the wxEvtHandler object but the event will not be processed immediately. Note that the event queue can be flushed by calling wxApp::Yield or wxYieldIfNeeded.

Creating a custom event (with the ugly macro stuff)

You need to declare your event type first:

 DECLARE_EVENT_TYPE(wxCUSTOM_EVENT, 7777)
 DEFINE_EVENT_TYPE(wxCUSTOM_EVENT)

The second argument to DECLARE_EVENT_TYPE is only needed in wxWidgets < 2.4.0, and it's your event id there. Must be unique. As of 2.4.0, event ID's are dynamically allocated at runtime so theres no conflicts.

Next, you'll want an event table macro for your event. This one can be pretty funky.

 #define EVT_CUSTOM_EVENT(fn) \
     DECLARE_EVENT_TABLE_ENTRY( \
 	wxCUSTOM_EVENT, wxID_ANY, wxID_ANY, \
 	(wxObjectEventFunction)(wxEventFunction)&fn, \
 	(wxObject *) NULL \
   ),

This is a declaration that doesn't want an id, and it's for wxEvent-derived events. You'll need to add a cast to wxCommandEvent for command events, and add arguments for events that have IDs.

TODO: provide an example

Finally, implement your event itself. The things to remember are:

  • Set the m_eventType param with your new event type in the constructor
  • Implement a virtual Clone() method:
virtual wxEvent *Clone() const {return new wxCustomEvent(*this);}
  • Implement a copy constructor
  • Be sure to use the DECLARE_DYNAMIC_CLASS and IMPLEMENT_DYNAMIC_CLASS macros

A quick custom event example:

customevent.h:

 DECLARE_EVENT_TYPE(wxCUSTOM_EVENT, 7777)
 #define EVT_CUSTOM_EVENT(fn) \
     DECLARE_EVENT_TABLE_ENTRY( \
 	wxCUSTOM_EVENT, wxID_ANY, wxID_ANY, \
 	(wxObjectEventFunction)(wxEventFunction)&fn, \
 	(wxObject *) NULL \
     ),
 class wxCustomEvent : public wxEvent {
     wxCustomEvent();
     wxCustomEvent(const wxCustomEvent &event);
     virtual wxEvent *Clone() const {return new wxCustomEvent(*this);};
     DECLARE_DYNAMIC_CLASS(wxCustomEvent)
 };

customevent.cpp:

 DEFINE_EVENT_TYPE(wxCUSTOM_EVENT);
 IMPLEMENT_DYNAMIC_CLASS(wxCustomEvent, wxEvent)
 wxCustomEvent::wxCustomEvent():m_eventType(wxCUSTOM_EVENT) {
 }
 wxCustomEvent::wxCustomEvent(const wxCustomEvent &event) {
   this->m_eventType = event.m_eventType;  //not really needed in this sample, but it's boring to have it empty
 }


Using your new class:

 DECLARE_EVENT_TABLE(myForm, wxForm)
     EVT_CUSTOM_EVENT(myForm::HandleCustomEvent)
 END_EVENT_TABLE()


Why do Custom Events seem so Confusing?

Two significant reasons are the optional use of macros; and the EVT_CUSTOM Macro. I hope this section will make things clearer.

David Hart 29.11.06
wxGTK-2.6.3


Macros and their Equivalents

Part of the problem is that some examples use macros, and some don't. So there seem to be several different ways to do something, when in fact they are identical. Here is a list of the important equivalents.

  • DECLARE_EVENT_TYPE(MyFooCommandEvent,wxID_ANY)
is the same as
extern expdecl const wxEventType MyFooCommandEvent;
  • DEFINE_EVENT_TYPE(MyFooCommandEvent)
is the same as
const wxEventType MyFooCommandEvent = wxNewEventType();
  • BEGIN_DECLARE_EVENT_TYPES() and END_DECLARE_EVENT_TYPES()
used to do something. Starting from wxWidgets 2.4 they are empty!

It's not a macro, but since wxWidgets 2.4

  • wxNewEventType()
is used instead of doing things like
const newEVT_MYCLASS_FIRST = wxEVT_FIRST + 5400;

There is also EVT_CUSTOM (and EVT_CUSTOM_RANGE). More about this below, but for the record

  • EVT_CUSTOM(eventtype, id, func)
becomes wx__DECLARE_EVT1(eventtype, id, wxEventHandler(func))
which becomes
DECLARE_EVENT_TABLE_ENTRY(eventtype, id, wxID_ANY, func, NULL),


The alternative methods in a working example

This shows how to create a custom event class, which carries a trivial amount of data, and the alternative ways to make it work.


// Could have been DECLARE_EVENT_TYPE(MyFooCommandEvent,-1)
extern expdecl const wxEventType MyFooCommandEvent; // Not needed if you only use the event-type in one .cpp file

class MyFooEvent: public wxCommandEvent	// An exciting custom event that transports a whole wxString!
{
public:
    MyFooEvent( wxEventType commandType = MyFooCommandEvent, int id = 0 )
                            :  wxCommandEvent(commandType, id) {}
    MyFooEvent( const MyFooEvent &event )	// You *must* copy here the data to be transported
                            :  wxCommandEvent(event) { this->SetText( event.GetText() ); }
    // required for sending with wxPostEvent()
    wxEvent* Clone() const { return new MyFooEvent(*this); }

    wxString GetText() const { return m_Text; }
    void SetText( const wxString& text ) { m_Text = text; }

private:
    wxString m_Text;
};


typedef void (wxEvtHandler::*MyFooEventFunction)(MyFooEvent &);

	// This #define simplifies the one below, and makes the syntax less ugly if you want to use Connect() instead of an event table
#define MyFooEventHandler(func) \
      (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)wxStaticCastEvent(MyFooEventFunction, &func)

  // Define the event table entry. Yes, it really *does* end in a comma
#define EVT_MYFOO(id, fn) \
        DECLARE_EVENT_TABLE_ENTRY( \
            MyFooCommandEvent, id, wxID_ANY, \
            (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction) wxStaticCastEvent(MyFooEventFunction, &fn), \
            (wxObject*) NULL  ),

  // Optionally, you can do a similar #define for EVT_MYFOO_RANGE
#define EVT_MYFOO_RANGE(id1,id2, fn) \
        DECLARE_EVENT_TABLE_ENTRY( \
            MyFooCommandEvent, id1, id2, \
            MyFooEventHandler(fn), \
            (wxObject*) NULL  ),

  // If you want to use the custom event to send more than one sort of data, or to more than one place,
  // make it easier by providing named IDs in an enum
enum { Foo_DoFirstThing = 1, Foo_DoSecondThing, Foo_DoThirdThing };
// Could have been DEFINE_EVENT_TYPE(MyFooCommandEvent)
const wxEventType MyFooCommandEvent = wxNewEventType();



// Usage:

// To Send the event
...
MyFooEvent event(MyFooCommandEvent, Foo_DoFirstThing); // Or Foo_DoSecondThing, or...
wxString bar( wxT("This is a Foo_DoFirstThing event") );
  // Add the exciting data. In real life you can put anything you like into the class: ints, structs, binary data...
event.SetText(bar);
wxPostEvent(panel, event);
...

// To receive the event either use an event table like so:
BEGIN_EVENT_TABLE(MyDestination,wxDestination)
        EVT_MYFOO(wxID_ANY,MyDestination::DoSomething)
// or   EVT_MYFOO_RANGE(Foo_DoFirstThing, Foo_DoThirdThing,MyDestination::DoSomething)
// or   EVT_MYFOO(Foo_DoFirstThing,MyDestination::DoFirstThing)
// or   EVT_MYFOO(Foo_DoSecondThing,MyDestination::DoSecondThing)
// or   EVT_MYFOO(Foo_DoThirdThing,MyDestination::DoThirdThing)
END_EVENT_TABLE()

//or use Connect(). You'd probably do this in the MyDestination constructor
Connect( wxID_ANY, MyFooCommandEvent, MyFooEventHandler(MyDestination::DoSomething), NULL, this );

// To handle the event
void MyDestination::DoSomething( MyFooEvent& event )
{
switch( event.GetId() )
  { case Foo_DoFirstThing:  wxLogDebug( event.GetText() ); break;
    case Foo_DoSecondThing: /* Do something different */ break;
//  ...
  }
}


The MyFooEvent class could have been made dynamic, but for me it works without. YMMV of course.

The EVT_CUSTOM Macro method

A different approach is to use the EVT_CUSTOM macro. This has the distinct advantage that you don't need the other, complicated macro stuff. Just make your new event type (and declare it too if necessary):

const wxEventType MyFooCommandEvent = wxNewEventType();

and create your MyFooEvent class. Then all you need to do is to use EVT_CUSTOM directly in the event table:

BEGIN_EVENT_TABLE(MyDestination,wxDestination)
  EVT_CUSTOM(MyFooCommandEvent,wxID_ANY,MyDestination::DoSomething)
END_EVENT_TABLE()

and send and receive as before.

Much simpler, so why not do it all the time? Well, I don't think you can do this with Connect(). More importantly, the macro expects MyDestination::DoSomething to take a wxEvent& parameter, not a wxFooEvent&, so you'd have to change the function to:

void MyDestination::DoSomething( wxEvent& event )

This means that whenever you want to access the data in the event, you would need to cast. So the switch statement would need to be:

case Foo_DoFirstThing:  wxLogDebug( ((MyFooEvent&)event).GetText() ); break;


See also

by Markus Neifer: [1]. Unfortunately outdated.

It is a Very Good Idea to look at the events sample, which has a custom event (even though there is some ugly macro stuff going on).

Official docs has a line in the Event Handling Overview for Custom Events that reads:

DECLARE_EVENT_MACRO( wxEVT_PLOT_ACTION, -1 )

It should be:

DECLARE_EVENT_TYPE( wxEVT_PLOT_ACTION, -1 )