# 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.
If an exception is raised during computation, instead errback will be called.
"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
MULTIPROCESSING = 0
THREADS = 1
def exec_async(window, computation, args=(), kwargs={}, callback=None, errback=None, polling=100, method=MULTIPROCESSING):
if method == MULTIPROCESSING:
_request_results = _request_results_using_multiprocessing
elif method == THREADS:
_request_results = _request_results_using_threads
else:
raise ValueError("Not valid method")
future_result = _request_results(computation, args=args, kwargs=kwargs)
if callback or errback:
_after_completion(window, future_result, callback, errback, polling)
return future_result
def _request_results_using_multiprocessing(func, args, kwargs):
import multiprocessing
future_result= multiprocessing.Queue()
worker = multiprocessing.Process(target=_compute_result, args=(func, args, kwargs, future_result))
worker.daemon = True
worker.start()
return future_result
def _request_results_using_threads(func, args, kwargs):
import threading
import Queue
future_result= Queue.Queue()
worker = threading.Thread(target=_compute_result, args=(func, args, kwargs, future_result))
worker.daemon = True
worker.start()
return future_result
def _after_completion(window, future_result, callback, errback, polling):
def check():
try:
result = future_result.get(block=False)
except:
window.after(polling, check)
else:
if isinstance(result, Exception):
if errback is not None:
errback(result)
else:
if callback is not None:
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=30)
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 4 2017-02-05 00:50:21
+++ revision 5 2017-02-05 00:50:50
@@ -16,10 +16,8 @@
import traceback
-
MULTIPROCESSING = 0
THREADS = 1
-
def exec_async(window, computation, args=(), kwargs={}, callback=None, errback=None, polling=100, method=MULTIPROCESSING):
if method == MULTIPROCESSING: