1

I'm using wxpython.

I'd like to solve a simple problem for a GUI that consists of a button "Add", which when pressed creates a new button called "Remove". You can press "Add" as many times as you like and many remove buttons are created and added to the panel. What I would like is for when you press one of the remove buttons, that remove button itself is removed from the panel.

The problem is when you bind a function to a button with this:

self.Bind(wx.EVT_BUTTON, self.remove_function, button_name)

you can't pass an argument to the function telling it which button to remove. (Or can you?)

3 Answers 3

2

I wrote about this topic a few years ago here:

The following example might help though:

import wx

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        self.btns = 1

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        add_btn = wx.Button(self, label='Add')
        add_btn.Bind(wx.EVT_BUTTON, self.add_button)

        self.main_sizer.Add(add_btn, 0, wx.CENTER|wx.ALL, 5)
        self.SetSizer(self.main_sizer)

    #----------------------------------------------------------------------
    def add_button(self, event):
        """"""
        new_btn = wx.Button(self, label="Remove %s" % self.btns)
        new_btn.Bind(wx.EVT_BUTTON, self.remove_button)
        self.btns += 1
        self.main_sizer.Add(new_btn, 0, wx.CENTER|wx.ALL, 5)
        self.main_sizer.Layout()

    #----------------------------------------------------------------------
    def remove_button(self, event):
        """"""
        btn = event.GetEventObject()
        print "Deleting %s button" % btn.GetLabel()
        self.main_sizer.Hide(btn)
        self.main_sizer.Remove(btn)
        self.main_sizer.Layout()


########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Buttons")
        panel = MyPanel(self)
        self.Show()

if __name__ == '__main__':
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

You could also pass the button object around by using a lambda, inline functions or functools:

And here's some additional information about binding multiple widgets to the same event handler:

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

Comments

1

If your trouble is to pass an argument to the function, consider following:

self.Bind(wx.EVT_BUTTON, lambda e: otherFunction(arg1, arg2), button_name)

Comments

0

@Mike Driscoll thanks for the great example. Unfortunately, it does not work correctly (anymore?) with Python 3.9.2. Your line

self.main_sizer.Remove(btn)

in the remove_button() method throws an TypeError:

TypeError: Sizer.Remove(): arguments did not match any overloaded call:

overload 1: argument 1 has unexpected type 'Button'

overload 2: argument 1 has unexpected type 'Button'

For me, wx.Sizer.Remove() did only work with the index of the SizerItem. So instead of using

self.main_sizer.Remove(btn)

I used:

sizerItemIdx = 0

for sizerItem in self.main_sizer.GetChildren():
    widget = sizerItem.GetWindow()
    if widget == btn:
        self.main_sizer.Remove(sizerItemIdx)
    sizerItemIdx += 1

Please, let me know if there is a simpler or more elegant/Pythonian way to do this.

Comments

Your Answer

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.