Reputation: 3155
I'm building a UI in wxPython,[note 1] and I have three main body panels, two toolbars, and a status bar, with two BoxSizer
elements (a vertical one containing all of the above, and a horizontal one containing the body panels). I cannot get the layout to work quite right, and I'm getting some behavior that I can't find in the documentation. I'm leaving out some details, but here is the relevant (working) part of the app:
import wx
import wx.adv
class MyApp(wx.App):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def OnInit(self):
self.frame = AppFrame(parent=None, title="My Sweet App")
self.SetTopWindow(self.frame)
self.frame.Show()
return True
class AppFrame(wx.Frame):
def __init__(self, size=(1024, 768), *args, **kwargs):
super().__init__(size=size, *args, **kwargs)
# Menu (quit only)
menubar = wx.MenuBar()
file_menu = wx.Menu()
quit_item = wx.MenuItem(
file_menu, wx.ID_EXIT, '&Exit', 'Close the application')
file_menu.Append(quit_item)
self.Bind(wx.EVT_MENU, OnQuit, id=wx.ID_EXIT)
menubar.Append(file_menu, '&File')
self.SetMenuBar(menubar)
# Outer box wrapper
main_box = wx.BoxSizer(orient=wx.VERTICAL)
self.SetSizer(main_box)
main_box.SetMinSize(size)
# Inner box with the three main view panels in it
wrap_panels_box = wx.BoxSizer(orient=wx.HORIZONTAL)
wrap_panels_box.SetMinSize(200, 200)
panel1 = wx.Panel(self, -1)
panel2 = wx.Panel(self, -1)
panel3 = wx.Panel(self, -1)
common_flags = wx.SizerFlags().Expand().Border(wx.ALL, 5)
wrap_panels_box.AddMany([(panel1, common_flags),
(panel2, common_flags),
(panel3, common_flags)])
# Two toolbars for different sets of commands
toolbar1 = wx.ToolBar(parent=self)
tool1 = toolbar1.CreateTool(
toolId=wx.ID_NEW, label='',
bmpNormal=wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR))
toolbar1.AddTool(tool1)
toolbar1.Realize()
toolbar2 = wx.ToolBar(parent=self)
tool2 = toolbar2.CreateTool(
toolId=wx.ID_SAVE, label='',
bmpNormal=wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR))
toolbar2.AddTool(tool2)
toolbar2.Realize()
statusbar = wx.StatusBar(parent=self)
# Add all layout elements
bar_flags = common_flags.Proportion(0)
main_box.Add(toolbar1, bar_flags)
main_box.Add(wrap_panels_box, common_flags.Proportion(1))
main_box.Add(toolbar2, bar_flags)
main_box.Add(statusbar, bar_flags)
self.Layout()
def OnQuit(event):
exit(0)
if __name__ == '__main__':
app = MyApp()
app.MainLoop()
All the sub-component generation methods (_GenerateMenu()
, _GenerateToolStatusBars()
, and _GenerateViewPanels()
) work as expected and basically as desired, so I'm leaving them aside.
The various pieces are largely in the right places, but I have a couple quirks here.
The status bar returned by the _GenerateToolStatusBars()
method acts like it has Proportion(1)
set on it: it expands or contracts vertically as the main window is expanded vertically. It also has additional space above it. I can make this stop, however, by setting the panel proportions as follows:
bar_flags = common_flags.Proportion(-1)
main_box.Add(toolbar1, bar_flags)
main_box.Add(wrap_panels_box, common_flags.Proportion(0))
main_box.Add(toolbar2, bar_flags)
main_box.Add(statusbar, bar_flags)
A -1
value isn't even documented for the Proportion()
[note 2] setting on a Sizer
, and the behavior basically matches what I would expect from the original code sample. What's going on here?
BoxSizer
sliding over earlier elementsRegardless of how I have the proportions set (at least between the two options above), the latter items behave as expected with relation to each other. They slide over the first element if the box becomes small, however. So, if I have _GenerateViewPanels()
return the panels (as usual), they slide up and cover the top toolbar. If I make that do nothing (none of the normal panels are generated), the next toolbar slides up and covers the top toolbar. To reiterate: none of the bottom toolbars or panels interact with each other that way; they only do it with the first toolbar. As before, I'm confused: what's going on here?
[Edit: Made the above code a fully working sample application.]
Notes:
2.9.5.81-r73784
, running against Python 3.3 on both Windows 7 and OS X. It is possible this is a problem from the snapshot build, but I'm doubtful.Proportion()
is a fancy wrapper for the basic proportion=<value>
argument for adding an element to a Sizer. As for why I tried -1
, I just mis-remembered the default/base values for the proportion
argument to BoxSizer.Add()
.Upvotes: 3
Views: 1127
Reputation: 3155
The problem is apparently with the distinct Proportion()
calls. A little testing and use of the wxPython Widget Inspection Tool makes clear that common_flags
is being modified by the calls on it.
All methods of SizerFlags
objects return the same object (not a copy of the object), so calling a method updates the object and all references to it – it does not return a copy, but the same object. So, the original code with comments added explaining what went wrong:
common_flags = wx.SizerFlags().Expand().Border(wx.ALL, 1) # creates the object
bar_flags = common_flags.Proportion(0) # bar_flags points to the common_flags object
# Referencing common_flags with Proportion set to 0
main_box.Add(toolbar1, bar_flags)
# Changes the value of common_flags.
main_box.Add(wrap_panels_box, common_flags.Proportion(1))
# Since bar_flags points to common_flags, it also has Proportion set to 1
main_box.Add(toolbar2, bar_flags)
main_box.Add(statusbar, bar_flags)
The solution is simple: declare bar_flags
and box_flags
as separate objects. This involves some small repetition of code, but it's worth note that you are not repeating the action on the same object; you are performing the same actions on multiple objects. Supplying the following code instead solves the issue:
bar_flags = wx.SizerFlags().Expand().Border(wx.ALL, 1).Proportion(0)
box_flags = wx.SizerFlags().Expand().Border(wx.ALL, 1).Proportion(1)
main_box.Add(tool_status_bars.main, bar_flags)
main_box.Add(wrap_panels_box, box_flags)
main_box.Add(tool_status_bars.panel_view, bar_flags)
main_box.Add(tool_status_bars.status, bar_flags)
As expected, the boxes now relate to each other as they should: the wrap_panels_box
expands, while the tool and status bars do not.
Upvotes: 3