wxWidgets with AUI: OpenGL render loop method and wxPaintEvent

1k Views Asked by At

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;
}
1

There are 1 best solutions below

0
On BEST ANSWER

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.