# Author: Miguel Martinez Lopez
#
# Uncomment the next line to see my email
# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))
"""
I provide in this module the function "exec_async".
"exec_async" executes the function "computation" asyncronously with the provided "args" and "kwargs" without blocking the tkinter event loop.
If "callback" is provided, it will be called with the result when the computation is finnished.
"Polling" will be the frequency to poll to check for results.
There is two methods to execute the task: using multiprocessing or using threads.
"""
import traceback
import multiprocessing
import threading
import Queue
MULTIPROCESSING = 0
THREADS = 1
def exec_async(window, computation, args=(), kwargs={}, callback=None, polling=100, method=MULTIPROCESSING):
if method == MULTIPROCESSING:
_request_results = _request_results_using_multiprocessing
elif method == THREADS:
_request_results = _request_results_using_threads
future_result = _request_results(computation, args=args, kwargs=kwargs)
if callback:
_after_completion(window, future_result, callback, polling)
return future_result
def _request_results_using_multiprocessing(func, args, kwargs):
future_result= multiprocessing.Queue()
request_parameters = func, args, kwargs, future_result
worker = multiprocessing.Process(target=_compute_result, args=request_parameters)
worker.daemon = True
worker.start()
return future_result
def _request_results_using_threads(func, args, kwargs):
future_result= Queue.Queue()
request_parameters = func, args, kwargs, future_result
worker = threading.Thread(target=_compute_result, args=request_parameters)
worker.daemon = True
worker.start()
return future_result
def _after_completion(window, future_result, callback, polling):
def check():
try:
result = future_result.get(block=False)
except:
window.after(polling, check)
else:
callback(result)
window.after(0, check)
def _compute_result(func, func_args, func_kwargs, future_result):
try:
_result = func(*func_args, **func_kwargs)
except Exception as errmsg:
_result = Exception(traceback.format_exc())
future_result.put(_result)
if __name__ == "__main__":
try:
from Tkinter import Tk, Frame, Entry, Label, Button, IntVar, StringVar, LEFT
import tkMessageBox as messagebox
except ImportError:
from tkinter import Tk, Frame, Entry, Label, Button, IntVar, StringVar, LEFT
from tkinter import messagebox
def fibonnacci(n):
if n == 0: return 0
elif n == 1: return 1
else: return fibonnacci(n-1)+fibonnacci(n-2)
disabled = False
def calculate_fibonacci():
global disabled
if disabled:
messagebox.showinfo("warning", "It's still calculating...")
return
def callback(result):
global disabled
disabled = False
result_var.set(result)
disabled = True
exec_async(root, fibonnacci, args=(n.get(),), callback=callback)
root = Tk()
n = IntVar(value=1)
row = Frame(root)
row.pack()
Entry(row, textvariable=n).pack(side=LEFT)
Button(row, text="Calculate fibonnaci", command =calculate_fibonacci).pack(side=LEFT)
Button(row, text="It's responsive", command= lambda: messagebox.showinfo("info", "it's responsive")).pack(side=LEFT)
result_var = StringVar()
Label(root, textvariable=result_var).pack()
root.mainloop()
Diff to Previous Revision
--- revision 2 2017-02-04 07:46:23
+++ revision 3 2017-02-04 07:48:00
@@ -7,7 +7,7 @@
"""
I provide in this module the function "exec_async".
-"exec_async" executes the function "computation" asyncronously with the provided "args" and "kwargs".
+"exec_async" executes the function "computation" asyncronously with the provided "args" and "kwargs" without blocking the tkinter event loop.
If "callback" is provided, it will be called with the result when the computation is finnished.
"Polling" will be the frequency to poll to check for results.
There is two methods to execute the task: using multiprocessing or using threads.