I am trying to automate tasks behind a gui. My gui has multiple buttons that are supposed to start certain functions and one button to stop any function that is currently running. When one function is running, all buttons except the "cancel" button are greyed out. I need the extra thread so that my gui is still responding while the lengthy functions are running.
Now I would like to implement one decorator to decorate those functions. This decorator should run the function in a separate thread. When the user presses the cancel button the function in this extra thread should receive some kind of stop signal which the decorated function can use to stop its current task and exit.
I know that it's possible to implement a class of type threading.Thread for each function and insert the current function as "def run(self):", but it does not seem like an elegant solution.
Is there a way? To me it seems like a common problem, but I have not found any solution via Google except to write the functions as classes and run those as separate threads.
Edit 1:
Let me add some examples. Right now my functions look like this:
def function1:
function_code
But if I create classes it would look like this:
class class1(threading.Thread):
stopper = None
def __init__(self):
init_code
def run(self):
function_code
def function1:
t = class1()
t.stopper = some_stop_condition
t.run()
The second code is much longer and needs a class and a function for every button. It just seems so much more complicated and I hope it's unnecessary.
Am I wrong or am I doing something wrong?
Edit 2:
My new Code, after salomonderossi's great example:
def run_async(func):
@functools.wraps(func)
def async_func(*args, **kwargs):
queue = Queue.Queue()
t = threading.Thread(target=func, args=(queue,) + args, kwargs=kwargs)
t.start()
return queue, t
return async_func
# @layout_decorators.runInSeparateThread()
@run_async
def test2(myQueue):
print "executing test2"
import time
for k in range(6):
print k
try:
myQueue.get(False)
except Queue.Empty:
print "cancelled"
return
time.sleep(1)
def test22():
print "executing test22"
global globalQueue
globalQueue, t = test2()
if __name__ == "__main__":
import time
print "\n\n"
test22()
time.sleep(2)
globalQueue.put("stop")
But it stops the thread at the first chance. Even if I remove the last line (which I thought stops the thread) my output is
executing test22
executing test2
0
cancelled
The decorator needs to create a way to comminucate with the thread. In this example every function which should be run as a thread must have the queue argument at first place. This queue is used to communicate with the threads. In this example anything can be put in the queue to stop the thread, because the function only checks if a value can be put out of the queue.
With queue.get(False) the function tries to get a element from the queue (without waiting). If there is no element (the queue is empty) an Exception is raised. Otherwise there is something in the queue and the thread is told to quit.
To tell the thread to quit, something has to be put in the thread queue. This is done with queue.put("stop"). In this case the argument doesn't matter.
This means, you have to check regullary if there is something in the queue and react to it (In this case just stop processing).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from threading import Thread
from functools import wraps
from time import sleep
try:
import queue as Queue
except ImportError:
import Queue as Queue
def run_async(func):
@wraps(func)
def async_func(*args, **kwargs):
queue = Queue.Queue()
t = Thread(target=func, args=(queue,) + args, kwargs=kwargs)
t.start()
return queue, t
return async_func
@run_async
def do_something_else(queue):
while True:
sleep(1)
print("doing something else")
# check if something in the queue and return if so
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
@run_async
def print_somedata(queue):
print('starting print_somedata')
sleep(2)
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
print('print_somedata: 2 sec passed')
sleep(2)
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
print('print_somedata: another 2 sec passed')
sleep(2)
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
print('finished print_somedata')
def test():
threads = list()
# at this moment the thread is created and starts immediately
threads.append(print_somedata())
print('back in main')
# at this moment the thread is created and starts immediately
threads.append(print_somedata())
print('back in main')
# at this moment the hread is created and starts immediately
threads.append(do_something_else())
print('back in main')
# at this moment the hread is created and starts immediately
threads.append(do_something_else())
print('back in main')
print("Wait a bit in the main")
sleep(1) # uncomment the wait here to stop the threads very fast ;)
# you don't have to wait explicitly, as the threads are already
# running. This is just an example to show how to interact with
# the threads
for queue, t in threads:
print("Tell thread to stop: %s", t)
queue.put('stop')
#t.join()
if __name__ == '__main__':
test()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With