[gtk] wxNotebook does not display page (panel with opengl content), wxListBooks displays all OK.
I didn't tried to reproduce as separate example, but I'm allowed to post videos from our app, may be you'll have ideas.
The code is identical but the correct example uses new wxListbook , while incorrect example uses new wxNotebook.
Additionally, the in the buggy case it complains
(granum:26129): Gtk-WARNING **: 15:03:22.839: Drawing a gadget with negative dimensions. Did you forget to allocate a size? (node tab owner GtkNotebook)
I have no idea what can be done.
Platform and version information
- wxWidgets: 3.2.6.7
- GTK version: 3-3.24.43
- X11
- Desktop environment: awesome
- linux: 6.6.59
Sorry, I have no idea what the problem is, but in any case we really do need an example reproducing it, if only to be able to test any fixes. Please do try reproducing it in the closest sample (again, no idea which one it would be, as I don't know if it's AUI-specific or not) and submit a patch reproducing it with the explanation of what actually goes wrong.
Thanks!
ok, will try
The example goes below. The key change is new wxListbook vs new wxNotebook
The behavior is recorded in videos and attached.
https://github.com/user-attachments/assets/1b0a2aea-d3a0-4ab3-a8b9-ecd5d22b0950
https://github.com/user-attachments/assets/5a5d2128-f179-40ad-a5b9-9cd980771b8c
#include <wx/app.h>
#include <wx/frame.h>
#include <wx/panel.h>
#include <wx/listbook.h>
#include <wx/notebook.h>
#include <wx/textctrl.h>
#include <wx/sizer.h>
#include <wx/glcanvas.h>
#include <wx/dcclient.h>
#include <GL/glu.h>
#include <GL/gl.h>
class BasicGLPane : public wxGLCanvas
{
wxGLContext* m_context;
public:
BasicGLPane(wxWindow* parent, int* args, GLfloat bg_r, GLfloat bg_g, GLfloat bg_b);
virtual ~BasicGLPane();
void resized(wxSizeEvent& evt);
int getWidth();
int getHeight();
void render(wxPaintEvent& evt);
void prepare3DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y);
void prepare2DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y);
GLfloat bg_r;
GLfloat bg_g;
GLfloat bg_b;
// events
DECLARE_EVENT_TABLE()
};
GLfloat v[8][3];
GLint faces[6][4] = { /* Vertex indices for the 6 faces of a cube. */
{0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
{4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} };
BEGIN_EVENT_TABLE(BasicGLPane, wxGLCanvas)
EVT_PAINT(BasicGLPane::render)
END_EVENT_TABLE()
BasicGLPane::BasicGLPane(wxWindow* parent, int* args, GLfloat bg_r_, GLfloat bg_g_, GLfloat bg_b_):
wxGLCanvas(parent, wxID_ANY, args, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
bg_r{bg_r_}, bg_g{bg_g_}, bg_b{bg_b_}
{
printf("%f, %f, %f\n", bg_r, bg_g, bg_b);
m_context = new wxGLContext(this);
// prepare a simple cube to demonstrate 3D render
// source: http://www.opengl.org/resources/code/samples/glut_examples/examples/cube.c
v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;
// To avoid flashing on MSW
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
}
BasicGLPane::~BasicGLPane()
{
delete m_context;
}
void BasicGLPane::resized(wxSizeEvent& evt)
{
// wxGLCanvas::OnSize(evt);
Refresh();
}
void BasicGLPane::prepare3DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y)
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_COLOR_MATERIAL);
glViewport(topleft_x, topleft_y, bottomrigth_x-topleft_x, bottomrigth_y-topleft_y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float ratio_w_h = (float)(bottomrigth_x-topleft_x)/(float)(bottomrigth_y-topleft_y);
gluPerspective(45 /*view angle*/, ratio_w_h, 0.1 /*clip close*/, 200 /*clip far*/);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/** Inits the OpenGL viewport for drawing in 2D. */
void BasicGLPane::prepare2DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y)
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
glEnable(GL_TEXTURE_2D); // textures
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glViewport(topleft_x, topleft_y, bottomrigth_x-topleft_x, bottomrigth_y-topleft_y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(topleft_x, bottomrigth_x, bottomrigth_y, topleft_y);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int BasicGLPane::getWidth()
{
return GetSize().x;
}
int BasicGLPane::getHeight()
{
return GetSize().y;
}
void BasicGLPane::render( wxPaintEvent& evt )
{
if(!IsShown()) return;
wxGLCanvas::SetCurrent(*m_context);
wxPaintDC(this); // only to be used in paint events. use wxClientDC to paint outside the paint event
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ------------- draw some 2D ----------------
prepare2DViewport(0,0,getWidth()/2, getHeight());
glLoadIdentity();
// white background
// glColor4f(bg_r, bg_g, bg_r, 1);
glBegin(GL_QUADS);
glVertex3f(0,0,0);
glVertex3f(getWidth(),0,0);
glVertex3f(getWidth(),getHeight(),0);
glVertex3f(0,getHeight(),0);
glEnd();
glColor4f(1, 0, 0, 1);
glBegin(GL_QUADS);
glVertex3f(getWidth()/8, getHeight()/3, 0);
glVertex3f(getWidth()*3/8, getHeight()/3, 0);
glVertex3f(getWidth()*3/8, getHeight()*2/3, 0);
glVertex3f(getWidth()/8, getHeight()*2/3, 0);
glEnd();
// ------------- draw some 3D ----------------
prepare3DViewport(getWidth()/2,0,getWidth(), getHeight());
glLoadIdentity();
glColor4f(0,0,1,1);
glTranslatef(0,0,-5);
glRotatef(50.0f, 0.0f, 1.0f, 0.0f);
glColor4f(bg_r, bg_b, bg_b, 1);
// glColor4f(bg_r, bg_b, bg_b, 1);
for (int i = 0; i < 6; i++)
{
glBegin(GL_LINE_STRIP);
glVertex3fv(&v[faces[i][0]][0]);
glVertex3fv(&v[faces[i][1]][0]);
glVertex3fv(&v[faces[i][2]][0]);
glVertex3fv(&v[faces[i][3]][0]);
glVertex3fv(&v[faces[i][0]][0]);
glEnd();
}
glFlush();
SwapBuffers();
}
class WxNotebook1Frame : public wxFrame
{
public:
WxNotebook1Frame(const wxString& title);
};
class WxNotebook1App : public wxApp
{
public:
virtual bool OnInit();
};
bool WxNotebook1App::OnInit()
{
WxNotebook1Frame* frame = new WxNotebook1Frame(L"WxNotebook1");
frame->Show(true);
return true;
}
WxNotebook1Frame::WxNotebook1Frame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title)
{
// Create a top-level panel to hold all the contents of the frame
wxPanel* panel = new wxPanel(this, wxID_ANY);
wxBookCtrlBase* tabs = new wxListbook(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
// Add 2 pages to the wxNotebook widget
int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0};
tabs->AddPage(new BasicGLPane(tabs, args, 1., 1., 1.), L"Tab 1");
tabs->AddPage(new BasicGLPane(tabs, args, 0., 1., 0.), L"Tab 2");
tabs->AddPage(new BasicGLPane(tabs, args, 1., 0., 0.), L"Tab 3");
tabs->AddPage(new BasicGLPane(tabs, args, 0., 0., 1.), L"Tab 4");
// Set up the sizer for the panel
wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
panelSizer->Add(tabs, 1, wxEXPAND);
panel->SetSizer(panelSizer);
// Set up the sizer for the frame and resize the frame
// according to its contents
wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL);
topSizer->SetMinSize(250, 100);
topSizer->Add(panel, 1, wxEXPAND);
SetSizerAndFit(topSizer);
}
wxIMPLEMENT_APP(WxNotebook1App);
Can you please describe in words what exactly doesn't work? I don't see any obvious difference between the 2 videos...
On the 2nd video (notebook) at during the 5th second when I move mouse around "tab 2" header, the tab content starts blinking (black to white background and vise versa). The same for "tab 3".
When I do mouse movement for "listbook" such effect does not appear (video1).
FWIW I don't see this here, the background remains black on tab 2 and red on tab 3, so it must be driver-dependent :-( I have no idea how to debug this, to be honest.
Thank you, my colleague tried the sample provided different hardware/OS, and it seems also ok.
However, our app (wx + vtk) still has the initial problem, described above, even in my colleague environment.
Is there sense to try to here minimal application wx + vtk?
I don't know VTK well so even reproducing this in a minimal example using VTK wouldn't make it simple for me to debug this. Reproducing this in the notebook sample with some really minimal changes mirroring whatever VTK does would be helpful, but is probably going to be more difficult.
P.S. I've deleted a duplicate comment.
Here is an update with sources and videos attached:
When on the line 151 it uses
auto tabs = new wxNotebook
then the behavior is buggy.
but when
auto tabs = new wxListbook
then everything works as expected. The panel with opengl/vtk itself also renderers without any issues. The cmake file is attached to link with VTK.
https://github.com/user-attachments/assets/75e9691d-2229-4f1e-84d7-99ed27183165 https://github.com/user-attachments/assets/302d067b-9255-4f8e-87f6-28ce122068e5
#include <vtkAnimationCue.h>
#include <vtkAnimationScene.h>
#include <vtkCommand.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>
#include <wx/wx.h>
#include <wx/slider.h>
#include <wx/evtloop.h>
#include <wx/notebook.h>
#include <wx/listbook.h>
#if !defined(__WXMSW__)
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#endif
/*****************************************************************************/
namespace utils {
// there is a need to get native window id to let it be OpenGL'ized by vtk
void* NativeHandle(wxWindow* window)
{
auto handle = window->GetHandle();
#if defined(__WXMSW__)
return reinterpret_cast<void*>(handle);
#elif defined(__WXGTK20__)
gtk_widget_realize(handle); // otherwise xwindow id will can be null
//gtk_widget_set_double_buffered(handle, false);
GdkWindow* gdk = gtk_widget_get_window(GTK_WIDGET(handle));
return reinterpret_cast<void*>(GDK_WINDOW_XID(gdk));
#else
#error "TODO: add platform support"
#endif
}
/*****************************************************************************/
} // namespace
/*****************************************************************************/
#define DEBUG_MESSAGE(format, ...) fprintf(stderr, format "\n", __VA_ARGS__)
/*****************************************************************************/
class MyApp: public wxApp
{
public:
using Parent = wxApp;
bool OnInit() override;
int OnExit() override;
};
/*****************************************************************************/
struct MyPanel;
/*****************************************************************************/
class MyFrame: public wxFrame
{
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
private:
wxSlider* slider;
wxButton* button;
MyPanel* panel;
vtkSmartPointer<vtkAnimationScene> scene;
};
/*****************************************************************************/
struct MyInteractor: vtkRenderWindowInteractor
{
using Parent = vtkRenderWindowInteractor;
MyInteractor(wxPanel* panel);
~MyInteractor();
void Initialize() override;
void UpdateSize(int x, int y) override;
private:
wxPanel* panel; // wx widget to be OpenGL'ized
};
/*****************************************************************************/
// Proxies wx-events to VTK events
struct MyPanel: public wxPanel
{
using Parent = wxPanel;
MyPanel(wxWindow* parent, const char* sphere_color);
~MyPanel();
void OnRender(wxPaintEvent& event);
void OnResize(wxSizeEvent& evt);
void OnTabChange(wxBookCtrlEvent&);
MyInteractor* interactor;
vtkRenderer* renderer;
DECLARE_EVENT_TABLE()
};
/*****************************************************************************/
BEGIN_EVENT_TABLE(MyPanel, wxPanel)
EVT_PAINT(MyPanel::OnRender)
EVT_SIZE(MyPanel::OnResize)
END_EVENT_TABLE()
/*****************************************************************************/
bool MyApp::OnInit()
{
DEBUG_MESSAGE("%s", "MyApp::OnInit");
MyFrame *frame = new MyFrame( "Hello World", wxPoint(50, 50), wxSize(450, 340) );
frame->Show( true );
return true;
}
/*****************************************************************************/
int MyApp::OnExit()
{
DEBUG_MESSAGE("%s", "MyApp::OnExit");
return Parent::OnExit();
}
/*****************************************************************************/
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame(NULL, wxID_ANY, title, pos, size)
{
auto sizer = new wxBoxSizer(wxVERTICAL);
auto tabs = new wxNotebook
(
this, wxID_HIGHEST + 1, wxDefaultPosition, wxDefaultSize, wxNB_TOP
);
auto page_0 = new MyPanel(tabs, "Green");
auto page_1 = new MyPanel(tabs, "Red");
auto page_2 = new MyPanel(tabs, "Cyan");
tabs->AddPage(page_0, _("page_0"), true);
tabs->AddPage(page_1, _("page_1"), false);
tabs->AddPage(page_2, _("page_2"), false);
sizer->Add(tabs, 1, wxEXPAND);
SetSizerAndFit(sizer);
PostSizeEvent();
}
/*****************************************************************************/
MyPanel::MyPanel(wxWindow* parent, const char* sphere_color):
Parent(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
{
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
interactor = new MyInteractor(this);
renderer = vtkRenderer::New();
interactor->GetRenderWindow()->AddRenderer(renderer);
renderer->SetBackground(0, 0, 0);
renderer->ResetCamera();
vtkNew<vtkSphereSource> sphereSource;
sphereSource->SetCenter(0.0, 0.0, 0.0);
sphereSource->SetRadius(5.0);
// Make the surface smooth:
sphereSource->SetPhiResolution(100);
sphereSource->SetThetaResolution(100);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkNamedColors> colors;
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(colors->GetColor3d(sphere_color).GetData());
renderer->AddActor(actor);
interactor->GetRenderWindow()->Render();
interactor->Start();
parent->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &MyPanel::OnTabChange, this);
parent->Bind(wxEVT_LISTBOOK_PAGE_CHANGED, &MyPanel::OnTabChange, this);
}
/*****************************************************************************/
MyPanel::~MyPanel()
{
DEBUG_MESSAGE("%s", "MyPanel::~MyPanel");
renderer->Delete();
interactor->GetRenderWindow()->Delete();
interactor->Delete();
}
/*****************************************************************************/
void MyPanel::OnRender(wxPaintEvent& event)
{
DEBUG_MESSAGE("%s", "MyPanel::OnRender");
interactor->GetRenderWindow()->Render();
}
/*****************************************************************************/
void MyPanel::OnResize(wxSizeEvent& evt)
{
auto w = evt.GetSize().GetWidth();
auto h = evt.GetSize().GetHeight();
DEBUG_MESSAGE("MyPanel::OnResize %d x %d", w, h);
interactor->UpdateSize(w, h);
Refresh();
}
/*****************************************************************************/
void MyPanel::OnTabChange(wxBookCtrlEvent& event)
{
DEBUG_MESSAGE("%s %p", "MyFrame::OnTabChange", this);
// interactor->GetRenderWindow()->Initialize();
// interactor->Initialize();
// interactor->GetRenderWindow()->WindowRemap();
}
/*****************************************************************************/
MyInteractor::MyInteractor(wxPanel* panel_): panel{panel_}
{
this->RenderWindow = NULL;
this->SetRenderWindow(vtkRenderWindow::New());
// this->RenderWindow->Delete();
DEBUG_MESSAGE("%s", "MyInteractor::MyInteractor()");
}
/*****************************************************************************/
MyInteractor::~MyInteractor()
{
DEBUG_MESSAGE("%s", "MyInteractor::~MyInteractor");
}
/*****************************************************************************/
void MyInteractor::Initialize()
{
DEBUG_MESSAGE("%s", "MyInteractor::Initialize()");
Parent::Initialize();
RenderWindow->SetWindowId(utils::NativeHandle(panel));
RenderWindow->SetParentId(utils::NativeHandle(panel->GetParent()));
RenderWindow->SetDisplayId(RenderWindow->GetGenericDisplayId());
int *size = RenderWindow->GetSize();
DEBUG_MESSAGE("MyInteractor::Initialize(), sz = %u x %u", size[0], size[1]);
Size[0] = size[0];
Size[1] = size[1];
}
/*****************************************************************************/
void MyInteractor::UpdateSize(int w, int h)
{
if (w != Size[0] || h != Size[1])
{
DEBUG_MESSAGE("MyInteractor::UpdateSize(), sz = %u x %u", w, h);
Size[0] = w;
Size[1] = h;
RenderWindow->SetSize(w, h);
panel->Refresh();
}
}
/*****************************************************************************/
wxIMPLEMENT_APP(MyApp);