Python – wxPython minimal size of Frame with a Panel

pythonwxpythonwxwidgets

wxpython 2.8.11.0, python 2.7

If i put some Sizer with some controls directly into a Frame like

import wx

app=wx.App()

frm = wx.Frame(None, title='title')

sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(frm))
sizer.Add(wx.SpinCtrl(frm))

frm.SetSizerAndFit(sizer)
frm.Show()

app.MainLoop()

the Frame will automagically have a correct minimum size to contain the Sizer and it is not possible to make it smaller.
If there is a Panel in between (as needed for tabbing between controls) this does not work, the window can be made too small.

import wx

app=wx.App()

frm = wx.Frame(None, title='title')
pan = wx.Panel(frm)

sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(pan))
sizer.Add(wx.SpinCtrl(pan))

pan.SetSizerAndFit(sizer)
frm.Show()

app.MainLoop()

Additionally frm.Fit() and frm.SetMinSize(frm.GetEffectiveMinSize()) are required to get the same behavior. Full Code:

import wx

app=wx.App()

frm = wx.Frame(None, title='title')
pan = wx.Panel(frm)

sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(pan))
sizer.Add(wx.SpinCtrl(pan))

pan.SetSizerAndFit(sizer)
frm.Fit()
frm.SetMinSize(frm.GetEffectiveMinSize())
frm.Show()

app.MainLoop()

I am okay with frm.Fit(), but i dislike frm.SetMinSize(frm.GetEffectiveMinSize()). Isn't there a better solution so that the Frame automatically considers the minimum size of the Panel just like with the Sizer before? I'm considered with what happens if the EffectiveMinSize changes after another control is added to the sizer.

Edit:
Apparently

panel.SetSizerAndFit(sizer)
frm.Fit()
frm.SetMinSize(frm.GetEffectiveMinSize())

should be replaced by

panel.SetSizer(sizer)
sizer.SetSizeHints(frm)

which looks somewhat cleaner. So in total this looks like

import wx

app=wx.App()

frm = wx.Frame(None, title='title')
pan = wx.Panel(frm)

sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(pan))
sizer.Add(wx.SpinCtrl(pan))

pan.SetSizer(sizer)
sizer.SetSizeHints(frm)
frm.Show()

app.MainLoop()

Is this the preferred way to do this?

If widgets are added later on even the first approach with the Sizer directly on the Frame doesn't get it right without intervention, so i think that is a totally different question.

Best Answer

The most elegant way (IMHO) is given in phineas' answer. It is slightly inefficient to have an extra otherwise unneeded sizer but I don't think it can really be noticeable.

Some people do call SetSizeHints() manually, as in your last example, and this works as well and might be more clear (whenever I use an extra sizer I feel the need to leave a comment explaining why it shouldn't be removed).

Unfortunately there is no better way and I am not sure if we can even add one because you need to do something with all of the panel, the sizer and the frame and this means that you can't do it with a single method call without passing unrelated parameters to it. I.e. we could have something like

panel.SetSizerAndFitParent(sizer, frame);

but it's not really clear whether this would be better.

Related Topic