I keep seeing people asking about how to add or remove widgets after they’ve already started their wxPython application. This is actually something that’s really easy to do, so I decided it was time to write a simple tutorial on the subject. I have had to do this myself from time to time depending on what kind of user was accessing my program so I could show slightly different options. Anyway, let’s get started!

I decided to make this really simple. All this application will do is allow the user to add or remove buttons. The following script will create a window similar to the one at the beginning of this article. If you press the “add” button a few times, you should see something like this:

As you can see, you end up with more buttons! Now let’s take a moment and read the code. I’ll explain it as soon as you get done reading it.

import wx
 
########################################################################
class MyPanel(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        self.number_of_buttons = 0
        self.frame = parent
 
        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
        controlSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.widgetSizer = wx.BoxSizer(wx.VERTICAL)
 
        self.addButton = wx.Button(self, label="Add")
        self.addButton.Bind(wx.EVT_BUTTON, self.onAddWidget)
        controlSizer.Add(self.addButton, 0, wx.CENTER|wx.ALL, 5)
 
        self.removeButton = wx.Button(self, label="Remove")
        self.removeButton.Bind(wx.EVT_BUTTON, self.onRemoveWidget)
        controlSizer.Add(self.removeButton, 0, wx.CENTER|wx.ALL, 5)
 
        self.mainSizer.Add(controlSizer, 0, wx.CENTER)
        self.mainSizer.Add(self.widgetSizer, 0, wx.CENTER|wx.ALL, 10)
 
        self.SetSizer(self.mainSizer)
 
    #----------------------------------------------------------------------
    def onAddWidget(self, event):
        """"""
        self.number_of_buttons += 1
        label = "Button %s" %  self.number_of_buttons
        name = "button%s" % self.number_of_buttons
        new_button = wx.Button(self, label=label, name=name)
        self.widgetSizer.Add(new_button, 0, wx.ALL, 5)
        self.frame.fSizer.Layout()
        self.frame.Fit()
 
    #----------------------------------------------------------------------
    def onRemoveWidget(self, event):
        """"""
        if self.widgetSizer.GetChildren():
            self.widgetSizer.Hide(self.number_of_buttons-1)
            self.widgetSizer.Remove(self.number_of_buttons-1)
            self.number_of_buttons -= 1
            self.frame.fSizer.Layout()
            self.frame.Fit()
 
########################################################################
class MyFrame(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, parent=None, title="Add / Remove Buttons")
        self.fSizer = wx.BoxSizer(wx.VERTICAL)
        panel = MyPanel(self)
        self.fSizer.Add(panel, 1, wx.EXPAND)
        self.SetSizer(self.fSizer)
        self.Fit()
        self.Show()
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

I think this is pretty straight forward code, so we’ll just focus on the important bits. The first topic I’m going to point out is that I call the Frame’s Fit() method right before I show it. I normally avoid using Fit, but I was having trouble getting the Frame to change size appropriately whenever I added or removed the buttons and Fit fixed that issue for me. I should note that Fit always tries to make the widgets fit the container and sometimes it ends up doing it in ways I don’t like.

Anyway, the other bit in the onAddWidget and onRemoveWidget methods. You normally want to call Layout on the container object to make it update and layout the controls whenever you add or remove a widget. Oddly enough, it seems that Fit does that automatically, so those Layout() calls that you see in the code above can actually be removed. I tried removing the Fit ones to see if Layout was enough, but when you do that, the frame doesn’t update its size, so Fit seems to be required in this case. Now, if you happened to be adding or removing widgets in such a way that it wouldn’t effect the frame’s overall size, I think Layout would be enough.

Finally, as a side note, you sometimes use Layout() at the end of a Freeze / Thaw update as well.

Alright, that’s it! Now you too should be able to add or remove widgets after your application is running too. I hope you learned something new.

Print Friendly