2013-12-18 74 views
1

我試圖修改我在stackoverflow論壇上找到的一些代碼(How can I plot the same figure standalone and in a subplot in Matplotlib?第一個答案)。點擊沒有事件連接

如果單擊該子圖(僅顯示畫布上單擊的子圖),並在再次單擊時放大(顯示畫布上的所有子圖),它基本上會放大子圖。我嘗試修改代碼以通過多種方式將其調整到我的程序,但是我仍然遇到同樣的問題。帶有子圖的圖形畫布已正確創建,並且輸入了縮放子圖課程,但它似乎無法與我的點擊事件(它不會輸入'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() 

回答

2

發生了什麼事有點經典的疑難雜症與matplotlib的「弱」到回調等引用您的類實例正在垃圾收集之前,情節顯示。


因爲pylab狀態機必須是「長期」,它需要保持引用很多不同的東西,matplotlib使用對於大多數用戶提供的對象「弱」引用。這可以讓垃圾收集,即使一個人物仍然可以參考他們。

它在大多數情況下用於很好的理由,但是您可以提出一個論據,即matplotlib不應該對回調使用弱引用。無論如何,你正在經歷的是這個長期以來的功能/錯誤/設計選擇的結果。


最簡單的解決方法就是做:

x = ZoomingSubplots(fig) 

x參考將保留類的實例,從收集到x垃圾超出範圍(因爲x是一個全局變量這個特殊情況,直到程序結束纔會發生)。


上述解決方案的工作很好,但我不喜歡有未使用的變量掛(如pylint的事物等,會抱怨也一樣)。

因此,我會經常爲這樣的事情添加show方法,它調用plt.show()並進入gui mainloop。例如:

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主循環(即plt.show())稱爲類實例的方法,因此,類的實例不能被垃圾之前show()端收集。

哪個「更好」純粹是個人風格的問題。但是,我覺得第一個例子不太適合未來發展,因爲其他人可能會出現,並且「x未使用,我們將其刪除」,並且無意中重新介紹了該問題。