No widget is created in wx.EVT_SIZE callback function

first of all, I know there is a grid layout in wxPython.

But I try to mimic wNim’s Layout DSL with kiwisolver firstly. Then the ultimate goal is to coin Autolayout in wxPython which is more concise to describe GUI layout; which is very hard for me now, so it could be better that the community can make one.

Let’s go back to my question. In the code, the position and size of widget should be recalculated when the user resize the frame. In my simplified code, a button named b1 is placed on the frame. The horizontal gaps between button and frame are 10, the vertical gaps are 5.

if I commented out self.Bind(wx.EVT_SIZE, self.OnResize, self), the button with default size will be created. However if self.Bind(wx.EVT_SIZE, self.OnResize, self) is used, there is no button displayed. So what is the problem?

Thanks

import wx

class Example(wx.Frame):
    def __init__(self, parent, title):
        super(Example, self).__init__(parent, title=title,
            size=(350, 300))

        self.InitUI()
        self.Bind(wx.EVT_SIZE, self.OnResize, self)
        self.Centre()

    def OnResize(self, evt):
        self.w = self.panel.GetSize()[0]
        self.h = self.panel.GetSize()[1]

        self.b1_w =  self.w - 10 * 2
        self.b1_h =  self.h - 5 * 2

        self.b1_x = 10
        self.b1_y = 5

        print((self.b1_x, self.b1_y))
        print(self.b1_w, self.b1_h)

        self.b1.SetPosition((self.b1_x, self.b1_y))
        self.b1.SetSize(wx.Size(self.b1_w, self.b1_h))
        self.b1.Update()

        print(dir(self.b1))

        self.Refresh()

        evt.Skip()

    def InitUI(self):
        self.panel = wx.Panel(self)
        self.b1 = wx.Button(self.panel, wx.ID_ANY, "hello")

def main():

    app = wx.App()
    ex = Example(None, title='Absolute positioning')
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

Remove the print(dir(...)) and add a print("OnResize", self.panel.GetSize()) at the top of OnResize and you will immediately see what’s the problem…

The panel is created with a default size of (20,20). The final size will only be set after your handler returns.
So, create the panel with a size to fill the available space.
I think self.GetClientSize() will return the correct size without the window decoration.

P.S.: I think you can remove the Update and Refresh calls.

Thanks. The following code works
BTW, self.Refresh() must be in the def OnResize(self, evt):, otherwise the drawn button often has wrong size.

import wx

class Example(wx.Frame):
    def __init__(self, parent, title):
        super(Example, self).__init__(parent, title=title, size=(350, 300))

        self.InitUI()
        self.Bind(wx.EVT_SIZE, self.OnResize, self)
        self.Centre()

    def OnResize(self, evt):
        self.w, self.h = self.panel.GetSize()

        self.b1_w =  self.w - 10 * 2
        self.b1_h =  self.h - 5 * 2

        self.b1_x = 10
        self.b1_y = 5

        self.b1.SetPosition((self.b1_x, self.b1_y))
        self.b1.SetSize(self.b1_w, self.b1_h)

        self.Refresh() # or self.Update()

        self.panel.SetSize(self.GetClientSize() )

        evt.Skip()

    def InitUI(self):
        self.panel = wx.Panel(self)
        self.b1 = wx.Button(self.panel, wx.ID_ANY, "hello")

        self.panel.SetSize(self.GetClientSize() )

def main():
    app = wx.App()
    ex = Example(None, title='Absolute positioning')
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

Refresh has nothing to do with the size. It simply causes a EVT_PAINT event to be sent in the near future. If you’re still seeing size issues with that example then it’s likely to be a layout issue. One possibility is that there hasn’t yet been a EVT_SIZE event to do the layout, due to your setting the frame’s size before binding the size event handler. Changing the order that size related things are done would probably change that behavior. Or adding a call to self.Layout() at the end of __init__ might also take care of it.