Help understanding custom events

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Help understanding custom events

Post by iwbnwif »

I have tried reading the official documentation, the wiki, forum posts and event sample but still not quite clear :oops: . I would like to ask for some help based on a hypothetical example...

I have some classes as follows:
  • A data manager
  • Something that requests modifications to the data
  • Various views of the data
Each class might be in a different thread.

The data manager for example might look like this:

Code: Select all

class MyDataManager
{
public:
     void Initialise (); // fills the vector with default values
     void ChangeData (int position, int value); // allow objects in the same thread as myDataManager direct access
     void ChangeData (const wxThreadEventSubclass& customEvent); // allow objects in other threads event based access to data
     
     ??? Subscribe (???);
     ??? Unsubscribe (???);     
private:
    wxVector data; // data store modified by ChangeData and viewable by subscribers via events
}
I think it is best for me to only consider Bind<> and not use Connect or event tables at all. However, I am still a little bewildered by the array of QueueEvent, wxQueueEvent, AddPendingEvent, ProcessWindowEvent, wxPostEvent, wxMessageQueue, ProcessEventLocally, ProcessPendingEvents, SafelyProcessEvent, etc. etc.
So for the classes that want to modify the data...
  1. Is there such a thing as an event queue? Are there lots of them, for example 1 per wxEvtHandler?
  2. Should I derive MyDataManager from wxEvtHandler?
  3. Can I have a wxEvtHandler instance as a member instead of deriving from it? Which is best and why?
  4. For the thread safe (event) version of ChangeData, which wxEvtHandler should the event get sent to?
  5. How should I post / queue the event to ChangeData from the class that wants to make a change?
  6. When to use the event handler belonging to wxTheApp?
And for the classes that want to display the data...
  1. How to use Bind<> inside MyDataManager to register an external class (in a different thread) with MyDataManager?
  2. Which event handler should MyDataManager post events to to notify subscribed classes of updates? For example do subscribing classes have to be derived from wxEvtHandler with their own handlers (queues?)?
And finally...
When is it preferable to use wxMessageQueue instead of events?

I appreciate that some of these are answered in the Wiki entry on inter-thread and inter-process communication but I would like to ask them here to get a really thorough understanding.
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
User avatar
T-Rex
Moderator
Moderator
Posts: 1249
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Re: Help understanding custom events

Post by T-Rex »

Hi. Not sure what should do ChangeData() call with event parameter. Should it modify the vector according to event object params?
If yes then:
  • Everything that should receive events, should be derived from wxEvtHandler
  • If data manager should receive events then you should also subclass it from wxEvtHandler
  • "Something that requests modifications of data" - do you mean that it will have direct access? Or send modification events? In general, if you have simple data objects and don't do any processing in data manager, you could use mutex or critical section for protecting the data access code and there will be no need in event-based access. Event-based access looks like an overhead
  • If your data manager's behavior stays always the same, then there is no need to subclass and you can have a pointer to it in all classes which will access the data.
  • For sending events you can use wxEvtHandler::AddPendingEvent() call if you access from the same thread. And wxPostEvent() from other thread
  • You can use PushEventHandler() for wxTheApp and insert your data manager to app's event handling queue and send events to wxTheApp if you don't want to have a pointer to your data manager in the classes which should access the data. However if your views should display the data, then you anyway need to access the data manager directly somehow. You also can make the data manager a member of your app class and then access it via wxGetApp().GetDataManager() accessor method (should be added to the app class and implemented by you). Or make your data manager a singleton.
But again, direct access protected by mutex looks like a good idea for me. this will increase the coupling of the components of your app, but will have a better performance than event-based calls.
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Re: Help understanding custom events

Post by iwbnwif »

Firstly thank you T-Rex for your reply. Sorry for not being clear, but your assumption is correct ChangeData should modify the vector according to the event params.

My example is somewhat contrived, because I am trying to get my head around how to use events in general rather than for a specific task. Mostly I would like to have a mechanism for de-coupling data updates and views where there may be multiple views onto a single set of data (i.e. graphical view, text view, views compounded from different datasets, each with their own data manager etc.).
Everything that should receive events, should be derived from wxEvtHandler
Thank you for this, I suppose it is obvious, but I got misled by the description in the Events Overview which gave an example of a event being bound to a global function:

Code: Select all

void HandleExit( wxCommandEvent & )
{
    // Do something useful
}
MyFrame::MyFrame()
{
    Bind( wxEVT_COMMAND_MENU_SELECTED, &HandleExit, wxID_EXIT );
}
So I was thinking that this would use the wxEvtHandler belonging to MyFrame, whereas:

Code: Select all

void MyFrameHandler::OnFrameExit( wxCommandEvent & )
{
    // Do something useful.
}
MyFrameHandler myFrameHandler;
MyFrame::MyFrame()
{
      Bind( wxEVT_COMMAND_MENU_SELECTED, &MyFrameHandler::OnFrameExit,
              &myFrameHandler, wxID_EXIT );
}
Would use the event handler from MyFrameHandler.
And wxPostEvent() from other thread
Shouldn't this be wxQueueEvent for a thread?

Thanks also for the tip regarding performance using events vs. mutexes. For me it is not an issue at the moment, but it is good information to have.

I think for the subscribing 'watchers' I can add them to the event handler chain when they subscribe, and remove them when they unsubscribe. As long as the watchers call event.Skip() then all subscribed watchers should get a data update event (containing the changed data since the last event).
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
User avatar
doublemax
Moderator
Moderator
Posts: 19162
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Help understanding custom events

Post by doublemax »

Just one more detail, because it's not clear from your initial post:

You must be aware that only the main thread receives events. Any event handler is always called in the context of the main thread. This also means that you can't send an event *to* a thread. If you want to send some kind of information to a thread, you can use wxMessageQueue.
Use the source, Luke!
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Re: Help understanding custom events

Post by iwbnwif »

Thanks DM.
You must be aware that only the main thread receives events. Any event handler is always called in the context of the main thread. This also means that you can't send an event *to* a thread. If you want to send some kind of information to a thread, you can use wxMessageQueue.
Ah, so that means I can't have view objects running in their own threads as I originally planned. But of course that is in fact the case anyway because they will be GUI objects and GUI objects can only run in the main thread.

However it also means that my data manager must also be in the main thread, or receive messages via wxMessageQueue<DataUpdateStructure> as you suggest instead of via my proposed ChangeData(event) method.
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
User avatar
T-Rex
Moderator
Moderator
Posts: 1249
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Re: Help understanding custom events

Post by T-Rex »

Shouldn't this be wxQueueEvent for a thread?
Mmm... Yes, according to this doc.
---
Well, if you want to notify the watchers when the data changes, then you could have a list watchers (pointers to wxEvtHandler) inside your data manager object (or add the watchers to event chain in any other way, e.g. using wxEvtHandler::SetNextHandler() and then Unlink() when you want to unsubscribe), implement the custom command event (or any other event, e.g. wxNotifyEvent and add some additional fields there, as far as I remember, this was described in Chapter 3 of wxWidgets book and there was an example). And in the end of ChangeData() method you will iterate through this list and send the event to each event handler (or if you used the event handler chaining, you can skip this step).

The watcher should have the method which processes this kind of event and subscribe either statically using event table, or dynamically connect the event handler using Connect() or Bind(). So each your view which displays the data can have its own method which handles that notification in a specific way.

I assume that if you create your own list of watchers and iterate manually, it will be easier to implement the event handling.

1. In your view class you implement the event handler method
2. Statically add that method to the event table of your view class inside BEGIN_EVENT_TABLE()...END_EVENT_TABLE() macros
3. Subscribe that view to updates of your data by calling DataManager::Subscribe() which just adds the event handler to the list
4. Derive your data manager from wxEvtHandler and implement the event handler method (I assume this is ChangeData() which receives an event as a parameter) for the event which adds the data to the vector.
5. Statically add that ChangeData() to data manager's event table.
6. When data producer needs to add the data to your vector, it calls wxQueueEvent() and this event gets then processed by data manager and ChangeData() method.
7. When data changes, you iterate through the list of watchers and send the event to watchers manually.
8. When updates are no longer needed, you call DataManager::Unsubscribe() and remove the watcher from list.

---
If you statically add the event handler to view class, this means that the view will handle this event always if such event arrives. If you don't need to subscribe/unsubscribe the views dynamically, then probably you could simplify the way of how you notify the views about data changes just by sending the event to wxTheApp. Since all views will have the event table entry for your notification event, and the event will be propagated from wxTheApp to the apps' main frame and its child windows, all views will get the notification.

---
In case if you want to dynamically subscribe/unsubscribe the views to data updates and want to use the app's event chain, the solution might be more complex. In this case you probably need to Connect()/Bind() the event handler for views inside the DataManager::Subscribe() and then add the view to even handler chain.

Since you need to know which view's method to Connect() or Bind(), you probably would need to send 2 parameters to Subscribe(): 1. the event handler to add to the chain; 2: handler function to connect (see e.g. wxCommandEventFunction macro) to the event handler. Or just derive all your views from one abstract class which declares the specific name for such event handler, and send the pointer to such class to DataManager::Subscribe(). In this case the data manager will know which subscriber's method it needs to connect.
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Re: Help understanding custom events

Post by iwbnwif »

T-Rex, thank you for such a comprehensive answer.
you could have a list watchers (pointers to wxEvtHandler) inside your data manager object (or add the watchers to event chain in any other way, e.g. using wxEvtHandler::SetNextHandler() and then Unlink() when you want to unsubscribe)
Yes, using one of these two ways is exactly what I was starting to think about.
Since all views will have the event table entry for your notification event, and the event will be propagated from wxTheApp to the apps' main frame and its child windows, all views will get the notification.
This sounds even more interesting, because it means I don't have to maintain the list of watchers anywhere - if they exist they will get the event and call Skip() on it to make sure the next one gets it. But I didn't understand one thing which I guess is really fundemental ...
the event will be propagated from wxTheApp to the apps' main frame and its child windows, all views will get the notification.
So the views are wxPanels within AUI tabs or dialogs. Will the events propagate from wxTheApp to those classes? Also does this mean that I need to wxQueueEvent in MyDataManager to wxTheApp->GetEventHandler() or wxTheApp->GetTopWindow()?
4. Derive your data manager from wxEvtHandler and implement the event handler method (I assume this is ChangeData() which receives an event as a parameter) for the event which adds the data to the vector.
5. Statically add that ChangeData() to data manager's event table.
6. When data producer needs to add the data to your vector, it calls wxQueueEvent() and this event gets then processed by data manager and ChangeData() method.
I think that this is the most flexible thing to do, otherwise I could follow DoubleMax's suggestion and use wxMessageQueue from the data providers instead of ChangeData. The disadvantage (I think) of wxMessageQueue is that it will block MyDataManager whilst it is waiting for data. That isn't necessary a problem because it doesn't need to do anything until a message is received, but it does restrict options for the future.

Thanks again for all your help :)
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
User avatar
T-Rex
Moderator
Moderator
Posts: 1249
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Re: Help understanding custom events

Post by T-Rex »

Will the events propagate from wxTheApp to those classes?
yes
Also does this mean that I need to wxQueueEvent in MyDataManager to wxTheApp->GetEventHandler() or wxTheApp->GetTopWindow()?
with GetTopWindow() defnitely should propagate. As for posting to wxTheApp directly - I don't remember, you can try and see whether this works.
mzet82
In need of some credit
In need of some credit
Posts: 8
Joined: Thu Dec 29, 2011 8:23 am

Re: Help understanding custom events

Post by mzet82 »

Do you really believe that the events are propagated from wxApp to wxFrame and then to the children wxFrame and not vice versa? I tried to write code, I did not succeed.
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Re: Help understanding custom events

Post by iwbnwif »

Do you really believe that the events are propagated from wxApp to wxFrame and then to the children wxFrame and not vice versa? I tried to write code, I did not succeed.
Hmm, well it appears not and (as you suggest) the events propagate in the other direction until they finally arrive at wxTheApp.

So this is what works:

Code: Select all

wxQueueEvent (wxTheApp->GetTopWindow(), event);
Sends the event to the main frame's event handler and so ...

Code: Select all

Bind (wxEVT_COMMAND_TEXT_UPDATED, &MainFrame::OnNumberUpdate, this, NUMBER_UPDATE_ID);
... calls MainFrame::OnNumberUpdate as expected. This is (I think) the normal way to work, however binding any child object (wxFrame or wxDialog) to the wxTheApp event handler like this ...

Code: Select all

wxTheApp->Bind (wxEVT_COMMAND_TEXT_UPDATED, &MainFrame::OnNumberUpdate, this, NUMBER_UPDATE_ID);
also works and allows the child object to receive the event (providing it hasn't already been sunk by the MainFrame).

According to the documentation it seems perfectly valid to allow an event to propagate up to wxTheApp level, but I cannot find any mention whether it is okay to Bind<> to the wxTheApp event handler or not.
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
User avatar
T-Rex
Moderator
Moderator
Posts: 1249
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Re: Help understanding custom events

Post by T-Rex »

Well, indeed. Just have looked at the docs and can see that events get propagated only upwards... :oops:
In this case you can use your data manager as a `bus` for your events and push the watcher to the event handling chain using PushEventHandler() inside Subscribe() method. And pop on Unsubscribe(). Or use Bind()/Unbind(). I used the same approach in wxModularApp project where the host app creates the event handler, sends it to plugins and plugins' config windows add their event handling methods to that event handler obtained from the main app. And that event handler works like a bus for communication between plugins.
mzet82
In need of some credit
In need of some credit
Posts: 8
Joined: Thu Dec 29, 2011 8:23 am

Re: Help understanding custom events

Post by mzet82 »

T-Rex. Thanks for clarifying. I also believe that events extends to wxtheapp. but then I do not understand a bit of the architecture program written by you above.
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Re: Help understanding custom events

Post by iwbnwif »

T-Rex, a big thank you for your time in answering these questions.

I have written a small test program that brings together all the elements we discussed and it works very well!

As you say the descriptions in Chapter 3 of the wxWidgets book are very useful, especially the diagram at Figure 3-1 which I didn't see in the current online documentation. This really cleared up my very first question "Is there such a thing as an event queue? Are there lots of them, for example 1 per wxEvtHandler?" - the answer is clearly yes!

Thanks again :)
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
mzet82
In need of some credit
In need of some credit
Posts: 8
Joined: Thu Dec 29, 2011 8:23 am

Re: Help understanding custom events

Post by mzet82 »

I have written a small test program that brings together all the elements we discussed and it works very well!
Could you share your test programs?
iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 282
Joined: Tue Mar 19, 2013 8:52 pm

Re: Help understanding custom events

Post by iwbnwif »

Yes I intend to tidy it up and post it here, but it won't be until next weekend I am afraid.
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.
Post Reply