如何在 Pyqt5 环境中使用 Pickle 保存 Matplotlib 图?
Posted
技术标签:
【中文标题】如何在 Pyqt5 环境中使用 Pickle 保存 Matplotlib 图?【英文标题】:How save a Matplotlib figure with Pickle in a Pyqt5 environment? 【发布时间】:2017-01-26 08:09:49 【问题描述】:我遇到了一个问题,我无法摆脱它。
我正在尝试使用 Pickle 包来保存 matplotlib 图形以重新绘制它,如果我想的话。
到目前为止,如果lfpViewer.__Init__()
中的“if”条件为 1,我有下面的代码打开一个 Qt 窗口并在其中绘制一些曲线(我放 0 只是为了检查泡菜加载功能)。
所以我在工具栏中添加了两个按钮,我可以在其中保存当前图形的 .pickle 或从前一个图形加载 .pickle。
import pickle
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import os
import matplotlib
import numpy as np
matplotlib.use('Qt5Agg')
import matplotlib.patches as patches
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.color = self.centralWidget.palette().color(QPalette.Background)
self.setCentralWidget(self.centralWidget)
self.plotview = QGroupBox(" ")
self.layout_plotview = QVBoxLayout()
self.mascenelfp = lfpViewer(self)
self.layout_plotview.addWidget(self.mascenelfp)
self.centralWidget.setLayout(self.layout_plotview)
class lfpViewer(QGraphicsView):
def __init__(self, parent=None):
super(lfpViewer, self).__init__(parent)
self.parent=parent
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.setBackgroundBrush(QBrush(self.parent.color))# self.setBackgroundBrush(QBrush(QColor(200, 200, 200)))
self.figure = plt.figure(facecolor=[self.parent.color.red()/255,self.parent.color.green()/255,self.parent.color.blue()/255]) #Figure()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
self.save_button = QPushButton()
self.save_button.setIcon(QIcon(os.path.join('icons','SaveData.png')))
self.save_button.setToolTip("Save Figure Data")
self.toolbar.addWidget(self.save_button)
self.save_button.clicked.connect(self.saveFigData)
self.load_button = QPushButton()
self.load_button.setIcon(QIcon(os.path.join('icons','LoadData.png')))
self.load_button.setToolTip("Load Figure Data")
self.toolbar.addWidget(self.load_button)
self.load_button.clicked.connect(self.loaddatapickle)
if 0:
t=np.arange(1000)
self.axes_l=self.figure.add_subplot(311)
self.axes_l.plot(t, np.sin(2*3.14*100*t))
self.axes_Y=self.figure.add_subplot(312)
self.axes_Y.plot(t, np.cos(2*3.14*100*t))
self.axes_Yi=self.figure.add_subplot(313)
self.axes_Yi.plot(t, np.tan(2*3.14*100*t))
self.canvas.setGeometry(0, 0, 1600, 500 )
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
def loaddatapickle(self):
fileName = QFileDialog.getOpenFileName(self,'Load Data', '', 'pickle (*.pickle)')
if (fileName[0] == '') :
return
fileName = str(fileName[0])
filehandler = open(fileName , 'rb')
self.figure = pickle.load(filehandler)
filehandler.close()
self.canvas.draw()
self.parent.parent.processEvents()
return
def saveFigData(self):
fileName = QFileDialog.getSaveFileName(self,'Save Figure Data', '', 'pickle (*.pickle)')
if (fileName[0] == '') :
return
fileName = str(fileName[0])
file_pi = open(fileName, 'wb')
pickle.dump(self.figure, file_pi, -1)
file_pi.close()
return
def main():
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.showMaximized()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
保存似乎有效(好吧,至少,我有一个文件),但加载按钮绝对没有任何作用! 即使我有一个 .pickle 文件,我也不知道 pickle 是否保存了正确的二进制二进制文件,因为当我在调试模式下加载 pickle 文件时,我会得到很多红色的东西。 查找下图:
如果我在没有 PyQt5 的情况下编写代码,它可以正常工作,例如,使用以下代码:
import pickle
import matplotlib
import numpy as np
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
def loaddatapickle():
filehandler = open('test.pickle' , 'rb')
figure = pickle.load(filehandler )
filehandler.close()
return figure
def saveFigData(figure):
file_pi = open('test.pickle', 'wb')
pickle.dump(figure , file_pi, 1)
file_pi.close()
return
figure = plt.figure( ) #Figure()
Save= 0
if Save==1:
t=np.arange(1000)
axes_l=figure.add_subplot(311)
axes_l.plot(t, np.sin(2*3.14*100*t))
axes_Y=figure.add_subplot(312)
axes_Y.plot(t, np.cos(2*3.14*100*t))
axes_Yi=figure.add_subplot(313)
axes_Yi.plot(t, np.tan(2*3.14*100*t))
saveFigData(figure)
else:
figure=loaddatapickle()
plt.show()
如果有人知道这里发生了什么,请告诉我! 祝你有美好的一天。
【问题讨论】:
这个问题可能是因为pickle保存了一个matplotlib.figure.Figure而不是一个matplotlib.pylab.Figure。所以当我从泡菜文件中重新加载 self.figure 时它无法处理? type(plt.figure()) 也返回 matplotlib.figure.Figure,它们都是一样的。 【参考方案1】:我不确定这是否能解决您面临的问题,但让我们试一试。我需要提一下,我没有可用的 QT5,我正在使用 python 2.7 和 matplotlib 2.0.0。但这里的解决方案可能适用于一般情况。
将程序适配到pyqt4并运行时,我发现酸洗工作正常。而且 unpickling 没有抛出任何错误,所以我怀疑显示 unpickled 的图形可能存在问题。
事实证明,允许加载图形不仅是将图形加载到self.figure
,而且用这个未腌制的图形重新创建画布并将其新添加到布局中:
def loaddatapickle(self):
#needed to change some stuff here, since in Qt4 the dialog directly returns a string
fileName = QFileDialog.getOpenFileName(self,'Load Data', '' )
if (fileName == '') :
return
fileName = str(fileName)
filehandler = open(fileName , 'rb')
self.figure = pickle.load(filehandler)
filehandler.close()
# remove the old canvas
self.layout().removeWidget(self.canvas)
# create a new canvas
self.canvas = FigureCanvas(self.figure)
# add the new canvas at the position of the old one
self.layout().addWidget(self.canvas, 1)
self.canvas.draw()
self.parent.parent.processEvents()
return
当然最好直接用新的图形更新画布,但我还没有找到任何方法。
【讨论】:
以上是关于如何在 Pyqt5 环境中使用 Pickle 保存 Matplotlib 图?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 pandas read_pickle 从 qrc 资源文件中读取包含 pandas 数据框的 pickle 文件?