I am running Ubuntu/Lubuntu Linux 22.04.1 x86_64 which has the following packages:
wxPython: python3-wxgtk4.0 (4.0.7)
wxWidgets: libwxgtk3.0-gtk3-0v5 (3.0.5.1)
Gtk: libgtk-3-0 (3.24.33)
Python/wxPython info:
$ python3
Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import wx
>>> wx.version()
'4.0.7 gtk3 (phoenix) wxWidgets 3.0.5'
My problem is that I cannot get a page (wx.Panel
) of a wx.Notebook
to refresh its layout consistently. The following is a sample app to demonstrate:
import wx
class Dialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "A Dialog Window",
parent.GetPosition(), wx.Size(640, 480),
wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
tabs = wx.Notebook(self)
panel1 = wx.Panel(tabs, wx.ID_ANY)
layout1 = wx.BoxSizer(wx.VERTICAL)
layout1.AddStretchSpacer()
layout1.Add(wx.StaticText(panel1, label="Centered"), 1,
wx.ALIGN_CENTER)
layout1.AddStretchSpacer()
panel1.SetSizer(layout1)
panel1.SetAutoLayout(True)
panel1.Layout()
panel2 = wx.Panel(tabs, wx.ID_ANY)
layout2 = wx.BoxSizer(wx.VERTICAL)
layout2.Add(wx.StaticText(panel2, label="Not Centered"), 1)
panel2.SetSizer(layout2)
panel2.SetAutoLayout(True)
panel2.Layout()
tabs.AddPage(panel1, "Page 1")
tabs.AddPage(panel2, "Page 2")
self.Refresh()
self.Update()
class Window(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Test", wx.Point(50, 50),
wx.Size(200, 200))
btn = wx.Button(self, wx.ID_ANY, "PUSH ME")
btn.Bind(wx.EVT_BUTTON, self.onButton)
def onButton(self, evt):
dia = Dialog(self)
dia.ShowModal()
# ~ dia.Destroy()
app = wx.App()
frame = Window()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
When the button is pressed & the dialog is opened, the text on the first page should be centered. However, this generally isn’t the case initially. Usually the dialog will open without the notebook’s children being positioned & sized correctly. It appears that the panel isn’t being expanded to fill the entire page. Like in the following image.
If I repeatedly close & reopen the dialog it will eventually be laid out correctly, as in the following image.
Once it has opened with the correct layout, it will do the same when opened subsequently. It is also possible to force the layout to refresh by resizing the dialog window.
I am wondering if this is a problem with Gtk itself. When the dialog opens with the correct layout the system prints this message (error?):
gtk_box_gadget_distribute: assertion 'size >= 0' failed in GtkNotebook
I don’t know what it means, but it sounds like Gtk is having issues detecting space for the widgets. And it is weird that the message displays when the layout is correct. The problem sounds similar to the issues described in Frame doesn't refresh its content & self.Refresh() doesn't do anything. And I have tried using Refresh()
& Update()
on the dialog, the notebook, & the panel without any change in behavior. I have also tried adding wx.GetApp().Yield()
before the call to ShowModal()
.
Is it possible to get it to work every time the dialog is opened or is this a Gtk platform issue that wxPython isn’t able to circumvent?
I have also asked about this on Stackoverflow ( python - wxPython/wxGtk Platform Issue: Cannot Force Refresh/Update on Opening Modal Dialog Panel - Stack Overflow ). But at the time of posting this topic, have not received any suggestions.
Edit: I forgot to mention that making the dialog an attribute of the wx.Frame
class solves the problem. But I would like the dialog to be created in the button event handler.
...
self.dia = Dialog(self)
def onButton(self, evt):
self.dia.ShowModal()
...
Edit: If I remove the call to panel1.Layout()
the text is fully displayed but still not centered. If I use panel1.SetSizerAndFit
some space is allocated above for the spacer, but text is still off center.
...
#panel1.SetSizer(layout1)
#panel1.SetAutoLayout(True)
#panel1.Layout()
panel1.SetSizerAndFit(layout1)
...
Perhaps related, the dialog window does not always respect the pos
parameter when it is constructed.
Edit: I created a video to demonstrate the behavior: https://youtu.be/WKcFXOVhzAU