python - 单击时无事件连接

标签 python matplotlib

我尝试修改在 stackoverflow 论坛上找到的一些代码(How can I plot the same figure standalone and in a subplot in Matplotlib? 第一个答案)。

它的基本作用是,如果单击子图,则放大该子图(仅在 Canvas 上显示单击的子图),并在再次单击时缩小(在 Canvas 上显示所有子图)。我尝试通过多种方式修改代码以将其调整为我的程序,但是我不断遇到同样的问题。带有子图的图形 Canvas 已正确创建,并且输入了 Zooming 子图类,但它似乎与我的单击事件没有连接(它没有输入“on_click”)。

我尝试找出问题所在并尝试了几次修改,但仍然遇到同样的问题。我无法使用我从中检索到的主题中显示的代码,因为它不适合我的程序的其余部分。

import numpy as np
from matplotlib import pyplot as plt

class ZoomingSubplots(object):
    ''' zoom to subplot if subplot is clicked, unzoom when clicked again'''
    def __init__(self, fig):
        print 'class entered'
        self.fig = fig       
        self.fig.canvas.mpl_connect('button_press_event', self.on_click)

    def zoom(self, selected_ax):
        for ax in self.axes.flat:
            ax.set_visible(False)
        self._original_size = selected_ax.get_position()
        selected_ax.set_position([0.125, 0.1, 0.775, 0.8])
        selected_ax.set_visible(True)
        self._zoomed = True

    def unzoom(self, selected_ax):
        selected_ax.set_position(self._original_size)
        for ax in self.axes.flat:
            ax.set_visible(True)
        self._zoomed = False

    def on_click(self, event):
        print 'click event'
        if event.inaxes is None:
            return
        if self._zoomed:
            self.unzoom(event.inaxes)
        else:
            self.zoom(event.inaxes)
        self.fig.canvas.draw()

#make a figure with 9 random imshows

plots = 9         #number of plots
plotrows = 3      #subplot rows
plotcols = 3      #subplot columns

fig = plt.figure()
for i in range(plots):
    arr = np.random.rand(10,10)
    ax = fig.add_subplot(plotrows, plotcols, i+1)
    ax.imshow(arr, interpolation = 'nearest')
    ax.set_title('%s %i' % ('plot', i+1), fontsize = 10)

# connect with zoom class
ZoomingSubplots(fig)

plt.show()

将其调整为更简单的代码,您可以在其中注意到相同的问题:

import numpy as np
from matplotlib import pyplot as plt

class ZoomingSubplots(object):
    def __init__(self, fig):
        print 'class entered'
        self.fig = fig       
        self.fig.canvas.mpl_connect('button_press_event', self.on_click)

    def on_click(self, event):
        print 'click event'

#make a figure with 9 random imshows
fig = plt.figure()
for i in range(9):
    arr = np.random.rand(10,10)
    ax = fig.add_subplot(3, 3, i+1)
    ax.imshow(arr, interpolation = 'nearest')

# connect with zoom class
ZoomingSubplots(fig)

plt.show() 

最佳答案

发生的情况是一个典型的陷阱,涉及 matplotlib 对回调的“弱”引用等。在显示绘图之前,您的类实例将被垃圾收集。

<小时/>

因为 pylab 状态机必须是“长期存在的”,并且需要保留对许多不同事物的引用,所以 matplotlib 对大多数用户提供的对象使用“弱”引用。这允许对事物进行垃圾收集,即使图形可能仍然引用它们。

在大多数情况下,它的使用都有充分的理由,但您可以提出一个论点,即 matplotlib 不应使用回调的弱引用。无论如何,您所遇到的情况是这种长期存在的功能/错误/设计选择的结果。

<小时/>

最简单的解决方案就是这样做:

x = ZoomingSubplots(fig)

x 的引用将阻止类实例被垃圾回收,直到 x 超出范围(并且因为 x 是一个全局变量)在这种特殊情况下,变量在程序结束之前不会发生)。

<小时/>

上面的解决方案工作得很好,但我不喜欢有未使用的变量闲置(像 pylint 等东西也会提示)。

因此,我经常会添加一个 show 方法来调用 plt.show() 并进入 gui 主循环。例如:

import numpy as np
from matplotlib import pyplot as plt

class ZoomingSubplots(object):
    def __init__(self, fig):
        print 'class entered'
        self.fig = fig
        self.fig.canvas.mpl_connect('button_press_event', self.on_click)

    def on_click(self, event):
        print 'click event'

    def show(self):
        plt.show()

#make a figure with 9 random imshows
fig = plt.figure()
for i in range(9):
    arr = np.random.rand(10,10)
    ax = fig.add_subplot(3, 3, i+1)
    ax.imshow(arr, interpolation = 'nearest')

# connect with zoom class
ZoomingSubplots(fig).show()

在这种情况下,gui mainloop(即plt.show())作为类实例的方法被调用,因此,类实例不能在之前被垃圾回收>show() 结束。

哪个“更好”纯粹是个人风格问题。但是,我觉得第一个示例不太适合 future ,因为其他人可能会出现并说“x 未使用,让我们删除它”,并无意中重新引入该问题。

关于python - 单击时无事件连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20655881/

相关文章:

python - 绘制 2 度线性回归的困难

python - 使用 __repr__ 等返回 matplotlib Fig

python - 线上的标记

python - Heroku 上 Django 的 SMTP 身份验证错误

python - python中列表概念的列表

python - 无法在win7上安装Cython

python - 模式对话框卡住整个应用程序

python - 更改 Matplotlib 后端会使 Debug模式崩溃

python - 将数组作为 .jpg 图像上传到 Azure Blob 存储

python - clr.AddReferenceToFile() 在 IronPython 2.7 中失败