<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div name="messageBodySection" style="font-size: 14px; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">Hi everyone,
<div><br /></div>
<div>I’m new to both asynchronous programming and to GUI programming so please forgive any newbie mistakes in the below code.</div>
<div><br /></div>
<div>I’m developing a simple GUI app that takes some parameters from the user, then when the user clicks a button, launches some long-running task, *which includes making and saving a matplotlib plot*. When using the standard Tk `app.mainloop()`, this causes the application to become non-responsive while the long computation is running.</div>
<div><br /></div>
<div>So I figured I’d use this newfangled asyncio thnigy that everyone is talking about. =P I settled on modelling it after Guido’s own tkcrawl.py [1]_, which periodically “manually" calls tkinter’s `update()` within the asyncio event loop. Unfortunately, when matplotlib is invoked in the asynchronous task, it crashes tkinter with the error:</div>
<div><br /></div>
<div>RuntimeError: main thread is not in main loop<br /></div>
<div><br /></div>
<div>Apparently calling tk functions from anything other than the main thread is a big no-no.</div>
<div><br /></div>
<div>But, this is where I’m stuck: how do I asynchronously launch a long-running task that includes matplotlib plotting? Any ideas about how I can tweak my design so my app is responsive but tasks include making big plots?</div>
<div><br /></div>
<div>I’ve included sample code below. By swapping out “fasync” for “fsync”, you too can experience the frustration of a beach-balled or greyed out app — but at least the app works! ;)</div>
<div><br /></div>
<div>Thanks!</div>
<div><br /></div>
<div>Juan.</div>
<div><br /></div>
<div>.. [1] https://bugs.python.org/file43873/tkcrawl.py</div>
<div><br /></div>
<div><br /></div>
<div>Minimal code:</div>
<div><br /></div>
<div>
<div>import asyncio</div>
<div>import matplotlib</div>
<div>matplotlib.use('TkAgg')</div>
<div>import tkinter as tk</div>
<div>from tkinter import ttk</div>
<div>from skimage._shared._tempfile import temporary_file</div>
<div>import numpy as np</div>
<div><br /></div>
<div><br /></div>
<div>@asyncio.coroutine</div>
<div>def _async(func, *args):</div>
<div>    loop = asyncio.get_event_loop()</div>
<div>    return (yield from loop.run_in_executor(None, func, *args))</div>
<div><br /></div>
<div><br /></div>
<div>STANDARD_MARGIN = (3, 3, 12, 12)</div>
<div><br /></div>
<div><br /></div>
<div>def long_computation():</div>
<div>    import time</div>
<div>    time.sleep(4)</div>
<div>    import matplotlib.pyplot as plt</div>
<div>    fig, ax = plt.subplots()</div>
<div>    ax.imshow(np.random.rand(500, 500))</div>
<div>    with temporary_file(suffix='.png') as fname:</div>
<div>        fig.savefig(fname)</div>
<div><br /></div>
<div><br /></div>
<div>class MainWindow(tk.Tk):</div>
<div>    def __init__(self):</div>
<div>        super().__init__()</div>
<div>        self.title('I gonna die')</div>
<div>        main = ttk.Frame(master=self, padding=STANDARD_MARGIN)</div>
<div>        main.grid(row=0, column=0, sticky='nsew')</div>
<div>        fsync = long_computation</div>
<div>        fasync = lambda: asyncio.ensure_future(_async(long_computation))</div>
<div>        button = ttk.Button(master=main, padding=STANDARD_MARGIN,</div>
<div>                            text='Run stuff',</div>
<div>                            command=fasync)</div>
<div>        button.grid(row=0, column=0)</div>
<div>        main.pack()</div>
<div><br /></div>
<div><br /></div>
<div>def tk_update(loop, app):</div>
<div>    try:</div>
<div>        app.update()</div>
<div>    except tk.TclError as e:</div>
<div>        loop.stop()</div>
<div>        return</div>
<div>    loop.call_later(.01, tk_update, loop, app)</div>
<div><br /></div>
<div><br /></div>
<div>def main():</div>
<div>    loop = asyncio.get_event_loop()</div>
<div>    app = MainWindow()</div>
<div>    #app.mainloop()</div>
<div>    tk_update(loop, app)</div>
<div>    loop.run_forever()</div>
<div><br /></div>
<div><br /></div>
<div>if __name__ == '__main__':</div>
<div>    main()</div>
</div>
<div><br /></div>
</div>
</body>
</html>