matplotlib的show()函数不能重复使用
我遇到了一个奇怪的问题,跟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 个回答
我之前也遇到过同样的问题,就是show()函数只在第一次调用时有效。你现在还在用0.99.3版本吗?我最近解决了这个问题,如果你还想改变show()的行为,可以试试这个方法:
我在matplotlib下载网站的“新特性”部分看到有一段标题为支持多次调用show()的内容。
一个长期以来的请求是支持多次调用show()。这一直很困难,因为在不同的操作系统、用户界面工具包和版本之间保持一致的行为很难。Eric Firing在统一不同后端的show()行为方面做了很多工作,期望的效果是让show()能够显示所有新创建的图形,并在它们关闭之前阻止其他代码的执行。重复调用show()应该能显示自上次调用以来新创建的图形。Eric对他能接触到的用户界面工具包、版本和平台进行了大量测试,但不可能测试所有的情况,所以如果你遇到问题,请向邮件列表和错误跟踪系统反馈。
这段内容是关于1.0.1版本的“新特性”,而在我写这段话时,synaptic中的版本仍然是0.99.3。我能够从源代码下载并构建1.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'
我找到了一些新信息,是通过谷歌搜索得来的。
这段话来自matplotlib的作者。具体内容可以在这个链接找到:http://old.nabble.com/calling-show%28%29-twice-in-a-row-td24276907.html
嗨,Ondrej,
我不太确定哪里有好的解释,但我可以给你一些提示。这个“show”函数应该在每个程序中只用一次。也就是说,“show”应该是你脚本中的最后一行。如果你想要交互式绘图,可以考虑使用交互模式(pyplot.ion和pyplot.ioff),就像下面的例子一样。
另外,对于动态绘图,所有的动画示例可能会很有用。
也许你还想看看这个链接:http://matplotlib.sourceforge.net/users/shell.html。
祝好,Matthias
所以看起来这可能是一个未记录的“特性”(或者说是个bug?)。
补充:这是他的代码块:
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()你可以实现你想要的效果。我还没有测试这段代码,想知道它的表现如何。
在你的代码开头,使用 plt.ion()
来开启互动模式。