Search code examples
pythonuser-interfacewxpython

How can I make a wxpython Widget span two cells without pushing other widgets aside?


I'm trying to make a form that has several input fields. Underneath these fields I want to have a wxpython Ultimate List Control (for all intents and purposes it's the same thing as a List Control). My issue is with sizers. To give some context, my form looks like this

Name         [TextCtrl]
Blah         [TextCtrl]

ListControl

I want it to look like

Name   [TextCtrl]
Blah   [TextCtrl]
ListCtrl (this spans to the end of the row)

My problem is when I try to add the List Control. I want the list control to Stretch from The Static Text to the Text Control, but it pushes the TextControl over. Can someone please point me in the right direction? I have attached the relevant code below.

class UserField(wx.Dialog):
def __init__(self, parent):
    wx.Dialog.__init__(self, parent=parent, title="Info", size=(350, 400),
                       style=wx.DEFAULT_FRAME_STYLE)


    self.init_ui()
    self.Center()
    self.ShowModal()

def init_ui(self):
    panel = wx.Panel(self, wx.ID_ANY)
    hbox = wx.BoxSizer(wx.VERTICAL)
    flex_grid = wx.FlexGridSizer(5, 2, 5, 10) # row, col, vgap, hgap


    info_text = wx.StaticText(parent=panel, label="Enter information")
    self.search_button = wx.Button(parent=panel, label="Search")
    self.list_control = UltimateListCtrl(panel,
        agwStyle=wx.LC_REPORT | wx.BORDER_SUNKEN | ULC_HAS_VARIABLE_ROW_HEIGHT, )

    flex_grid.AddMany(
            [
                info_text, self.search_button
            ]
    )
    lbox = wx.BoxSizer(wx.HORIZONTAL)
    lbox.Add(self.list_control
    hbox.Add(flex_grid, wx.EXPAND|wx.ALL)
    hbox.Add(lbox, proportion=1, flag=wx.ALL|wx.EXPAND)
    panel.SetSizer(hbox)

Solution

  • Here's a quick demonstration of wx.GridBagSizer. The program opens a simple frame with a single button that spawns a dialog with a GridBagSizer. You can place items in the sizer according to a position (pos) and optionally allow a widget to span multiple rows and/or columns (span).

    import wx
    
    class Example(wx.Frame):
    
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
            self.SetSize((300, 200))
            self.Centre()
            self.Show(True)
            self.InitUI()
    
        def InitUI(self):    
            panel = wx.Panel(self)
            sizer = wx.BoxSizer()
            btn = wx.Button(panel, label="Spawn Window")
            btn.Bind(wx.EVT_BUTTON, self.spawn_window)
            sizer.Add(btn)
            panel.SetSizerAndFit(sizer)
    
        def spawn_window(self, evt):
            UserField(self)
    
        def OnQuit(self, e):
            self.Close()
    
    class UserField(wx.Dialog):
    
        def __init__(self, parent):
            wx.Dialog.__init__(self, parent=parent, title="Info", size=(350, 400),
                               style=wx.DEFAULT_FRAME_STYLE)
            self.init_ui()
            self.Center()
            self.ShowModal()
    
        def init_ui(self):
            panel = wx.Panel(self)
            sizer = wx.GridBagSizer(10, 10)
            field1Label = wx.StaticText(panel, label="Field 1")
            field2Label = wx.StaticText(panel, label="Field 2")
            field1Ctrl = wx.TextCtrl(panel)
            field2Ctrl = wx.TextCtrl(panel)
            listCtrl = wx.ListCtrl(panel)
            sizer.Add(field1Label, pos=(0, 0))
            sizer.Add(field2Label, pos=(1, 0))
            sizer.Add(field1Ctrl, pos=(0, 1))
            sizer.Add(field2Ctrl, pos=(1, 1))
            # HERE'S THE IMPORTANT LINE. NOTE THE 'span' ARGUMENT:
            sizer.Add(listCtrl, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
            panel.SetSizerAndFit(sizer)
    
    if __name__ == '__main__':
        ex = wx.App()
        mainFrame = Example(None)
        ex.MainLoop()