multiple wxGLCanvas's don't initiall show up Topic is solved

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.
Post Reply
wxMike
In need of some credit
In need of some credit
Posts: 5
Joined: Fri Jan 11, 2008 8:12 pm

multiple wxGLCanvas's don't initiall show up

Post by wxMike » Thu Jan 17, 2008 6:50 am

I'm having some trouble with getting wxGLCanvas's to work. I've put my drawing code in my wxFrame idle event handler.

When I have two wxGLCanvas's with one parent wxFrame they don't show up until I resize, or move the window. Then they both show up fine.

EDIT: Additionally this problem exists if I create a wxFrame with a wxButton and wxGLCanvas in it.

On the other hand if I have only a single wxGLCanvas it shows up and starts drawing immediately just fine. For some reason it also set's the glViewport and resizes itself to the size of the parent wxFrame even though I may have set an explicit size for it (this isn't really a problem though).

The drawing code is in the idle event handler, so one thing that I thought to check for is whether it's not getting called initially when the app starts up. To my surprise it is, so that doesn't seem to be the problem.

Obviously what I'd like to have happen is for the wxGLCanvas's to start drawing right when the app starts.

What am I missing?

I'm on a mac 10.4.10, with wx 2.8.7 insstalled and I passed configure "--with-opengl --enable-debug"

heres my code:

main.cpp:

Code: Select all

#include "main.hpp"

IMPLEMENT_APP(App)

bool App::OnInit() {
        Frame *frame = new Frame(NULL, wxID_ANY, wxString("Typing Mentr"), wxDefaultPosition, wxSize(300, 300));
        frame->Show();
        SetTopWindow(frame);
        return 1;
}

Frame::Frame(wxWindow *rParent, wxWindowID rId, const wxString &rTitle, const wxPoint &rPos, const wxSize &rSize, long rStyle, const wxString &rName) : wxFrame(rParent, rId, rTitle, rPos, rSize, rStyle, rName) {
        int attributeList[] = {
                WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0
        };
        glCanvas = new wxGLCanvas(this, wxID_ANY, wxPoint(0, 0), wxSize(100, 100), 0, wxString("GLCanvas"), attributeList);
        glCanvas2 = new wxGLCanvas(this, wxID_ANY, wxPoint(100, 100), wxSize(100, 100), 0, wxString("GLCanvas"), attributeList);
        glContext = glCanvas->GetContext();
        glContext2 = glCanvas2->GetContext();
        glCanvas->Show();
        glCanvas2->Show();
}

void Frame::on_close(wxCloseEvent &rEvent) {
        Destroy();
}

void Frame::on_idle(wxIdleEvent &rEvent) {
        float red, blue, green;
        red = blue = green = 0.5;
        wxBell();       //This makes some noise, so I know that it IS getting an initial idle event that the opengl drawing code depends on.

        glCanvas->SetCurrent();
        glViewport(0, 0, glCanvas->GetSize().GetWidth(), glCanvas->GetSize().GetHeight());
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glColor3f(red, blue, green);
        glBegin(GL_POLYGON);
                glVertex2f(-0.5, -0.5);
                glVertex2f(-0.5, 0.5);
                glVertex2f(0.5, 0.5);
                glVertex2f(0.5, -0.5);
        glEnd();
        glFlush();
        glCanvas->SwapBuffers();

        glCanvas2->SetCurrent();
        glViewport(0, 0, glCanvas2->GetSize().GetWidth(), glCanvas2->GetSize().GetHeight());
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glColor3f(red, blue, green);
        glBegin(GL_POLYGON);
                glVertex2f(-0.5, -0.5);
                glVertex2f(-0.5, 0.5);
                glVertex2f(0.5, 0.5);
                glVertex2f(0.5, -0.5);
        glEnd();
        glFlush();
        glCanvas2->SwapBuffers();
        rEvent.RequestMore();
}
main.hpp:

Code: Select all

#ifndef INC__MAIN_HPP__
#define INC__MAIN_HPP__

#include "wx/wx.h"
#include "wx/glcanvas.h"

class App : public wxApp {
public:
        bool OnInit();
};

DECLARE_APP(App)

class Frame : public wxFrame {
public:
        Frame(wxWindow *rParent, wxWindowID rId, const wxString &rTitle, const wxPoint &rPos = wxDefaultPosition, const wxSize &rSize = wxDefaultSize, long rStyle = wxDEFAULT_FRAME_STYLE, const wxString &rName = "frame");
        void on_close(wxCloseEvent &rEvent);
        void on_idle(wxIdleEvent &rEvent);
        wxGLCanvas *glCanvas;
        wxGLCanvas *glCanvas2;
        wxGLContext *glContext;
        wxGLContext *glContext2;
        DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(Frame, wxFrame)
        EVT_CLOSE(Frame::on_close)
        EVT_IDLE(Frame::on_idle)
END_EVENT_TABLE()

#endif
here's main.cpp modified to produce to above mentioned results where the one wxGLCanvas initially shows up:

Code: Select all

#include "main.hpp"

IMPLEMENT_APP(App)

bool App::OnInit() {
        Frame *frame = new Frame(NULL, wxID_ANY, wxString("Typing Mentr"), wxDefaultPosition, wxSize(300, 300));
        frame->Show();
        SetTopWindow(frame);
        return 1;
}

Frame::Frame(wxWindow *rParent, wxWindowID rId, const wxString &rTitle, const wxPoint &rPos, const wxSize &rSize, long rStyle, const wxString &rName) : wxFrame(rParent, rId, rTitle, rPos, rSize, rStyle, rName) {
        int attributeList[] = {
                WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0
        };
        glCanvas = new wxGLCanvas(this, wxID_ANY, wxPoint(0, 0), wxSize(100, 100), 0, wxString("GLCanvas"), attributeList);
        //glCanvas2 = new wxGLCanvas(this, wxID_ANY, wxPoint(100, 100), wxSize(100, 100), 0, wxString("GLCanvas"), attributeList);
        glContext = glCanvas->GetContext();
        //glContext2 = glCanvas2->GetContext();
        glCanvas->Show();
        //glCanvas2->Show();
}

void Frame::on_close(wxCloseEvent &rEvent) {
        Destroy();
}

void Frame::on_idle(wxIdleEvent &rEvent) {
        float red, blue, green;
        red = blue = green = 0.5;
        wxBell();       //This makes some noise, so I know that it IS getting an initial idle event that the opengl drawing code depends on.

        glCanvas->SetCurrent();
        glViewport(0, 0, glCanvas->GetSize().GetWidth(), glCanvas->GetSize().GetHeight());
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glColor3f(red, blue, green);
        glBegin(GL_POLYGON);
                glVertex2f(-0.5, -0.5);
                glVertex2f(-0.5, 0.5);
                glVertex2f(0.5, 0.5);
                glVertex2f(0.5, -0.5);
        glEnd();
        glFlush();
        glCanvas->SwapBuffers();

        /*glCanvas2->SetCurrent();
        glViewport(0, 0, glCanvas2->GetSize().GetWidth(), glCanvas2->GetSize().GetHeight());
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glColor3f(red, blue, green);
        glBegin(GL_POLYGON);
                glVertex2f(-0.5, -0.5);
                glVertex2f(-0.5, 0.5);
                glVertex2f(0.5, 0.5);
                glVertex2f(0.5, -0.5);
        glEnd();
        glFlush();
        glCanvas2->SwapBuffers();*/
        rEvent.RequestMore();
}

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria » Thu Jan 17, 2008 3:40 pm

I would recommend derivating from wxGLCanvas and catching paint events, this will ensure your render code is called whenever it is needed, and also this enables you to call ->Refresh() to trigger paint events whenever you want (see the wxGLCanvas article on the wiki for examples) Or you could catch paint events without derivating using Connect.

In any event that will mean cretae a wxPaintDC on paint events, and wxClientDC otherwise. I would recommend creating a "render" function/method that you can call as needed

wxMike
In need of some credit
In need of some credit
Posts: 5
Joined: Fri Jan 11, 2008 8:12 pm

doesn't work still. However I may have found a temp solution

Post by wxMike » Fri Jan 18, 2008 4:22 am

I tried what you suggested Auria, and derived wxGLCanvas. It didn't work.

I have found something that works however.

No opengl commands had any effect until the window was resized or moved (the wxGLCanvas was still there but it's contents were just uninitialized garbage). Logically one way to get the canvas to draw some graphics is to simulate the window being moved so that whatever magic that happens when it's move/resize handlers are called can happen.

simply uncommenting the SetSize() call in OnInit() below makes it work.

Is there a better way to solve this problem? Or will this result in mysterious crashes later?

heres my code:

main.cpp:

Code: Select all

#include "main.hpp"

IMPLEMENT_APP(App)

bool App::OnInit() {
    Frame *frame = new Frame(NULL, wxID_ANY, wxString("Typing Mentr"));
    frame->Show();
    SetTopWindow(frame);
    //With the following uncommented it works.
    //frame->SetSize(25, 25, 300, 300);
    return 1;
}

Frame::Frame(wxWindow *rParent, wxWindowID rId, const wxString &rTitle, const wxPoint &rPos, const wxSize &rSize, long rStyle, const wxString &rName) : wxFrame(rParent, rId, rTitle, rPos, rSize, rStyle, rName) {
    int attributeList[] = {
        WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0
    };
    glCanvas = new GLCanvas(this, wxID_ANY, wxPoint(0, 0), wxSize(100, 100), 0, wxString("GLCanvas"), attributeList);
    glCanvas->Show();
    //new wxButton(this, wxID_ANY, wxString("button"), wxPoint(80, 80));
    new GLCanvas(this, wxID_ANY, wxPoint(100, 100), wxSize(100, 100), 0, wxString("GLCanvas"), attributeList);
}

GLCanvas::GLCanvas(wxWindow *rParent, wxWindowID rId, const wxPoint &rPos, const wxSize &rSize, long rStyle, const wxString &rName, int *rAttribList, const wxPalette &rPalette) : wxGLCanvas(rParent, rId, rPos, rSize, rStyle, rName, rAttribList, rPalette) {}

void GLCanvas::on_paint(wxPaintEvent &rEvent) {
    wxPaintDC dc(this);
    wxBell();
    render();
}

void GLCanvas::on_idle(wxIdleEvent &rEvent) {
    wxClientDC dc(this);
    render();
    rEvent.RequestMore();
}

void GLCanvas::render() {
    SetCurrent();
    glViewport(0, 0, GetSize().GetWidth(), GetSize().GetHeight());
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(0.5, 0.5, 0.5);
    glBegin(GL_LINES);
        glVertex2f(0.25, 0.75);
        glVertex2f(0.75, 0.25);
    glEnd();
    glFlush();
    SwapBuffers();
}

BEGIN_EVENT_TABLE(GLCanvas, wxGLCanvas)
    EVT_IDLE(GLCanvas::on_idle)
    EVT_PAINT(GLCanvas::on_paint)
END_EVENT_TABLE()
main.hpp:

Code: Select all

#ifndef INC__MAIN_HPP__
#define INC__MAIN_HPP__

#include "wx/wx.h"
#include "wx/glcanvas.h"

class App : public wxApp {
public:
    bool OnInit();
};

DECLARE_APP(App)

class Frame : public wxFrame {
public:
    Frame(wxWindow *rParent, wxWindowID rId, const wxString &rTitle, const wxPoint &rPos = wxDefaultPosition, const wxSize &rSize = wxDefaultSize, long rStyle = wxDEFAULT_FRAME_STYLE, const wxString &rName = "frame");
    wxGLCanvas *glCanvas;
};

class GLCanvas : public wxGLCanvas {
public:
    GLCanvas(wxWindow *rParent, wxWindowID rId = wxID_ANY, const wxPoint &rPos = wxDefaultPosition, const wxSize &rSize = wxDefaultSize, long rStyle = 0, const wxString &rName = "GLCanvas", int *rAttribList = NULL, const wxPalette &rPalette = wxNullPalette);
    void on_paint(wxPaintEvent &rEvent);
    void on_idle(wxIdleEvent &rEvent);
    void render();
    DECLARE_EVENT_TABLE()
};

#endif

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria » Fri Jan 18, 2008 3:54 pm

Use sizers and not direct positionning. It will most liekly fix the rendering issues (if you do direct positionning you are responsible for doing eveything manually on the objects)

PS: Use wxT() around string literals, your code won't build with unicode enabled (or just wait for wx 3)

idhan
Experienced Solver
Experienced Solver
Posts: 88
Joined: Tue Feb 28, 2006 9:03 pm

Post by idhan » Fri Jan 18, 2008 9:47 pm

Normally you shoud intercept the size event of wxFrame and there set position and size for you glcanvas controls. On the wxFrame contructor just use the default position and size for you glcanvas controls (when you wxframe is initialize by the OS, before its first render, automatically the size event is triger).

In you glcanvas controls, the viewport also shoud be set inside of the size events. That is the normal logic for graphics programs. It is the logical solution if you allow to modify you wxframe size by the user.

wxMike
In need of some credit
In need of some credit
Posts: 5
Joined: Fri Jan 11, 2008 8:12 pm

fixed

Post by wxMike » Mon Jan 21, 2008 10:44 pm

FWIW I found this page http://www.wxwidgets.org/wiki/index.php ... olayout.29 indicates why it worked with only one (wxGLCanvas) child.

It took a while to figure out that a wxGridBagSizer only resizes with the nifty AddGrowable(Row/Col) function, but otherwise it works perfectly.

Thanks for the help guys.

Post Reply