Difference between revisions of "Custom Events in wx2.8 and earlier"

From WxWiki
Jump to navigation Jump to search
(Corrected the usage of this custom event approach. Calling wxNewEventType() in the header will create a new event type for each class that includes the header.)
(link fix)
Line 1: Line 1:
The information here is provided to supplement the [http://docs.wxwidgets.org/trunk/overview_eventhandling.html#overview_eventhandling_custom Custom Event Summary] which you should read first. Also, it is a good idea to look at the events project in the distribution ''samples'' directory.
+
The information here is provided to supplement the [http://docs.wxwidgets.org/stable/wx_eventhandlingoverview.html#customevents Custom Event Summary] which you should read first. Also, it is a good idea to look at the events project in the distribution ''samples'' directory.
  
 
You may also find the following articles helpful:
 
You may also find the following articles helpful:

Revision as of 04:12, 11 May 2009

The information here is provided to supplement the Custom Event Summary which you should read first. Also, it is a good idea to look at the events project in the distribution samples directory.

You may also find the following articles helpful:

Quick User Notes

  • 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 propagates 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 two mentioned above:
    • 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 sub-classing 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();

wxEvtHandler::ProcessEvent vs. wxPostEvent and wxEvtHandler::AddPendingEvent

These can be used when the event handler object (usually a wxWindow) is known at compile time.

wxEvtHandler::ProcessEvent(wxEvent &event)

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

wxEvtHandler::AddPendingEvent(wxEvent& event)
wxPostEvent(wxEvtHandler *evtHandler, wxEvent &event)

wxPostEvent is just a wrapper for wxEvtHandler::AddPendingEvent: wxPostEvent(pWindow,event) is the same as pWindow->AddPendingEvent(event). Either will put the event in the event queue of the wxEvtHandler object, meaning that the event will not be processed immediately. They can therefore be used to communicate safely between a worker thread and the main gui thread. The delay can also be useful if you want something to happen, but not until an event already in the event queue has been processed.

Note that the event queue can be flushed by calling wxApp::Yield or one of its variants (e.g. wxYieldIfNeeded()).

Why are Custom Events So Confusing?

An Explanation of the Macros

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),

Events and wxEventType

An event is an object of class wxEvent, or more usually of one of its derivatives. A wxEventType is just an int, with a unique value. However the (understandable) tendency to use names like MyFooCommandEvent for eventtypes means that there is scope for confusion.

For example, the event table entry to catch plain wxCommandEvents has a definition (in wx/event.h) that starts:

#define EVT_COMMAND(winid, event, func)

But what is 'event'? It's a wxEventType. So you can't have an entry like this where wxCommandEvent is a class, not an int:

EVT_COMMAND(wxID_ANY, wxCommandEvent, myClass::Foo)

You have to either reuse an existing wxEventType, or create one of your own.

Creating a Custom Event - Method 1

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

extern const wxEventType newEVT_MYCLASS_CLICK;

#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:

const wxEventType newEVT_MYCLASS_CLICK = wxNewEventType();

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

Creating a Custom Event - Method 2

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_CUSTOM( myEVT_SOMETHINGHAPPENED, wxID_ANY, myFunkyObject::OnSomethingHappened )
END_EVENT_TABLE()

myFunkyObject::OnSomethingHappened( wxCommandEvent &event )
{
...
}

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, Aug 2004)

Creating a Custom Event - Method 3

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 which 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:

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
{
public:
	wxCustomEvent();
	wxCustomEvent( const wxCustomEvent &event );

	virtual wxEvent *Clone() const
		{ return new wxCustomEvent(*this); };

	DECLARE_DYNAMIC_CLASS(wxCustomEvent)
};
DEFINE_EVENT_TYPE( wxCUSTOM_EVENT );
IMPLEMENT_DYNAMIC_CLASS( wxCustomEvent, wxEvent )

wxCustomEvent::wxCustomEvent() : m_eventType(wxCUSTOM_EVENT) { }

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

Using your new class:

BEGIN_EVENT_TABLE( myFrame, wxFrame )
   EVT_CUSTOM_EVENT( myFrame::HandleCustomEvent )
END_EVENT_TABLE()

Creating a Custom Event - Method 4

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 )
// Not needed if you only use the event-type in one .cpp file
extern expdecl const wxEventType MyFooCommandEvent;

// A custom event that transports a whole wxString.
class MyFooEvent: public wxCommandEvent
{
public:
	MyFooEvent( wxEventType commandType = MyFooCommandEvent, int id = 0 )
	:  wxCommandEvent(commandType, id) { }

	// You *must* copy here the data to be transported
	MyFooEvent( const MyFooEvent &event )
	:  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 enumeration.
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 );
wxString bar( wxT("This is a Foo_DoFirstThing event") );
// Add the exciting data. 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 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();

Then create your MyFooEvent class. All you need to do from there is use EVT_CUSTOM directly in the event table:

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

Then 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;

"But I don't need a whole new event class..."

Often you'll want to send an event just to notify the receiving class that something has happened; in other words, you don't need to store data inside the event: the event itself is the message. So just send a wxCommandEvent with a specific type, and optionally a specific ID.

The simplest case

The event must have an eventtype. The simplest way is to reuse a pre-existing one e.g. wxEVT_COMMAND_BUTTON_CLICKED. In the originating function, create your event:

wxCommandEvent MyEvent( wxEVT_COMMAND_BUTTON_CLICKED ); // Keep it simple, don't give a specific event ID
wxPostEvent(this, MyEvent); // This posts to ourselves: it'll be caught and sent to a different method
wxPostEvent(pBar, MyEvent); // Posts to a different class, Bar, where pBar points to an instance of Bar

The event table would be:

BEGIN_EVENT_TABLE(MyFoo, wxSomething)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, MyFoo::OnMyEvent)
  ...
END_EVENT_TABLE()

and the handler's signature would be void MyFoo::OnMyEvent(wxCommandEvent&)

The Normal Case

The simplest case method works. However reusing wxEVT_COMMAND_BUTTON_CLICKED would look rather confusing, and there's a (small) chance of the event being caught by the wrong event table entry. A better way is to create a unique eventtype, and send this instead.

// This goes somewhere in a cpp file, before you use it (unless you've declared it elsewhere)
const wxEventType MyExcitingEvent = wxNewEventType(); // You get to choose the name yourself

// In the posting method:
...
wxCommandEvent MyEvent( MyExcitingEvent ); // Still keeping it simple, don't give a specific event ID
wxPostEvent(this, MyEvent); // This posts to ourselves: it'll be caught and sent to a different method
wxPostEvent(pBar, MyEvent); // Posts to a different class, Bar, where pBar points to an instance of Bar

The event table would be:

BEGIN_EVENT_TABLE(MyFoo, wxSomething)
  EVT_COMMAND(wxID_ANY, MyExcitingEvent, MyFoo::OnMyEvent)
  ...
END_EVENT_TABLE()

The handler's signature is still void MyFoo::OnMyEvent(wxCommandEvent&)

Most of the time all of this will be happening within the same cpp file. However if you need to use your eventtype elsewhere, you'll need to declare it there as an extern:

extern const wxEventType MyExcitingEvent;

The individual ID case

What if you want to send several messages? Well, you could create several eventtypes: they're only ints after all, there isn't a world shortage. But it's a bit easier to have just one eventtype, and use different IDs for different destinations. It's easier to read if you do that with an enum.

// The values don't matter, as they're only used with this eventtype
enum { Foo_DoFirstThing = 1, Foo_DoSecondThing, Foo_DoThirdThing };

// In the posting method:
...
// The first two events are posted to ourselves
wxCommandEvent MyEvent1( MyExcitingEvent, Foo_DoFirstThing ); wxPostEvent(this, MyEvent1);
wxCommandEvent MyEvent2( MyExcitingEvent, Foo_DoSecondThing ); wxPostEvent(this, MyEvent2);
// Just for variety, let's send the third one elsewhere:
wxCommandEvent MyEvent3( MyExcitingEvent, Foo_DoThirdThing ); wxPostEvent(pBar, MyEvent3);

The MyFoo event table would be:

BEGIN_EVENT_TABLE(MyFoo, wxSomething)
  EVT_COMMAND(Foo_DoFirstThing, MyExcitingEvent, MyFoo::OnMyFirstEvent)
  EVT_COMMAND(Foo_DoSecondThing, MyExcitingEvent, MyFoo::OnMySecondEvent)
  ...
END_EVENT_TABLE()

Alternatively you could catch everything as before with wxID_ANY, and discriminate between them in the handler with event.GetId().