2

I have been working on a GUI code in wxpython that more or less repeats the same information 4 times. There are a lot of buttons on the screen that I have had to bind events to, and I'm finding that I have a lot of on_button_click definitions that look almost identical. So, I was wondering if there was a way to just pass through a parameter when binding the button to an event and cutting out 3 of the definitions. Here is an example:

self.VDBenchSlot1 = wx.Button(self, -1, "Slot 1 VDBench")
sizer.Add(self.VDBenchSlot1,(1, 5), (1, 5), wx.EXPAND)
self.VDBenchSlot1.Bind(wx.EVT_BUTTON, self.VDBenchSlot1_clicked)

self.VDBenchSlot2 = wx.Button(self, -1, "Slot 2 VDBench")
sizer.Add(self.VDBenchSlot2,(1, 5), (1, 5), wx.EXPAND)
self.VDBenchSlot2.Bind(wx.EVT_BUTTON, self.VDBenchSlot2_clicked)

self.VDBenchSlot3 = wx.Button(self, -1, "Slot 3 VDBench")
sizer.Add(self.VDBenchSlot3,(1, 5), (1, 5), wx.EXPAND)
self.VDBenchSlot3.Bind(wx.EVT_BUTTON, self.VDBenchSlot3_clicked)

self.VDBenchSlot4 = wx.Button(self, -1, "Slot 4 VDBench")
sizer.Add(self.VDBenchSlot4,(1, 5), (1, 5), wx.EXPAND)
self.VDBenchSlot4.Bind(wx.EVT_BUTTON, self.VDBenchSlot4_clicked)

def VDBenchSlot1_clicked(self, event):       
    global diskchange
    if diskchange[1] == 'No Disk':
        self.TextSlot1.AppendText("No Disk is currently in the slot so you cannot run this! \n")
    else:  
        # Open the file startDisk#VD.sh that has the setup to start running VDBench
        os.system("echo pcieRocks | sudo -S gnome-terminal --profile=VDbench --working-directory=/home/pciedev3ubuntu/Documents -e './vdbench -f disk%dVDscript.txt -vr' &" %diskchange[1])

def VDBenchSlot2_clicked(self, event):
    global diskchange

    if diskchange[2] == 'No Disk':
        self.TextSlot2.AppendText("No Disk is currently in the slot so you cannot run this! \n")
    else:   
        # Open the file startDisk#VD.sh that has the setup to start running VDBench
        os.system("echo pcieRocks | sudo -S gnome-terminal --profile=VDbench --working-directory=/home/pciedev3ubuntu/Documents -e './vdbench -f disk%dVDscript.txt -vr' &" %diskchange[2])

def VDBenchSlot3_clicked(self, event):
    global diskchange

    if diskchange[3] == 'No Disk':
        self.TextSlot3.AppendText("No Disk is currently in the slot so you cannot run this! \n") 
    else:   
        # Open the file startDisk#VD.sh that has the setup to start running VDBench
        os.system("echo pcieRocks | sudo -S gnome-terminal --profile=VDbench --working-directory=/home/pciedev3ubuntu/Documents -e './vdbench -f disk%dVDscript.txt -vr' &" %diskchange[3])

def VDBenchSlot4_clicked(self, event):
    global diskchange

    if diskchange[4] == 'No Disk':
        self.TextSlot4.AppendText("No Disk is currently in the slot so you cannot run this! \n")
    else:   
        # Open the file startDisk#VD.sh that has the setup to start running VDBench
        os.system("echo pcieRocks | sudo -S gnome-terminal --profile=VDbench --working-directory=/home/pciedev3ubuntu/Documents -e './vdbench -f disk%dVDscript.txt -vr' &" %diskchange[4])

I've tried changing VDBenchslotx_clicked to VDBenchslotx_clicked() and passing parameters to it, but one of two things happen; it tells me that the parameters entered do not match the parameters of the def, or it lets my program run but it automatically executes the def at program startup and not when the button is pressed, and the button then does not function properly.

JJJ
  • 32,902
  • 20
  • 89
  • 102
Trever Wagenhals
  • 381
  • 5
  • 14
  • 5
    I don't know wxpython, but it seems like you can't do that, which is annoying: even the humble Tkinter allows you to pass an additional arg when binding a callback to a widget. However, the top answer to [this question](http://stackoverflow.com/questions/173687/is-it-possible-to-pass-arguments-into-event-bindings) shows how you can achieve what you want. – PM 2Ring Apr 07 '16 at 14:20
  • BTW, you can reduce repetition by making a helper function that builds your buttons. A common technique is to define such helper functions inside the method that creates the widgets (typically the `__init__` method), so it can access the local variables of the method without you having to pass those variables in the function call. – PM 2Ring Apr 07 '16 at 14:23
  • I'm not sure what you mean when you say helper function as I'm fairly new to this... do you have a good link that you can supply as an example? – Trever Wagenhals Apr 07 '16 at 14:37
  • [Here](http://stackoverflow.com/a/27077057/4014959) is some code I wrote that uses the GTK2+ GUI framework. The helper function is named `make_button`. Hopefully that will give you the general idea even though GTK is rather different to wxPython. And I'll put a short example here that uses the code in your question. – PM 2Ring Apr 07 '16 at 14:50

2 Answers2

2

Use a lambda expression to pass arguments to bound functions. For example:

self.VDBenchSlot1.Bind(wx.EVT_BUTTON, lambda event: self.VDBenchSlot_clicked(event, 1))

def VDBenchSlot_clicked(self, event, position):   
    if position == 1:
        text_slot = self.TextSlot1
    elif position == 2:
        text_slot = self.TextSlot2
    elif position == 3:
        text_slot = self.TextSlot3
    elif position == 4:
        text_slot = self.TextSlot4    
    global diskchange
    if diskchange[position] == 'No Disk':
        text_slot.AppendText("No Disk is currently in the slot so you cannot run this! \n")
    else:  
        # Open the file startDisk#VD.sh that has the setup to start running VDBench
        os.system("echo pcieRocks | sudo -S gnome-terminal --profile=VDbench --working-directory=/home/pciedev3ubuntu/Documents -e './vdbench -f disk%dVDscript.txt -vr' &" %diskchange[position])
dbc
  • 677
  • 8
  • 21
0

dbc (and the linked question) show how to achieve what you want. But here's a short demo of the helper function concept that I mentioned in my comment above.

def make_button(text, callback):
    button = wx.Button(self, -1, text)
    sizer.Add(button, (1, 5), (1, 5), wx.EXPAND)
    button.Bind(wx.EVT_BUTTON, callback)
    return button

self.VDBenchSlot1 = make_button("Slot 1 VDBench", self.VDBenchSlot1_clicked)
self.VDBenchSlot2 = make_button("Slot 2 VDBench", self.VDBenchSlot2_clicked)

Note that make_button doesn't have self in its signature. That's because it's not a method, it's a function defined inside a method. For a fully-running example in GTK2+, please see this answer.

Also note that my code is based on your original code, but it should be easy for you to adapt it to use dbc's new VDBenchSlot_clicked callback method.

Community
  • 1
  • 1
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Ah okay, I see the general idea. I'm pretty sure I would want to take sizer.Add out of the def though, right? because otherwise I'm just going to keep creating all of those buttons at the same location. – Trever Wagenhals Apr 07 '16 at 14:55
  • @TreverWagenhals: I've fixed my code. Sorry about the earlier confusion. :oops: – PM 2Ring Apr 08 '16 at 05:46
  • 1
    made some edits to your post to reflect my concern about sizer.Add. Showed what it looks like it both cases so you can see what I mean a little better. Thanks for this though, between these two adjustments I've already scrapped about 300 lines of code. – Trever Wagenhals Apr 08 '16 at 13:06