matplotlib show()无法重复使用

20

我有一个关于matplotlib的奇怪问题。如果我运行这个程序,我能够打开并关闭同一张图形多次。

import numpy
from pylab import figure, show


X = numpy.random.rand(100, 1000)
xs = numpy.mean(X, axis=1)
ys = numpy.std(X, axis=1)

fig = figure()
ax = fig.add_subplot(111)
ax.set_title('click on point to plot time series')
line, = ax.plot(xs, ys, 'o', picker=5)  # 5 points tolerance


def onpick(event):

    figi = figure()
    ax = figi.add_subplot(111)
    ax.plot([1,2,3,4])        
    figi.show()

fig.canvas.mpl_connect('pick_event', onpick)

show()

相反地,如果我将onpick函数的相同代码用于我的自定义小部件中,它仅在第一次打开图形时打开,而在其他事件中,它会进入函数但不显示图形:

from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
import time

STEP = 0.000152 

class MplCanvas(FigureCanvas):

    def __init__(self):

        # initialization of the canvas
        FigureCanvas.__init__(self, Figure())

        self.queue = []
        self.I_data = np.array([])
        self.T_data = np.array([])

        self.LvsT = self.figure.add_subplot(111)
        self.LvsT.set_xlabel('Time, s')
        self.LvsT.set_ylabel('PMT Voltage, V')
        self.LvsT.set_title("Light vs Time")
        self.LvsT.grid(True)

        self.old_size = self.LvsT.bbox.width, self.LvsT.bbox.height
        self.LvsT_background = self.copy_from_bbox(self.LvsT.bbox)

        self.LvsT_plot, = self.LvsT.plot(self.T_data,self.I_data)
        #self.LvsT_plot2, = self.LvsT.plot(self.T_data2,self.I_data2) 

        self.mpl_connect('axes_enter_event', self.enter_axes)
        self.mpl_connect('button_press_event', self.onpick)
        self.count = 0
        self.draw()

    def enter_axes(self,event):

        print "dentro"

    def onpick(self,event):
        print "click"
        print 'you pressed', event.canvas

        a = np.arange(10)
        print a
        print self.count

        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot(a)    
        fig.show()



    def Start_Plot(self,q,Vmin,Vmax,ScanRate,Cycles):
        self.queue = q

        self.LvsT.clear()
        self.LvsT.set_xlim(0,abs(Vmin-Vmax)/ScanRate*Cycles)
        self.LvsT.set_ylim(-3, 3)
        self.LvsT.set_autoscale_on(False)
        self.LvsT.clear()
        self.draw()

        self.T_data = np.array([])
        self.I_data = np.array([])

        # call the update method (to speed-up visualization)
        self.timerEvent(None)
        # start timer, trigger event every 1000 millisecs (=1sec)
        self.timerLvsT = self.startTimer(3)

    def timerEvent(self, evt):

        current_size = self.LvsT.bbox.width, self.LvsT.bbox.height
        if self.old_size != current_size:
            self.old_size = current_size
            self.LvsT.clear()
            self.LvsT.grid()
            self.draw()
            self.LvsT_background = self.copy_from_bbox(self.LvsT.bbox)

        self.restore_region(self.LvsT_background, bbox=self.LvsT.bbox)

        result = self.queue.get()

        if result == 'STOP': 
            self.LvsT.draw_artist(self.LvsT_plot)
            self.killTimer(self.timerLvsT)
            print "Plot finito LvsT"

        else:
            # append new data to the datasets
            self.T_data = np.append(self.T_data,result[0:len(result)/2])
            self.I_data = np.append(self.I_data,result[len(result)/2:len(result)])

            self.LvsT_plot.set_data(self.T_data,self.I_data)#L_data
            #self.LvsT_plot2.set_data(self.T_data2,self.I_data2)#L_data

            self.LvsT.draw_artist(self.LvsT_plot)

            self.blit(self.LvsT.bbox)


class LvsT_MplWidget(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)        
        self.canvas = MplCanvas()
        self.vbl = QtGui.QVBoxLayout()
        self.vbl.addWidget(self.canvas)
        self.setLayout(self.vbl)

我需要这个小部件来创建一个动画图,当实验结束并且我单击图表时应该出现一个仅在第一次出现的图形。

你有任何线索吗?

非常感谢。


6个回答

13
在代码开始处,通过plt.ion()启用交互模式。

这绝对是正确而简单的答案! - La Cordillera

12

我有一些新的关于这个问题的信息,是通过谷歌搜索得到的。

这来自matplotlib的作者。来源为http://old.nabble.com/calling-show%28%29-twice-in-a-row-td24276907.html

嗨Ondrej,

我不确定在哪里可以找到一个好的解释,但让我给你一些提示。它旨在每个程序只使用一次show。也就是说,“show”应该是脚本中的最后一行。如果您想要交互式绘图,可以考虑交互式模式(pyplot.ion-ioff),如下面的示例所示。

此外,对于动态绘图,所有动画演示都可能很有用。

也许您还想看看http://matplotlib.sourceforge.net/users/shell.html

最好的问候Matthias

因此,似乎这是一个未记录的“特性”(错误?)。

编辑:以下是他的代码块:

from pylab import *

t = linspace(0.0, pi, 100)
x = cos(t)
y = sin(t)

ion()  # turn on interactive mode
figure(0)
subplot(111, autoscale_on=False, xlim=(-1.2, 1.2), ylim=(-.2, 1.2))

point = plot([x[0]], [y[0]], marker='o', mfc='r', ms=3)

for j in arange(len(t)):
    # reset x/y-data of point
    setp(point[0], data=(x[j], y[j]))
    draw() # redraw current figure

ioff() # turn off interactive mode
show()

也许通过使用draw()函数,你可以得到想要的结果。我还没有测试过这段代码,希望能了解它的行为。


作为我的第一个例子,它在单独的脚本中完美运行,但在我的函数中只能运行一次而不是第二次。问题在于最终的show()只能被调用一次。这太奇怪了,我只需要打开和关闭几次图形,它不应该那么困难。我猜可能是包含到qt小部件中导致了这种行为。顺便说一下,仅使用draw()而不使用show()不会显示任何内容。 - Sangi

2

我曾遇到过使用show()仅在第一次生效的问题。您目前仍在使用0.99.3版本或类似版本吗?最近我解决了我的问题,如果您依然希望改变show()的行为,请尝试以下方法:


我注意到matplotlib下载网站的“更新内容”部分有一个标题为支持对show()的多次调用的段落。

长期以来的一个请求是支持对show()的多次调用。这很困难,因为在操作系统、用户界面工具包和版本之间难以获得一致的行为。Eric Firing在统一后端上做了很多工作,期望的行为是使show()引发所有最新创建的图形并阻塞执行,直到它们关闭。对show()的重复调用应该引发自上次调用以来新创建的图形。Eric已经在他可以访问到的用户界面工具包、版本和平台上进行了大量测试,但不可能测试所有情况,请将问题报告给邮件列表和错误跟踪器。

这是版本1.0.1的“更新内容”,在撰写本文时,synaptic中的版本仍停留在0.99.3。我能够从源代码构建下载和安装v1.0.1。我还需要额外的包来满足依赖关系,它们是libfreetype6-dev tk-dev tk8.5-dev tcl8.5-dev python-gtk2-dev;您的情况可能有所不同。

现在我使用的是matplotlib.__version__ == 1.0.1,以下代码表现出了我期望的功能:

from matplotlib import pyplot as p
from scipy import eye
p.imshow(eye(3))
p.show()
print 'a' 
p.imshow(eye(6))
p.show()
print 'b' 
p.imshow(eye(9))
p.show()
print 'c' 

0
 def onpick(self,event):
        print "click"
        print 'you pressed', event.canvas
        ...
        ax.plot(a)    
        fig.show() # <--- this blocks the entire loop

尝试:

 def onpick(self,event):
        print "click"
        print 'you pressed', event.canvas
        ...
        ax.plot(a)    
        self.draw()
        self.update()

它不起作用。我知道show()会阻塞整个循环,但当我关闭图形时,GUI循环仍然继续工作。问题是当我再次点击图形时,show()不再起作用。 - Sangi
1
你不应该使用.show来显示它。请发布一个可工作的示例,以便我们可以测试它。 - fabrizioM
不幸的是,我无法发布整个项目,因为它太大了,有很多小部件在单独的文件中。我也尝试使用draw(),但它没有显示任何内容。此外,为什么我在顶部发布的示例可以使用多个show()? - Sangi

0
你可以通过以下方式创建一个图形实例:
fig = plt.figure(0)

通过操作这个图形来绘制你的内容。 你可以随时使用fig.show()来显示你的图形。


0

我解决这个问题的方法是永远不要调用close

我相信你可以在PyQt中控制小部件的透明度。你可以尝试使用Qt来控制可见性,而不是使用matplotlib。不过,我相信比我更了解matplotlib的其他人可以给出更好的答案:D


这是一种解决问题的方法,但我更愿意以正确的方式解决问题。不过,如果没有人能帮助我,我会按照你说的去做。 - Sangi
我同意。我也想知道更好的答案。 - Garrett Berg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接