3

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.

1. Status bar expansion

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?

2. Later elements in BoxSizer sliding over earlier elements

Regardless 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:

  1. I'm using a wxPython snapshot build of Phoenix, specifically 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.
  2. 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().
asked Jun 24, 2013 at 18:16
9
  • post one file please that demonstrates your issue ... just a small file as it stands this question probably will not get an answer, since it requires too much of the answerer ... dont use your fancy Proportion thing just siomething like self.main_box.Add(whatever, 0,wx.EXPAND|wx.ALIGN_CENTER) Commented Jun 24, 2013 at 18:22
  • If I can replicate it I will, but the majority of a workable file is there already. I didn't see the behavior in trivial examples. As for Proportion(), why not use it? It's fully supported by wxWidgets and wxPython. Commented Jun 24, 2013 at 18:28
  • I suspect its an issue from adding your sizer_wrapper or something ... unfortunately if you cant replicate it with a simple example this is likely to end up closed as too localized :/ Commented Jun 24, 2013 at 18:32
  • 1
    I have never seen the statusbar do that, but you don't show how you're creating it. As for widgets "sliding" around, that also is hard to see from your example. I am guessing you have parenting issues and will need to use the Widget Inspection Tool to find them. The toolbars shouldn't "slide" at all if they are attached to the frame. Commented Jun 24, 2013 at 18:46
  • 1
    I've never had to use multiple toolbars, but this old post talks about one way to do it: wxpython-users.1045709.n5.nabble.com/… Commented Jun 24, 2013 at 19:25

1 Answer 1

3

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.

answered Jun 25, 2013 at 16:05
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.