today is a good day to create some wxpython scrolled panels; what i had imagined is a scrolled panel with client size 256x256 and virtual size 1024x1024, using a wxpython 4.0.7 gtk2 build;

something that does not work

this is what i came up with earlier; very natural no brainer:

#!/usr/bin/env python3

'''
show a scrolled panel with client size 256x256 and virtual size 1024x1024;
'''

from wx.lib.scrolledpanel import ScrolledPanel
import wx

def OnScrollWin(evt):
    panel = evt.GetEventObject()
    frame = panel.GetParent()
    ##  expect: `frame=(..., ...), panel=(256, 256)`
    print(f'frame={frame.GetClientSize()}, panel={panel.GetClientSize()}')
    evt.Skip()

app = wx.App()

frame = wx.Frame(None)

panel = ScrolledPanel(frame)
panel.SetMinClientSize((256, 256))
panel.SetScrollbars(1, 1, 1024, 1024)
panel.Bind(wx.EVT_SCROLLWIN, OnScrollWin)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(panel, 0)
frame.SetSizer(sizer)

frame.Fit()
frame.Layout()
frame.Center()
frame.Show()

app.MainLoop()

unfortunately, this does not work; as i wrote it i was looking for a client size of 256x256 when scrolling; but the program always gave wrong result;

something that does work

then i realized there must be some magics; after some trial and error, this is what i came up with later that finally works:

#!/usr/bin/env python3

'''
show a scrolled panel with client size 256x256 and virtual size 1024x1024;
'''

from wx.lib.scrolledpanel import ScrolledPanel
import wx

def OnScrollWin(evt):
    panel = evt.GetEventObject()
    frame = panel.GetParent()
    ##  expect: `frame=(..., ...), panel=(256, 256)`
    print(f'frame={frame.GetClientSize()}, panel={panel.GetClientSize()}')
    evt.Skip()

app = wx.App()

frame = wx.Frame(None)
frame.SetClientSize(frame.GetClientSize())  # magic_1

panel = ScrolledPanel(frame)
panel.SetMinClientSize((256, 256))
panel.SetScrollbars(1, 1, 1024, 1024)
panel.Bind(wx.EVT_SCROLLWIN, OnScrollWin)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(panel, 0)
frame.SetSizer(sizer)

frame.Show()                                # magic_2
panel.SetMinClientSize((256, 256))          # magic_3
frame.Fit()
frame.Layout()
frame.Center()

app.MainLoop()

there are 3 magics that made it work:

  1. call SetClientSize after frame creation;

    setting-a-getting really looks like a (harmless) noop, but is actually no noop;

  2. call Show before Fit and Layout;

    i always considered early Show to be smelly code, cuz it displays partial result; but in this case i have no other way to get it work; my conjecture is that the scrollbar size is only known after the frame is shown (because it is native), but this is only my guess;

  3. call SetMinClientSize on panel, again;

    yup, just call it again after frame is shown; who knows why the first call no longer works? just call it, again;

to be honest, these are real magics; i could not explain why; one needs magics to deal with scrolled panels, really;