使用 asyncio 对 matplotlib 中的鼠标单击事件做出反应

Posted

技术标签:

【中文标题】使用 asyncio 对 matplotlib 中的鼠标单击事件做出反应【英文标题】:react to mouse click events in matplotlib using asyncio 【发布时间】:2016-04-23 08:23:46 【问题描述】:

我正在尝试制作一个简单的用户界面,用户可以在其中选择图像中的一些像素坐标。我想用matplotlib来做,因此我遇到了这个堆栈溢出问题: Store mouse click event coordinates with matplotlib

给出将点击的坐标存储在全局列表中的解决方案

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)

该解决方案工作得很好,但是我想摆脱那些全局变量,并且我认为获取点击坐标对于 asyncio 来说是一项完美的工作。

我天真地尝试了以下代码,但显然不起作用(但它显示了我希望实现的总体思路):

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()

如何结合使用 matplotlib 和 asyncio 来响应或收集事件?

【问题讨论】:

【参考方案1】:

我找到了同时使用 asyncio 和 matplotlib 的解决方案。

基本上,主要问题似乎是 matplotlib 的 gui 必须在主线程中运行,并且运行 plot gui 会阻塞主线程中的所有其他内容。我对此的解决方案是在另一个线程中运行 asyncio 循环并使用 loop.call_soon_thread_safequeue.put_no_wait

不确定这是否是一个好的解决方案,但至少到目前为止它似乎有效。

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()

【讨论】:

以上是关于使用 asyncio 对 matplotlib 中的鼠标单击事件做出反应的主要内容,如果未能解决你的问题,请参考以下文章

asyncio

Python协程之asyncio

asyncio

asyncio的简单了解

Python asyncio 模块

六十四 asyncio