Reputation:
I am trying to make a simple user interface where the user selects some pixel coordinates in an image. I was thinking to do it using matplotlib, and thus I came across this stack overflow question: Store mouse click event coordinates with matplotlib
Where a solution is given that stores clicked coordinates in a global list
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10)
y = x**2
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y)
coords = []
def onclick(event):
global ix, iy
ix, iy = event.xdata, event.ydata
print 'x = %d, y = %d'%(
ix, iy)
global coords
coords.append((ix, iy))
if len(coords) == 2:
fig.canvas.mpl_disconnect(cid)
return coords
cid = fig.canvas.mpl_connect('button_press_event', onclick)
The solution works just fine, however I would like to get rid of those global variables, and I am thinking that getting clicked coordinates would be a perfect job for asyncio.
Naively I tried following code, which obviously doesn't work (however it shows the general idea of what I wish to achieve):
import asyncio
import numpy as np
import matplotlib.pyplot as plt
queue = asyncio.Queue()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(np.random.rand(10))
@asyncio.coroutine
def onclick(event):
yield from queue.put(event.x)
print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
event.button, event.x, event.y, event.xdata, event.ydata))
cid = fig.canvas.mpl_connect('button_press_event', onclick)
@asyncio.coroutine
def consume():
while True:
value = yield from queue.get()
print("Consumed", value)
loop = asyncio.get_event_loop()
loop.create_task(plt.show())
loop.create_task(consume())
loop.run_forever()
How can I utilize matplotlib and asyncio together to react to or collect events?
Upvotes: 1
Views: 1404
Reputation:
I found a solution to using asyncio and matplotlib together.
Basically the main problems seems to be that the gui of matplotlib must be run in the main thread and that running the plot gui will block everything else in the main thread. My solution to this, is to run the asyncio loop in another thread and to use loop.call_soon_thread_safe
and queue.put_no_wait
.
Not sure if this is a good solution, but at least it seems to work so far.
import asyncio
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import threading
queue = asyncio.Queue()
loop = asyncio.get_event_loop()
fig = plt.figure()
img = mpimg.imread('1970_0101_1015_47_1.jpg')
plt.imshow(img)
def onclick(event):
loop.call_soon_threadsafe(queue.put_nowait, (event.x,event.y))
print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
event.button, event.x, event.y, event.xdata, event.ydata))
cid = fig.canvas.mpl_connect('button_press_event', onclick)
@asyncio.coroutine
def consume():
while True:
value = yield from queue.get()
print("Consumed", value)
def start_async_stuff():
print('lets async!')
loop.create_task(consume())
loop.run_forever()
threading.Thread(target=start_async_stuff).start()
plt.show()
Upvotes: 3