Page 1 of 1

The "main loop"

Posted: Sat Jun 23, 2007 1:55 am
by Zeal
Im a little fuzzy on the whole IMPLEMENT_APP concept. It seems like when I use that, wx is running its own internal main loop, which makes me wonder how im supposed to include MY main loop (which is unrelated to wx).

So, how can I still have control over my own main loop, and also init/pump/unit wx widgets at the same time? Heres a little test program, (which seems to work for the wxInit/Uninit part), but I have no idea how to run/pump the main wx event loop).

Code: Select all

//main.cpp

#include <wx/wxprec.h>
#include "windows.h"
#include "main.h"

//do i need this?
//IMPLEMENT_APP_NO_MAIN(MyApp)

//=========
//WinMain()
//=========
int WINAPI WinMain( HINSTANCE hI, HINSTANCE hPrevI, LPSTR lpCmdLine, int nCmdShow ) {

	wxInitialize();

	//my main loop
	bool mainQuit = false;
	while ( !mainQuit ) {

		//wx widgets main loop goes here

	}

	wxUninitialize();

	return 0;

}

bool MyApp::OnInit()
{
   MyFrame *frame = new MyFrame( "Hello World", wxPoint(50,50), wxSize(450,340));
   frame->Show(TRUE);
   return TRUE;
} 

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(ID_Quit, MyFrame::OnQuit)
    EVT_MENU(ID_About, MyFrame::OnAbout)
END_EVENT_TABLE()

MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
    wxMenu *menuFile = new wxMenu;
    menuFile->Append( ID_About, "&About..." );
    menuFile->Append( ID_Quit, "E&xit" );
    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, "&File" );
    wxFrame::SetMenuBar( menuBar );
}

void MyFrame::OnQuit(wxCommandEvent& event)
{
    Close(TRUE);
}

void MyFrame::OnAbout(wxCommandEvent& event)
{
    wxMessageBox("Hello World!");
}

Code: Select all

//main.h

#pragma once

enum
{
    ID_Quit = 100,
    ID_About = 200,
};

class MyApp: public wxApp
{
    virtual bool OnInit();
};

class MyFrame: public wxFrame
{
public:

    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);

    void OnQuit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);

    DECLARE_EVENT_TABLE()
};

Thanks in advance for any help!

Posted: Sat Jun 23, 2007 6:56 am
by lvdlinden
You could look into the source code, for example in src/common/appcmn.cpp. But you could also create your own main loop in a separate thread which you can probably start in wxApp::OnInit.

Posted: Sat Jun 23, 2007 8:29 am
by Zeal
I thought about putting wx in its own thread, but im still unsure how write the 'message pump'.

For example, when I write a simple 'single window' app, I include a little message pump that translates windows messages so the window can be moved, resized, ect...

Code: Select all

	MSG winmsg;
	while ( PeekMessage( &winmsg, NULL, 0, 0, PM_REMOVE ) ) {
		TranslateMessage( &winmsg );
		DispatchMessage( &winmsg );
	}
Im sure wxwidgets has a similar 'translate/dispatch' interface, which would allow me to run the loop inside my main loop. I skimmed through the app source, but will have to take a closer look later.

Posted: Mon Jun 25, 2007 12:35 am
by Zeal
Well I found..
// ----------------------------------------------------------------------------
// main event loop implementation
// ----------------------------------------------------------------------------

int wxAppBase::MainLoop()
{
wxEventLoopTiedPtr mainLoop(&m_mainLoop, new wxEventLoop);

return m_mainLoop->Run();
}

void wxAppBase::ExitMainLoop()
{
// we should exit from the main event loop, not just any currently active
// (e.g. modal dialog) event loop
if ( m_mainLoop && m_mainLoop->IsRunning() )
{
m_mainLoop->Exit(0);
}
}

bool wxAppBase::Pending()
{
// use the currently active message loop here, not m_mainLoop, because if
// we're showing a modal dialog (with its own event loop) currently the
// main event loop is not running anyhow
wxEventLoop * const loop = wxEventLoop::GetActive();

return loop && loop->Pending();
}

bool wxAppBase::Dispatch()
{
// see comment in Pending()
wxEventLoop * const loop = wxEventLoop::GetActive();

return loop && loop->Dispatch();
}
Which makes it sound like I CAN 'start' the main loop on my own, but correct me if im wrong, but the above seems to trigger the main loop to run forever. Im looking for a way to run it 'once' (ie do a single pass and dispatch all currently queued messages).

Anyonw know how that can be done?

Posted: Mon Jun 25, 2007 3:42 pm
by AndyVincent
According to the documentation, wxApp::Dispatch will dispatch a single event that's in the queue. wxApp::Pending will tell you if there are any in the queue.

Code: Select all

void DispatchPendingMessages(wxApp& app)
{
    while (app.Pending())
    {
        app.Dispatch();
    }
}
http://www.wxwidgets.org/manuals/stable ... ppdispatch

Posted: Tue Jun 26, 2007 1:24 am
by Zeal
Ok cool that looks like just what I need. But where am I getting the 'app' from? All i am doing is calling wxInit()/wxUninit(). At what point should I be 'making' the app object?

Posted: Thu Jun 28, 2007 7:09 pm
by diagonalfish
(Hi, I'm new! I saw this topic and noticed that it was exactly what I've been trying to do...)

The 'app' you're looking for can be obtained by using the global pointer 'wxTheApp' which is created when you use the IMPLEMENT_APP_NO_MAIN macro, from what I understand.

More important, however, is the problem that there is no event loop actually created or running if all we're doing is using wxInitialize/wxUninitialize (I just checked this). Thus, Dispatch() and Pending() don't work. What we need is a way to create a wxEventLoop ourselves and running the loop that AndyVincent suggested, while at the same time not letting it take over the program. Anyone have any ideas?

Posted: Fri Jun 29, 2007 12:00 pm
by Zeal
Cool thanks for the info about wxTheApp, but sounds like its not enough... Im sure this has to be possible... but how!?

Posted: Wed Sep 26, 2007 9:02 am
by Zeal
Took a break for awhile (focused on non gui things), but now im at a point where im ready to implement wxWidgets, but I still have this 'main loop' prooblem. I did a forum search and all I could find was a possible solution using wxTimer()? Doesnt seem right...

Is there no way to just pump the dang event queue manually?

Posted: Tue Oct 16, 2007 8:16 pm
by mattropolis
Zeal wrote:Took a break for awhile (focused on non gui things), but now im at a point where im ready to implement wxWidgets, but I still have this 'main loop' prooblem. I did a forum search and all I could find was a possible solution using wxTimer()? Doesnt seem right...

Is there no way to just pump the dang event queue manually?
Did you ever get an answer for this? I'm also looking for a solution as well...

Posted: Wed Oct 17, 2007 2:29 am
by Disch
I'm really interested in this too. For the program I'm working on, I need constant CPU attention. The easiest way for me to do what I want is to manually pump the event queue.

The alternatives I've been considering (creating a seperate thread, using idle events) are much less appealing and would really complicate my code.

Posted: Fri Oct 19, 2007 5:56 pm
by Zeal
Nope still no answer.. kinda given up in favor of a temporary alternative gui solution. Although someday ill come back to wx, so if anyone has an answer I would still love to hear it!

Posted: Sat Oct 20, 2007 5:18 pm
by Disch
One theory I had (which I didn't test!) was to hijack the event loop in an idle event. Something like:

Code: Select all

void MyApp::OnIdle(wxIdleEvent& event)
{
  event.Skip();

  if(inidle)
    return;

  inidle = 1;

  while(wantprogramopen)
  {
    /* hijacked new program loop */
    while(Pending())
      Dispatch();

    /* do whatever else you want your loop to do here */
  }
}
However this seemed very sketchy to me... so I decided against going this route in favor of just calling RequestMore for more idle messages. Currently, I'm doing something like this:

Code: Select all

void SchApp::OnIdle(wxIdleEvent& event)
{
  event.Skip();

  if(!event.MoreRequested())
    event.RequestMore();

  /*  do whatever else you want your loop to do here  */
}
This actually works suprisingly well as long as you remember to call ::wxMilliSleep() (since the constant RequestMore()s will prevent the program from sleeping)