I am having some issues properly expanding my sizers. I have added two sub panels which I was hoping would solve my issue, but it seems to have made it worse. What I am hoping to do is have top_panel
expand to the extents of the width of the main panel
. vbox_top_right
should expand to fill the rest of hbox_top
. The bottom_panel
should then expand both horizontally and vertically to fill in the rest of the space of the main panel
. None of this is happening.
Once I split everything up into two sub panels, I am now having an issues where I have a large blank space up top. Also, my bottom_panel
does not seem to be expanding anywhere. I am also not sure if I am using SetSizerAndFit
correctly because I haven't seen anywhere mentioning the use of it on multiple panels. Am I only supposed to be applying it to the main panel
?
Note that I am forcing use of wx 3.0. I have 2.8 and 3.0 installed and had issues with wx.StaticBoxSizer with 2.8. I know this likely has nothing to do with my issue, just the code won't work correctly if you are trying it in 2.8.
CODE:
import wxversion
wxversion.select('3.0')
import wx
import os
import sys
VERSION = '1.0.0'
class GUI(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=title, pos=wx.DefaultPosition,
size=wx.Size(1280, 768), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
menu_bar = wx.MenuBar()
file_menu = wx.Menu()
self.cwd = os.getcwd()
# Quit code
file_item = file_menu.Append(wx.ID_EXIT, 'Quit', 'Quit Application')
menu_bar.Append(file_menu, '&File')
self.SetMenuBar(menu_bar)
# Add Main panel
self.panel = wx.Panel(self)
# Add Top and Bottom Panels
self.top_panel = wx.Panel(self.panel)
self.bottom_panel = wx.Panel(self.panel)
# Create horizontal and vertical boxes
self.hbox_main = wx.BoxSizer(wx.HORIZONTAL)
self.vbox_main = wx.BoxSizer(wx.VERTICAL)
self.hbox_top = wx.BoxSizer(wx.HORIZONTAL)
self.vbox_top_left = wx.BoxSizer(wx.VERTICAL)
self.vbox_top_right = wx.BoxSizer(wx.VERTICAL)
####################################################
# TOP
####################################################
##########################
# TOP LEFT
##########################
# List box text
self.lbl_filter = wx.StaticText(self.top_panel, wx.ID_ANY,
u"Select all cases to apply file to",
wx.DefaultPosition, wx.DefaultSize, 0)
self.lbl_filter.Wrap(-1)
self.vbox_top_left.Add(self.lbl_filter, 0, wx.EXPAND)
# The list box that all the file names are in
self.list_box = wx.CheckListBox(self.top_panel, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(300, 300),
choices=[], style=wx.LB_HSCROLL|wx.LB_MULTIPLE|wx.LB_NEEDED_SB|wx.LB_SORT)
self.vbox_top_left.Add(self.list_box, 0, wx.EXPAND, 5)
# List box filter text
self.lbl_filter = wx.StaticText(self.top_panel, wx.ID_ANY,
u"Case filter (separate wildcards with a comma and choose filter logic (AND or OR)\n(i.e. HS, 2022 with AND selected will modify all 2022 HS cases)",
wx.DefaultPosition, wx.DefaultSize, 0)
self.lbl_filter.Wrap(-1)
self.vbox_top_left.Add(self.lbl_filter, 0, wx.EXPAND)
# List box filter
self.hbox_filter = wx.BoxSizer(wx.HORIZONTAL)
self.txt_filter = wx.TextCtrl(self.top_panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
self.txt_filter.SetMinSize(wx.Size(300, -1))
self.hbox_filter.Add(self.txt_filter, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 5)
# List box radio buttons
logic_choices = [u"AND", u"OR"]
self.rdo_logic = wx.RadioBox(self.top_panel, wx.ID_ANY, u"Filter Logic", wx.DefaultPosition, wx.DefaultSize,
logic_choices, 1, wx.RA_SPECIFY_ROWS)
self.rdo_logic.SetSelection(0)
self.hbox_filter.Add(self.rdo_logic, 0, wx.EXPAND, 5)
# Add filter stuff to vbox_top
self.vbox_top_left.Add(self.hbox_filter, 0, wx.EXPAND)
# Add top components to hbox
self.hbox_top.Add(self.vbox_top_left, 0, wx.EXPAND)
##########################
# TOP RIGHT
##########################
# Add warning text
self.lbl_warning = wx.StaticText(self.top_panel, wx.ID_ANY,
u"*** WARNING *** HELLO WORLD ",
wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTER_HORIZONTAL)
self.lbl_warning.Wrap(-1)
self.vbox_top_right.Add(self.lbl_warning, 0, wx.EXPAND, 5)
# Radio buttons for software choice
rdo_choices = [u"App 1", u"App 2", u"App 3"]
self.rdo_software = wx.RadioBox(self.top_panel, wx.ID_ANY, u"Select Software", wx.DefaultPosition,
wx.DefaultSize, rdo_choices, 1, wx.RA_SPECIFY_COLS)
self.rdo_software.SetSelection(0)
self.vbox_top_right.Add(self.rdo_software, 0, wx.EXPAND, 5)
# Checkbox for archive
self.cb_archive = wx.CheckBox(self.top_panel, wx.ID_ANY, u"Archive files before running", wx.DefaultPosition,
wx.DefaultSize, 0)
self.vbox_top_right.Add(self.cb_archive, 0, wx.EXPAND, 5)
# Checkbox for saving
self.cb_save = wx.CheckBox(self.top_panel, wx.ID_ANY, u"Save files after running", wx.DefaultPosition,
wx.DefaultSize, 0)
self.vbox_top_right.Add(self.cb_save, 0, wx.EXPAND, 5)
# Folder selection label
self.lbl_cases = wx.StaticText(self.top_panel, wx.ID_ANY, u"Select Folder With Cases", wx.DefaultPosition,
wx.DefaultSize, 0)
self.lbl_cases.Wrap(-1)
self.vbox_top_right.Add(self.lbl_cases, 0, wx.EXPAND, 5)
# Add Folder selection
self.hbox_folder = wx.BoxSizer(wx.HORIZONTAL)
self.txt_cases = wx.TextCtrl(self.top_panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
self.txt_cases.SetMinSize(wx.Size(400, -1))
self.hbox_folder.Add(self.txt_cases, 0, wx.EXPAND, 5)
# Folder select button
self.btn_cases = wx.Button(self.top_panel, wx.ID_ANY, u"Case Folder", wx.DefaultPosition, wx.DefaultSize, 0)
self.hbox_folder.Add(self.btn_cases, 0, wx.EXPAND, 5)
# Add to sizer
self.vbox_top_right.Add(self.hbox_folder, 1, wx.SHAPED, 5)
self.hbox_top.Add(self.vbox_top_right, 0, wx.EXPAND, 5)
self.vbox_main.Add(self.hbox_top, 0, wx.EXPAND)
####################################################
# BOTTOM
####################################################
self.bottom_box = wx.StaticBox(self.bottom_panel, label='Progress Output')
self.hbox_output = wx.StaticBoxSizer(self.bottom_box, wx.HORIZONTAL)
self.txt_output = wx.TextCtrl(self.bottom_box, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize,
wx.TE_MULTILINE | wx.TE_READONLY)
# Put it all together
self.vbox_main.Add(self.top_panel, 1, wx.EXPAND)
self.vbox_main.Add(self.bottom_panel, 1, wx.EXPAND)
self.top_panel.SetSizerAndFit(self.hbox_top)
self.bottom_panel.SetSizerAndFit(self.hbox_output)
self.panel.SetSizerAndFit(self.vbox_main)
self.Centre()
self.Layout()
if __name__ == '__main__':
app = wx.App(0)
MainFrame = GUI(None, title='Batch Apply %s' % VERSION)
app.SetTopWindow(MainFrame)
MainFrame.Show()
app.MainLoop()
The main frame has only one child called panel
. So, this only child of a frame (not of another kind of window) will fit it's parent's client area when it gets resized. Good. It would'nt be true in case of more than one child.
You have two more panels, as children of panel
. This would be good if, for example, you want different background colors. If not, that's not necessary, but there's nothing wrong with these superfluous panels.
There are two different regions: top (not resizable in vertical direction) and bottom (resizable). This would require two sub-sizers of the 'main sizer' that handles the layout of the contents of panel
. But you are using two panels for these regions, so better let's use a main sizer (I'll use vbox_main
) for the layout inside panel
of these two panels. The layout of the contents of a sub-panel will be handled by a sizer.
In the 'top' region there are also two different areas; so another two sub-sizers. Your design is correct.
vbox_top_left
wants the controls it manages (children of top_panel
) to fit in the available space. Because this is a vertical sizer we need:
1) A child can change vertical size: use proportion=1
2) A child can change horizontal size: use wx.EXPAND
flag.
3) If would be good to add vertical resizable spacers between some controls.
Apply similar criteria for the controls size-handled by vbox_top_right
(with children of top_panel
too).
Bottom area is peculiar because you want a rectangle to be drawn around a label and a text control. For this, we need a special sizer: StaticBoxSizer. It's peculiar in the sense that the controls it handles are children not of a panel, but of the underlaying wx.StaticBox
. See the upper docs link for an example and more explanations.
self.hbox_output = wx.StaticBoxSizer(wx.HORIZONTAL, self.bottom_panel)
self.hbox_output.Add(wx.StaticText(self.hbox_output.GetStaticBox(), ....), ...)
self.hbox_output.Add(wx.TextCtrl(self.hbox_output.GetStaticBox(), ....), 1, wx.EXPAND, 5)
To add children to this sizer follow the same criteria as for 'top' area. There's nothing distinct here.
Now, behaviours of the sizers (I skip other sub-sizers you use):
# No vertical nor horizontal expanding
# self.hbox_top.Add(self.vbox_top_left, 0, wx.EXPAND) <<== not what expected
self.hbox_top.Add(self.vbox_top_left, 0)
# Only horizontal expanding
# self.hbox_top.Add(self.vbox_top_right, 0, wx.EXPAND, 5) <<== not what expected
self.hbox_top.Add(self.vbox_top_right, 1, 0, 5)
The last job is to bind panels and sizers:
self.top_panel.SetSizer(self.hbox_top)
self.bottom_panel.SetSizer(self.hbox_output)
# Top panel expands only in horizontal
self.vbox_main.Add(self.top_panel, 0, wx.EXPAND)
# Bottom part expands in both directions
self.vbox_main.Add(self.bottom_panel, 1, wx.EXPAND)
self.panel.SetSizerAndFit(self.vbox_main)
self.Centre()
self.Layout()
Using a single panel is easier. I've used your two sub-panels just to demostrate how it works: Main sizer handles sub-panels, each panel uses a sizer for its sizers that handle children.