Welcome, guest | Sign In | My Account | Store | Cart
# 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 3 2017-02-04 07:48:00
+++ revision 4 2017-02-05 00:50:21
@@ -8,62 +8,71 @@
 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 "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
-import multiprocessing
-import threading
-import Queue
+
 
 MULTIPROCESSING = 0
 THREADS = 1
 
 
-def exec_async(window, computation, args=(), kwargs={}, callback=None, polling=100, method=MULTIPROCESSING):
+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:
-        _after_completion(window, future_result, callback, polling)
+    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()
-    request_parameters = func, args, kwargs, future_result
 
-    worker = multiprocessing.Process(target=_compute_result, args=request_parameters)
+    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()
-    request_parameters = func, args, kwargs, future_result
 
-    worker = threading.Thread(target=_compute_result, args=request_parameters)
+    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, polling):
+def _after_completion(window, future_result, callback, errback, polling):
     def check():
         try:
             result = future_result.get(block=False)
         except:
             window.after(polling, check)
         else:
-            callback(result)
+            if isinstance(result, Exception):
+                if errback is not None:
+                    errback(result)
+            else:
+                if callback is not None:
+                    callback(result)
                 
     window.after(0, check)
 
@@ -106,7 +115,7 @@
 
     root = Tk()
     
-    n = IntVar(value=1)
+    n = IntVar(value=30)
     row = Frame(root)
     row.pack()
     Entry(row, textvariable=n).pack(side=LEFT)

History