Can't resize windows in a sizer. Calling `Layout()` returns them to their original Size

This is so frustrating, because I have done this before, but for some reason I can not figure out what is going on. I’ve been messing with this for 3 hours I have distilled a runnable code snippet that shows the problem. I am trying to resize windows in a wx.FlexGridSizer, but every time I call Layout() it forces the windows back to their original size. I want the sizer to reposition the windows with their new size, but it’s not working:

import wx
import wx.lib.scrolledpanel as scrolled

app = wx.App()
root = wx.Frame(None)
root.Maximize(True)
vbox = wx.BoxSizer()
root.SetSizer(vbox)


################################################################
scrolly = scrolled.ScrolledPanel(root, size=root.GetSize())
fgs = wx.FlexGridSizer(cols=1, vgap=10, hgap=10)
scrolly.SetSizer(fgs)
scrolly.SetupScrolling()
scrolly.SetBackgroundColour(wx.WHITE)
vbox.Add(scrolly)

window = wx.Window(scrolly, size=(1000,1000))
window.SetBackgroundColour(wx.BLUE)

page = wx.Window(scrolly, size=(1000,1000))
page.SetBackgroundColour(wx.BLACK)

fgs.Add(window)
fgs.Add(page)

def zoom(_):
    page.SetSize(500,500)
    window.SetSize(600,600)
    fgs.Layout()

root.Bind(wx.EVT_CHAR_HOOK, lambda e: zoom(e))
#################################################################


def on_resize(e):
    scrolly.SetSize(root.GetVirtualSize())
    scrolly.Update()
    scrolly.SetupScrolling(scrollToTop=False)
root.Bind(wx.EVT_SIZE, on_resize)
root.Show()
app.MainLoop()

Use this:

def zoom(_):
    page.SetMinSize((500,500))
    window.SetMinSize((600,600))
    fgs.Layout()

My recommendation to get started with wxPython is always: use wxGlade and work through the tutorial supplied with it. This way you would learn all the basics, including this one…

Thank you for the help!

Is the tutorial just the calculator tutorial? If so, I can probably knock that out today.

Also, does the tutorial explain why this is the default behavior? Is there a more popular use case where I would want to attempt to change the size of my windows, but not have the sizer automatically adjust to that new size? This method seems very counter-intuitive to me.

A GUI builder lets you try out things and look at the generated source code.

The sizer is supposed to change the size of an item when it is added with proportion>0 or wx.EXPAND.
How would it do that other than calling SetSize? So, when callingLayout(), your value will be overwritten.
If you want to change the size yourself, use sizer.Add with proportion=0 and no wx.EXPAND. Then set the size using SetSize.

P.S.: Sometimes you may want to modify the SizerItem itself:
https://docs.wxpython.org/wx.SizerItem.html#wx-sizeritem
For special things it’s also often helpful if you integrate a shell to your application allowing to access the inner parts of the application while it is running. This can even be helpful in addition to running under a debugger.

well, no paint no faint :sweat_smile: have a nice Easter

4638_GIMP

btn_bm_class_sizer_window_1.py (3.0 KB)

or fancy scroll bars :joy:

btn_bm_class_sizer_window_2.py (3.0 KB)

and in case the flicker didn’t hit you it may be avoided by, you wouldn’t belief it, painting :rofl:

import wx

class Bmp(wx.StaticBitmap):
    def __init__(self, parent, img, txt):
        super().__init__(parent)

        self.over_btns = False
        self.over_sbm = False

        def evt_enter(_):
            self.over_sbm = True
            hbox.ShowItems(True)
            txt.SetLabel('Over Me')
        self.Bind(wx.EVT_ENTER_WINDOW, evt_enter)
        def evt_leave(_):
            self.over_sbm = False
            if not self.over_btns:
                txt.SetLabel('Not Over')
                hbox.ShowItems(False)
        self.Bind(wx.EVT_LEAVE_WINDOW, evt_leave)
        def evt_window(_):
            txt.SetLabel("yes, I'm a static Bitmap..")
        self.Bind(wx.EVT_LEFT_DOWN, evt_window)
        def evt_text(_):
            txt.Refresh()
            txt.SetLabel("yes, I'm text..")
        txt.Bind(wx.EVT_LEFT_DOWN, evt_text)

        def evt_button(evt):
            txt.SetLabel(
                f"yes, I'm a button.. {evt.GetEventObject().GetLabel()}")
        def evt_btn_enter(evt):
            self.over_btns = True
            evt.GetEventObject().SetFocus()
        def evt_btn_leave(_):
            self.over_btns = False
            if not self.over_sbm:
                hbox.ShowItems(False)

        hbox = wx.BoxSizer(wx.HORIZONTAL)           # row of buttons
        def bind_btn(btn):
            btn.Bind(wx.EVT_LEFT_DOWN, evt_button)
            btn.Bind(wx.EVT_ENTER_WINDOW, evt_btn_enter)
            btn.Bind(wx.EVT_LEAVE_WINDOW, evt_btn_leave)
        btn = wx.Button(self, label='btn')
        hbox.Add(btn, 0, wx.ALL, 5)
        bind_btn(btn)
        btn = wx.Button(self, label='btn1')
        hbox.Add(btn, 0, wx.ALL, 5)
        bind_btn(btn)
        self.SetSizerAndFit(hbox)
        hbox.ShowItems(False)
        self.SetBitmap(img)

        self.diff = 0
        def evt_mousewheel(evt):
            if evt.GetWheelRotation() > 0:
                self.diff += 5
            elif self.diff >= 5:
                self.diff -= 5
            else:
                return
            s = img.GetSize()
            si = img.Scale(s[0] + self.diff, s[1] + self.diff)
            parent.SetVirtualSize(si.GetSize())
            self.SetBitmap(si)
        self.Bind(wx.EVT_MOUSEWHEEL, evt_mousewheel)

        def evt_paint(_):
            if self.__nonzero__():
                wx.BufferedPaintDC(self, self.GetBitmap())
        self.Bind(wx.EVT_PAINT, evt_paint)

class Win(wx.ScrolledCanvas):
    def __init__(self, parent, img, txt):
        super().__init__(parent)

        self.SetScrollbars(20, 20, 50, 50)
        self.SetVirtualSize(img.GetSize())
        Bmp(self, img, txt)

class Gui(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent, title='enter / leave StaticBitmap')

        vbox = wx.BoxSizer(wx.VERTICAL)
        txt = wx.StaticText(self)
        vbox.Add(txt, 0, wx.LEFT|wx.TOP, 10)
        img = wx.Image('4638_GIMP.jpg', wx.BITMAP_TYPE_JPEG)
        win = Win(self, img, txt)
        vbox.Add(win, 1, wx.LEFT|wx.TOP|wx.EXPAND, 10)
        self.SetSizer(vbox)
        self.SetBackgroundColour(None)

        self.Show()

app = wx.App()
Gui(None)
app.MainLoop()