【发布时间】:2018-11-07 20:33:01
【问题描述】:
Matplotlib 中是否有函数或方法可以告诉你 哪些事件已连接,也许该侦听器正在调用什么代码?我都看了一遍,一点都不开心。我正在寻找一种通用的解决方案,而不是特定于后端的解决方案,但我会尽我所能。
【问题讨论】:
标签: python-3.x matplotlib events listener matplotlib-widget
Matplotlib 中是否有函数或方法可以告诉你 哪些事件已连接,也许该侦听器正在调用什么代码?我都看了一遍,一点都不开心。我正在寻找一种通用的解决方案,而不是特定于后端的解决方案,但我会尽我所能。
【问题讨论】:
标签: python-3.x matplotlib events listener matplotlib-widget
在这些情况下,不要害怕 matplotlib 代码库——它在大多数情况下都相当 Python 且清晰易读(可能到处都有一两个骨架)。
我将向您介绍我将采取的步骤,以了解正在发生的事情以及您可以访问的内容。
首先,从 GitHub 上的 matplotlib 标记版本开始 - 它将使所有链接保持一致(并且不会随着代码的移动而腐烂)。 https://github.com/matplotlib/matplotlib/tree/v3.0.2
我们的入口点是我们通过mpl_connect 方法附加事件。在 matplotlib 代码库中快速搜索(使用"def mpl_connect",包括引号)会出现https://github.com/matplotlib/matplotlib/blob/v3.0.2/lib/matplotlib/backend_bases.py#L2134-L2180。
self.callbacks.connect(s, func)
所以现在我们需要弄清楚self.callbacks 在这个对象上实际上是什么。我做了一个ctrl+f 在这个文件中找到文本self.callbacks =。
https://github.com/matplotlib/matplotlib/blob/v3.0.2/lib/matplotlib/backend_bases.py#L1621
# a dictionary from event name to a dictionary that maps cid->func
self.callbacks = cbook.CallbackRegistry()
我们现在正在取得进展,并且每次都更深入一点 :)
在逻辑上遵循导入,我们现在需要找出matplotlib.cbook.CallbackRegistry 的样子。 https://github.com/matplotlib/matplotlib/blob/v3.0.2/lib/matplotlib/cbook/init.py#L88.
具体来说,我们调用了CallbackRegistry.connect 方法:https://github.com/matplotlib/matplotlib/blob/v3.0.2/lib/matplotlib/cbook/init.py#L162-L177。撰写本文时的实施情况:
def connect(self, s, func):
"""Register *func* to be called when signal *s* is generated.
"""
self._func_cid_map.setdefault(s, {})
try:
proxy = WeakMethod(func, self._remove_proxy)
except TypeError:
proxy = _StrongRef(func)
if proxy in self._func_cid_map[s]:
return self._func_cid_map[s][proxy]
cid = next(self._cid_gen)
self._func_cid_map[s][proxy] = cid
self.callbacks.setdefault(s, {})
self.callbacks[s][cid] = proxy
return cid
我没有尝试阅读所有这些内容,而是寻找公共数据结构(那些不以_ 开头的数据结构)以查看是否有任何我可以查询的内容。它开始看起来像CallbackRegistry.callbacks 是一个将事件名称映射到包含这些事件函数的某种形式的集合的字典。
确实,这点试一试就支持了:
In [6]: fig.canvas.callbacks.callbacks
Out[6]:
{'button_press_event': {0: <weakref at 0x117bcff98; to 'FigureCanvasTkAgg' at 0x117c0f860>,
4: <matplotlib.cbook._StrongRef at 0x117c0f550>,
5: <matplotlib.cbook._StrongRef at 0x119686dd8>},
'scroll_event': {1: <weakref at 0x117be4048; to 'FigureCanvasTkAgg' at 0x117c0f860>},
'key_press_event': {2: <weakref at 0x117be43c8; to 'FigureManagerTk' at 0x117c0fe10>},
'motion_notify_event': {3: <weakref at 0x117be4438; to 'NavigationToolbar2Tk' at 0x117c0fe48>}}
有趣的是,在这种特殊情况下,我个人只添加了一个事件处理程序 (button_press_event),但显然我有更多的事件。这里看到的实际上也是在您的后端运行的所有事件。有没有想过如何禁用其中的一些(如键盘快捷键)?它只是一个事件字典,没有什么可以阻止它们:
# Delete / remove the keyboard press event handlers.
fig.canvas.callbacks.callbacks.pop('key_press_event')
如果你想获得对底层函数的引用,一点点内省建议你可以这样做:
In [37]: for cid, func_ref in fig.canvas.callbacks.callbacks['button_press_event'].items():
...: func = func_ref()
...: print(cid, func)
Out[37]: 5 <function onclick at 0x114d7e620>
【讨论】: