The method I traditionally use of drawing to an OpenGL canvas with wxWidgets is to trigger a 30 hertz timed refresh() from the current OpenGL canvas, which then generates a "wxEVT_PAINT", which I can propogate out to the rest of the frame. I also bind to the wxEVT_PAINT and call refresh() to catch any frame resizes.
In my programs WITHOUT using wxWidgets AUI, this method has worked flawlessly.
If I try to use AUI however, every time I try binding to wxEVT_PAINT, my paint events never trigger. Sometimes binding to the paint event will even stop other events like the timer from triggering.
Is there some special way that AUI treats events, or the wxEVT_PAINT that I'm missing here? What is the best method to draw to a OpenGL window inside of a AUI managed frame? Can anyone provide hints or examples, as documentation seems to be non-existent on this topic concerning AUI.
Edit: For clarity, and I've added my source code below for anyone who would like to help track down the problem. I've removed the OpenGL portions, as I'm really just trying to get the wxEVT_PAINT to trigger my bound handler in the frame when I resize the window.
GeneratedFrame.h
#ifndef __GENERATEDFRAME_H__
#define __GENERATEDFRAME_H__
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/panel.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/menu.h>
#include <wx/frame.h>
#include <wx/aui/aui.h>
class MainFrame : public wxFrame
{
private:
protected:
wxPanel* m_panelMainView;
wxMenuBar* m_menubar2;
public:
MainFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("SimpleAui"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 646,516 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
wxAuiManager m_mgr;
~MainFrame();
};
#endif //__GENERATEDFRAME_H__
GeneratedFrame.cpp
#include "GeneratedFrame.h"
MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
m_mgr.SetManagedWindow(this);
m_mgr.SetFlags(wxAUI_MGR_LIVE_RESIZE);
m_panelMainView = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelMainView->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVECAPTION ) );
m_panelMainView->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_INFOBK ) );
m_mgr.AddPane( m_panelMainView, wxAuiPaneInfo() .Name( wxT("MainView") ).Center() .Caption( wxT("Main View") ).MinimizeButton( true ).PinButton( true ).Dock().Resizable().FloatingSize( wxDefaultSize ).Floatable( false ) );
m_menubar2 = new wxMenuBar( 0 );
this->SetMenuBar( m_menubar2 );
m_mgr.Update();
this->Centre( wxBOTH );
}
MainFrame::~MainFrame()
{
m_mgr.UnInit();
}
AppFrame.h
#ifndef __AppFrame_h__
#define __AppFrame_h__
#include "GeneratedFrame.h"
#include "SimpleAuiApp.h"
class AppFrame : public MainFrame
{
public:
// Constructor/Descructor
AppFrame( wxWindow* parent, ApplicationData* pAppData );
~AppFrame( );
// display update (called from main App on an update event)
void UpdateDisplay( );
private:
// pointer to the main data structure
ApplicationData* m_pAppData;
void OnPaint( wxPaintEvent& event );
};
#endif //__AppFrame_h__
AppFrame.cpp
#include "AppFrame.h"
#include "SimpleAuiApp.h"
#include <iostream>
// Constructor for the frame
AppFrame::AppFrame( wxWindow* parent, ApplicationData* pAppData )
: MainFrame( parent )
{
// Pull in the app data pointer
m_pAppData = pAppData;
// Set the size of the inner drawing area of the frame
SetClientSize( 500, 500 );
// Show the frame
Show();
// Layout the frame
Layout();
// Bind to the wxEVT_PAINT event
Bind( wxEVT_PAINT, &AppFrame::OnPaint, this );
}
// Destructor for the frame
AppFrame::~AppFrame( )
{
// stop the update timer for the application, otherwise a timer update
// event can be generated while data is being deleted
if( m_pAppData->m_pTimer )
{
m_pAppData->m_pTimer->Stop( );
}
}
void AppFrame::OnPaint( wxPaintEvent& event )
{
std::cout << "Running AppFrame::OnPaint\n";
UpdateDisplay( );
}
// perform frame update for the display
void AppFrame::UpdateDisplay( )
{
std::cout << "Running AppFrame::UpdateDisplay\n";
}
SimpleAuiApp.h
#ifndef __SimpleAuiApp_h__
#define __SimpleAuiApp_h__
#include <wx/wx.h>
#define DEFAULT_UPDATE_RATE (10) // in Hz (set to 0 for OnIdle)
// Forward declarations
class AppFrame;
struct ApplicationData
{
// constructor
ApplicationData( )
{
m_pFrame = NULL;
m_pTimer = NULL;
m_nDisplayUpdateRate = DEFAULT_UPDATE_RATE;
}
// timer object for frame based updates
wxTimer* m_pTimer;
// rate of display update (in HZ) (0=update on idle)
int m_nDisplayUpdateRate;
// pointer to the main frame instance
AppFrame* m_pFrame;
};
// Main application class
// (derived from the wxWidget App class)
class SimpleAuiApp : public wxApp
{
public:
SimpleAuiApp( );
virtual ~SimpleAuiApp( );
// the main application data structure
ApplicationData m_AppData;
private:
// called by wxApp when starting up, program setup should be done here
bool OnInit( );
// called by wxApp when shutting down, program cleanup should be done here
int OnExit( );
// When running with "fixed" framerate, called for each timer event (frame)
void OnTimer( wxTimerEvent& event );
};
DECLARE_APP( SimpleAuiApp )
#endif //__SimpleAuiApp_h__
SimpleAuiApp.cpp
#include <wx/wx.h>
#include "SimpleAuiApp.h"
#include "AppFrame.h"
#include <iostream>
IMPLEMENT_APP( SimpleAuiApp )
SimpleAuiApp::SimpleAuiApp( )
{
}
SimpleAuiApp::~SimpleAuiApp( )
{
}
bool SimpleAuiApp::OnInit( )
{
// Open a console window for errors and standard output
AllocConsole( );
freopen( "CONOUT$", "wb", stdout );
freopen( "CONOUT$", "wb", stderr );
std::cout << "Initialization started...\n";
// Create the main application frame
m_AppData.m_pFrame = new AppFrame( (wxWindow*) NULL, &m_AppData );
// Bring the frame to the front
SetTopWindow( m_AppData.m_pFrame );
// See if a fixed frame rate is specified
if ( m_AppData.m_nDisplayUpdateRate > 0 )
{
// Start a timer to update the display at a fixed frame rate.
// Note that rate is increased by 10% to make up for wxWidget's inaccurate timers.
m_AppData.m_pTimer = new wxTimer( this );
float fMilliSeconds = 1000.0 / ( m_AppData.m_nDisplayUpdateRate * 1.1f );
m_AppData.m_pTimer->Start( fMilliSeconds );
Bind( wxEVT_TIMER, &SimpleAuiApp::OnTimer, this );
}
else
{
// capture the "on idle" event when not running at a fixed frame rate
Bind( wxEVT_IDLE, &SimpleAuiApp::OnIdle, this );
}
std::cout << "Initialization completed...\n";
return true;
}
// -------------------------------------------------------------
// Framerate - functions bound to framerate related events.
// Either OnTimer() or OnIdle() should be called here for each
// frame, but never both. They are two different refresh
// methods.
// -------------------------------------------------------------
// Called by widget app code on timer event
void SimpleAuiApp::OnTimer( wxTimerEvent& event )
{
std::cout << "Called SimpleAuiApp::OnTimer \n";
// update the frame's display
m_AppData.m_pFrame->UpdateDisplay( );
}
// -------------------------------------------------------------
// OnExit - Called by widget app code on shutdown
// -------------------------------------------------------------
int SimpleAuiApp::OnExit( )
{
// stop (and delete) the update timer if needed
if ( m_AppData.m_pTimer )
{
m_AppData.m_pTimer->Stop( );
delete m_AppData.m_pTimer;
m_AppData.m_pTimer = NULL;
}
// close the console window if needed
FreeConsole( );
// exit successful
return 0;
}
The problem ended up being that the AUI manager which you attach to the main Frame DOES consume the wxEVT_PAINT event when it propagates, and it never reaches the Frame's class. The Frame's children however, do receive the events.
Instead of just using Bind() from the Frame class, I called m_panelgl->Bind where the m_panelgl was my child container which held the OpenGL canvas.