0

My program has several buttons that, when clicked, call functions take a little while to run. The problem I'm having is that occasionally a button will "activate" twice and the function will run a second time for seemingly no reason. Also, because the functions take a while, the user can click on the same button multiple times or click on other buttons while the first is still running, and those buttons' functions will get executed immediately after the first finishes.

What I want to do is be able to throw away any pending or queued events so that these buttons can't double-activate themselves and so that the user can't accidentally queue up a bunch of these functions. I suppose I could also just disable all the buttons immediately after one of them has been called but that seems like a really inelegant solution compared to just dumping the event queue.

I tried looking everywhere and it does look like there are functions that can do what I want, except I can't actually figure out how to call it. Of course, that function may not exist in the version of wxPython that I'm using, not entirely sure if that's likely.

Thanks

2
  • 1
    You should create a Minimal, Complete and Verifiable Example to show what it is you've been doing so that we can better direct you towards where needs fixing or modification. Commented Jun 22, 2014 at 5:23
  • What I usually do in such cases is disable the button or widget after a single click and then put a code within the called function that would enable it after its completion. You could try that. Commented Jun 22, 2014 at 8:11

3 Answers 3

1

When a button is pressed you could try using the method SetEvtHandlerEnabled to false on the window that you want the events to stop, and then setting it back to True when its finished.

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

Comments

0

you should probably do something like this (keep in mind it is a very minimal example)

import wx ,time
import threading
def the_actual_long_task(whatever=None):
    for i in range(15):
        time.sleep(1)
        print "TASK RUNNING!"
     return whatever
my_lock = threading.Lock()
def long_task(callback,some_other_arg=None):
    my_lock.acquire() #this will block until the lock is released by another thread
    my_return_value = the_actual_long_task(some_other_arg) 
    wx.CallAfter(callback,my_return_value)
    my_lock.release() #allow another thread to acquire the lock
def OnTaskDone(result):
    print "Task Done!",result
    my_button.Enable(True)
    my_button.SetLabel("Run Task")
def OnButton(event):
    t = threading.Thread(target=long_task,args=(OnTaskDone,))
    t.start()
    my_button.Enable(False)
    my_button.SetLabel("Test In Progress...")
a = wx.App(redirect = False)
f = wx.Frame(None,-1,"Example")
my_button = wx.Button(f,-1,"Run Task",size=(200,-1))
my_button.Bind(wx.EVT_BUTTON,OnButton)
f.Show()
a.MainLoop()

using threads to run the task will allow you to not block the ui

disabling the button will not allow it to be clicked again until the task completes

4 Comments

I'm hesitant to have my functions run in a separate thread because only one of them can run at a time (there's 8 buttons and they all access a single piece of hardware). So, I have to disable all of them if one of them is pressed. That and I don't care if the GUI updates while one of my functions is running, the only thing that matters is the function's return value. So, that being the case, do you think the best way is just to disable every button every time one of them gets called? I can certainly create some kind of wrapper or decorator for all my button events but it seems inelegant.
edited to hand back a return value ... without seeing an example of what you are doing in which i can reproduce the issue .... i really recommend using a thread with a lock as that is how you handle long running tasks ...
Well, it's not really an issue because it's pretty clear what's going on, it's that while the GUI is blocked if I click on a button it stores that click event until the program returns to the GUI's event loop, at which point it handles that click event that's now like 3 minutes old. My issue is, I need it to not handle that click event. Ever. If one of my functions is running (doesn't matter if its in its own thread or its blocking the GUI), the GUI can't be allowed to register any events. I'm just starting to wonder if maybe I should be creating my own event loop after all.
Also, I should note that I'm hesitant to provide an example just because it's going to take forever to re-name all the variables and function calls such. If you really think you need an example to see what I'm trying to do I can write one tomorrow (and maybe re-ask the question while im at it)
0

As others have already pointed out, the simple answer is to just disable the button until the long running task completes. Then it won't accept additional button presses. On the other hand, you could call the button's Unbind() method and remove the event handler from the button in the event handler itself.

Something like this should work:

def myEventHandler(self, event):
    self.ButtonOne.Unbind(wx.EVT_BUTTON, self.myEventHandler)

See also:

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.