Page 1 of 1

Adding your own update code (MainLoop, PeekMessage)

Posted: Sat Aug 16, 2008 9:53 pm
by MrDoom
The Problem
You want to process something whenever wxWidgets is not processing any events of it's own, (when using Win32 code, this would be like using PeekMessage), but you're not sure how to change the MainLoop in wxWidgets.

The Solution
After numerous attempts to solve this and an evening of reading, I finally have a solution that works; this solution has been tested using wxWidgets 2.8.8 with Visual Studio 2008 on Windows XP.

The solution works by using wxWidgets' event system to call a function whenever wxWidgets is idle (so no MainLoop changes required). To start with, you will need a class derived from wxApp (chances are you have this already by I will include a cut down version of my header for clarity):

Code: Select all

class CApplication : public wxApp
{
	/* ... */

	public:	
		void DoUpdate(wxIdleEvent &event);

	/* ... */

	DECLARE_EVENT_TABLE()
};
The important parts to note in the above code is the "DoUpdate" function, this will be called whenever wxWidgets is idle; the "DECLARE_EVENT_TABLE" macro simply sets up the class to be able to use the event table (something which you will see more of later).

Now, in the CPP file for the above class, you will need to add some more macros to finish setting up the event table to call your "DoUpdate" function:

Code: Select all

BEGIN_EVENT_TABLE(CApplication, wxApp)
	EVT_IDLE(CApplication::DoUpdate)
END_EVENT_TABLE()
That registers your "DoUpdate" function to be called on an "Idle" event. Now to define the "DoUpdate" function itself:

Code: Select all

void CApplication::DoUpdate(wxIdleEvent &event)
{
	/* ... */

	if(IsMainLoopRunning())
		event.RequestMore();
}
You can put whatever you like in the ellipses comment, but the last part of the function is important. Since by default the "Idle" event is only passed once until something else happens in the application and it becomes idle again; you need to use "RequestMore" to keep adding the event to the event queue until you no longer need it (in the above case, that is the application exiting).

Notes
I hope this helps someone, I have seen many forum posts on this subject either unanswered or totally misinterpreted so I'm pretty sure this will help someone. Finally, I make no assumptions that this is the best solution, I have only been using wxWidgets for under a day myself and was looking for something with the relative simplicity of PeekMessage while avoiding threads.

Good luck. :D

Posted: Wed Nov 19, 2008 4:23 am
by wx_KNOX
I'm really surprised that there are no comments or replies to this.
I tried head and tail to figure this out.
But it looks like this is the only viable solution to this.

I tried lot's of work around, but none of them work.

Thanks doom for this.

One question though, has this method worked for people consistently across all platforms?

Posted: Wed Nov 19, 2008 11:02 am
by DavidHart
wx_KNOX wrote:I'm really surprised that there are no comments or replies to this.
Perhaps because it just describes the standard wx solution to the problem. See, for example, http://wiki.wxwidgets.org/WxIdleEvent

Incidentally, it is more usual to put the idle-event handler in a class other than wxApp: e.g. I've connected to idle-events in my frame class, to call code that needs to be run once when the frame is shown and has reached its intended size.
One question though, has this method worked for people consistently across all platforms?
Yes

Regards,

David

Posted: Thu Nov 20, 2008 3:50 am
by wx_KNOX
If I use an Idle event in my application, and in the event handler, I realize that my app is running way too fast.
Let's assume it's running at 700 fps. :)

Say I want it clamped down to 100 fps.
Is it safe to put a 'Sleep' in the Idle-event-handler?

Does the idle-event-handler work as expected in the Full screen window frame also?

Posted: Thu Nov 20, 2008 11:07 am
by DavidHart
If I use an Idle event in my application, and in the event handler, I realize that my app is running way too fast.
Is it safe to put a 'Sleep' in the Idle-event-handler?
I've no idea, but if you want controlled timing, use a wxTimer instead.
Does the idle-event-handler work as expected in the Full screen window frame also?
Why not try it and see, but I'd be very surprised if it didn't.

Posted: Fri Nov 21, 2008 4:49 am
by wx_KNOX
The earlier post I was actually referring to a real-time application like games.
Is 'handling the idle' event the right way then?
Is there any other way of doing it for real-time applications?

Posted: Sun Aug 16, 2009 11:44 pm
by MrDoom
Very late response from me, I haven't checked this forum in ages. Exactly one year it would seem.
DavidHart wrote:Incidentally, it is more usual to put the idle-event handler in a class other than wxApp: e.g. I've connected to idle-events in my frame class, to call code that needs to be run once when the frame is shown and has reached its intended size.
Yes, that would make more sense, and that is what I would do nowadays. As I said, I had only been using wxWidgets for a day when I posted this.

wx_KNOX wrote:The earlier post I was actually referring to a real-time application like games.
Is 'handling the idle' event the right way then?
Is there any other way of doing it for real-time applications?
I don't see anything wrong with that approach, I liken it to using PeekMessage which is what I do when using a Win32 window as a game shell. Really your render and update rate should be independent, I use an accumulator timer to have a fixed update step regardless of how fast the game is rendering.

Posted: Wed Dec 16, 2009 11:48 am
by DEmberton
wx_KNOX wrote:If I use an Idle event in my application, and in the event handler, I realize that my app is running way too fast.
Let's assume it's running at 700 fps. :)

Say I want it clamped down to 100 fps.
Is it safe to put a 'Sleep' in the Idle-event-handler?
The problem with Sleep is that it blocks everything and stops messages getting through. The better way of doing this sort of thing is to override the whole main loop. You can use MsgWaitForMultipleObjects (a Windows function, so I've no idea what the equivalent is for other platforms) to block for a certain time or until a message arrives.

I've attached an example. Feel free to do what you like with it. It has a 100ms delay, but you can easily do something more sophisticated to try to acheive a certain update rate.

Re:

Posted: Tue Feb 19, 2019 7:23 am
by cutecode
I've attached an example.

Code: Select all

   while(!message && wxTheApp && wxTheApp->ProcessIdle());
   {
	message = Pending();
   }
Why did you insert ';' at the end of the while loop?
Was it intentionally?

Re: Adding your own update code (MainLoop, PeekMessage)

Posted: Mon Aug 26, 2019 2:06 pm
by thatszechuan
MrDoom wrote: Sat Aug 16, 2008 9:53 pm

Code: Select all

void CApplication::DoUpdate(wxIdleEvent &event)
{
	/* ... */

	if(IsMainLoopRunning())
		event.RequestMore();
}
I have been trying to get some sort of auto-refresh working in a test program and also had issues finding a good example. A lot of half-answers and low effort C+P but no discussion of the implementation like you included in your post!

It's a very simple program updating a total value based on the sum of some text boxes - I want the total to change as the values are changed.

Even with event.RequestMore() the total value is updating only the first time the program is run, using the initial variables. If I change the initial values it will update the total when run, but it is not responding to real time changes in the values. The output box is flickering like it is being updated but the variables are not changing. If I'm missing something obvious I'd really like to know!

Code: Select all

void MyTestApp::OnIdle(wxIdleEvent& event)
{
//Grabbing a float
    float subtotal = (std::strtof (TextCtrl4->GetLabel(),NULL) + std::strtof (TextCtrl5->GetLabel(),NULL))*std::strtof(TextCtrl7->GetLabel(),NULL); 

//Setting the rounded float
    TextCtrl14->SetLabel(std::to_string(floor(subtotal*10)/10)); 

//Keep updating?
    event.RequestMore(true);
}

Re: Adding your own update code (MainLoop, PeekMessage)

Posted: Tue Aug 27, 2019 1:16 pm
by thatszechuan
Solution found for my question: I am a huge idiot and was using Get/Set Label instead of Get/Set Value. :(