1

I cannot figure out why my hierarchical layout is not doing as I expect in wxPython.

The basic idea is as follows:

+- Win1 ------+ +- Win2 -----+ +- Win3 ----------+ | | | | | | +-------------+ +------------+ +-----------------+ +- Win4 ----------+ +- Win5 ---------------------+ | | | | +-----------------+ +----------------------------+ +- Win6 -----------------------------------------+ | | +------------------------------------------------+ 

I am creating a vertical box layout to handle the three regions {1/2/3, 4/5, 6}. Inside each of those regions is another (horizontal) box layout to handle (in the first region, for example) the {1, 2, 3} sub-regions.

Then, inside each of those sub-regions, I have a static box sizer to give me the border with a multi-line, non-user-editable, text control.

Now the code below has been simplified down to two rows with two columns in the first row and one in the second. Only the first column in the first row is an attempt at drawing the nice-border control, the others are just static text controls.

import wx class MyFrame(wx.Frame): def __init__(self, title): wx.Frame.__init__(self, None, title=title, pos=(150,50), size=(1720,930)) self.Bind(wx.EVT_CLOSE, self.OnClose) self.topPanel = wx.Panel(self, wx.ID_ANY) self.screenPanel = wx.Panel(self.topPanel, wx.ID_ANY) self.spacer0 = wx.StaticText(self.topPanel, wx.ID_ANY, "") self.dummy1 = wx.StaticText(self.topPanel, wx.ID_ANY, "dummy1") self.dummy2 = wx.StaticText(self.topPanel, wx.ID_ANY, "dummy2") self.topSizer = wx.BoxSizer(wx.VERTICAL) self.row1Sizer = wx.BoxSizer(wx.HORIZONTAL) self.row2Sizer = wx.BoxSizer(wx.HORIZONTAL) self.screenSizer = wx.StaticBoxSizer(wx.HORIZONTAL, self.screenPanel, "Screen") self.screen = wx.TextCtrl(self.screenPanel, wx.ID_ANY, "This is the first line\nXYZZY\nPLUGH", wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY) self.row1Sizer.Add(self.screenSizer, 0, wx.ALL, 5) self.row1Sizer.Add(self.spacer0, 1, wx.ALL, 5) self.row1Sizer.Add(self.dummy1, 0, wx.ALL, 5) self.row2Sizer.Add(self.dummy2, 0, wx.ALL, 5) self.topSizer.Add(self.row1Sizer, 0, wx.ALL | wx.EXPAND, 5) self.topSizer.Add(self.row2Sizer, 0, wx.ALL | wx.EXPAND, 5) self.screenPanel.SetSizer(self.screenSizer) self.topPanel.SetSizer(self.topSizer) self.topPanel.Layout() def OnClose(self, event): self.Destroy() app = wx.App() top = MyFrame("My") top.Show() app.MainLoop() 

However, there appears to be some problems which I think may be caused by my confusion over whether sizers or non-sizers should own the resources being controlled, but I cannot get it to behave. The code as it stands gives me:

enter image description here

As you can see, the static box appears to be missing from around the first control, and the sizing appears screwed up. I would have thought the size would be calculated on layout so that it was at least large enough to hold the inner control and border.

Can anyone let me know what I'm doing wrong with this code?

It's also crashing on exit, which may be related. If not, I can handle that as a separate issue.

2 Answers 2

1

The static box may be either created independently or the sizer may create it itself as a convenience. In any case, the sizer owns the wx.StaticBox control and will delete it in the wx.StaticBoxSizer destructor.

Note that since wxWidgets 2.9.1 you are encouraged to create the windows which are added to wx.StaticBoxSizer as children of wx.StaticBox

So something like this:

import wx class MyFrame(wx.Frame): def __init__(self, title): wx.Frame.__init__(self, None, title=title, pos=(150,50), size=(500,400)) self.Bind(wx.EVT_CLOSE, self.OnClose) self.SetMinSize((500,400)) self.topPanel = wx.Panel(self, wx.ID_ANY) box1 = wx.StaticBox(self.topPanel, wx.ID_ANY, "Win1") box1.SetForegroundColour('#000000') self.textCtrl1 = wx.TextCtrl(box1, wx.ID_ANY, "This is the first line\nXYZZY\nPLUGH", wx.DefaultPosition, size = (200,100), style = wx.TE_MULTILINE | wx.TE_READONLY) self.dummy4 = wx.StaticText(box1, wx.ID_ANY, "Text below textctrl 1") boxSizer1 = wx.StaticBoxSizer(box1, wx.VERTICAL) boxSizer1.Add(self.textCtrl1, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) boxSizer1.Add(self.dummy4, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) box2 = wx.StaticBox(self.topPanel, wx.ID_ANY, "Win2") box2.SetForegroundColour('#000000') self.spacer = wx.StaticText(box2, wx.ID_ANY, "A spacer") boxSizer2 = wx.StaticBoxSizer(box2, wx.VERTICAL) boxSizer2.Add(self.spacer, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) box3 = wx.StaticBox(self.topPanel, wx.ID_ANY, "Win3") box3.SetForegroundColour('#000000') self.dummy1 = wx.StaticText(box3, wx.ID_ANY, "dummy text 1") boxSizer3 = wx.StaticBoxSizer(box3, wx.VERTICAL) boxSizer3.Add(self.dummy1, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) box4 = wx.StaticBox(self.topPanel, wx.ID_ANY, "Win4") box4.SetForegroundColour('#000000') self.textCtrl2 = wx.TextCtrl(box4, wx.ID_ANY, "This is the second line\nXYZZY\nPLUGH", wx.DefaultPosition, size = (200,100), style = wx.TE_MULTILINE | wx.TE_READONLY) boxSizer4 = wx.StaticBoxSizer(box4, wx.VERTICAL) boxSizer4.Add(self.textCtrl2, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) box5 = wx.StaticBox(self.topPanel, wx.ID_ANY, "Win5") box5.SetForegroundColour('#000000') self.dummy2 = wx.StaticText(box5, wx.ID_ANY, "dummy text 2") boxSizer5 = wx.StaticBoxSizer(box5, wx.VERTICAL) boxSizer5.Add(self.dummy2, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) box6 = wx.StaticBox(self.topPanel, wx.ID_ANY, "Win6") box6.SetForegroundColour('#000000') self.dummy3 = wx.StaticText(box6, wx.ID_ANY, "dummy text 3") boxSizer6 = wx.StaticBoxSizer(box6, wx.VERTICAL) boxSizer6.Add(self.dummy3, proportion = 0, flag=wx.ALIGN_CENTER|wx.EXPAND) self.topSizer = wx.BoxSizer(wx.VERTICAL) self.row1Sizer = wx.BoxSizer(wx.HORIZONTAL) self.row2Sizer = wx.BoxSizer(wx.HORIZONTAL) self.row3Sizer = wx.BoxSizer(wx.HORIZONTAL) self.row1Sizer.Add(boxSizer1, 0, wx.ALL, 5) self.row1Sizer.Add(boxSizer2, 1, wx.ALL|wx.EXPAND, 5) #Matches size of box1 self.row1Sizer.Add(boxSizer3, 0, wx.ALL|wx.EXPAND, 5) #Matches size of box1 self.row2Sizer.Add(boxSizer4, 0, wx.ALL, 5) self.row2Sizer.Add(boxSizer5, 1, wx.ALL, 5) # Does not match size of box4 self.row3Sizer.Add(boxSizer6, 1, wx.ALL, 5) self.topSizer.Add(self.row1Sizer, 0, wx.ALL | wx.EXPAND, 5) self.topSizer.Add(self.row2Sizer, 0, wx.ALL | wx.EXPAND, 5) self.topSizer.Add(self.row3Sizer, 0, wx.ALL | wx.EXPAND, 5) self.topPanel.SetSizer(self.topSizer) self.topPanel.Layout() def OnClose(self, event): self.Destroy() app = wx.App() top = MyFrame("My Window Layout") top.Show() app.MainLoop() 

enter image description here

However, be aware that sometimes the theme on your OS desktop fails to cope with boxes, so it's not a given!
Specifically, Mint-X on Linux Mint fails miserably, the same code with the theme Mint-X, looks like this.

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Excellent! Not only is everything laid out properly, you fixed my crash-on-exit bug :-) I gather the important bit was "you are encouraged to create the windows which are added to wx.StaticBoxSizer as children of wx.StaticBox" since I was adding it to the top panel.
@paxdiablo Not really, the old method will work but I couldn't work out why screenPanel was messing things up, so I started from afresh with the new format. See my second answer.
0

Your original code will work with a few modifications.
I still can't get my head around the issue with screenPanel via screenSizer being added into the row1Sizer, other than screen never gets added to the sizer and is not expanded.
Your modified code (modifications noted):

import wx class MyFrame(wx.Frame): def __init__(self, title): wx.Frame.__init__(self, None, title=title, pos=(150,50), size=(420,230)) self.Bind(wx.EVT_CLOSE, self.OnClose) self.topPanel = wx.Panel(self, wx.ID_ANY) ##self.screenPanel = wx.Panel(self.topPanel, wx.ID_ANY) self.spacer0 = wx.StaticText(self.topPanel, wx.ID_ANY, "") self.dummy1 = wx.StaticText(self.topPanel, wx.ID_ANY, "dummy1") self.dummy2 = wx.StaticText(self.topPanel, wx.ID_ANY, "dummy2") self.topSizer = wx.BoxSizer(wx.VERTICAL) self.row1Sizer = wx.BoxSizer(wx.HORIZONTAL) self.row2Sizer = wx.BoxSizer(wx.HORIZONTAL) ##self.screenSizer = wx.StaticBoxSizer(wx.HORIZONTAL, ## self.screenPanel, "Screen") self.screenSizer = wx.StaticBoxSizer(wx.HORIZONTAL, self.topPanel, "Screen") ##self.screen = wx.TextCtrl(self.screenPanel, wx.ID_ANY, self.screen = wx.TextCtrl(self.topPanel, wx.ID_ANY, "This is the first line\nXYZZY\nPLUGH", wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY) ##Additional line self.screenSizer.Add(self.screen, 0, wx.ALL|wx.EXPAND, 5) ## self.row1Sizer.Add(self.screenSizer, 0, wx.ALL|wx.EXPAND, 5) self.row1Sizer.Add(self.spacer0, 1, wx.ALL, 5) self.row1Sizer.Add(self.dummy1, 0, wx.ALL, 5) self.row2Sizer.Add(self.dummy2, 0, wx.ALL, 5) self.topSizer.Add(self.row1Sizer, 0, wx.ALL | wx.EXPAND, 5) self.topSizer.Add(self.row2Sizer, 0, wx.ALL | wx.EXPAND, 5) ##self.screenPanel.SetSizer(self.screenSizer) self.topPanel.SetSizer(self.topSizer) self.topPanel.Layout() def OnClose(self, event): self.Destroy() app = wx.App() top = MyFrame("My") top.Show() app.MainLoop() 

Results in: enter image description here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.