如何在 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 图?的主要内容,如果未能解决你的问题,请参考以下文章

如何保存当前python会话中的所有变量?

如何使用 pandas read_pickle 从 qrc 资源文件中读取包含 pandas 数据框的 pickle 文件?

如何使用 pickle 保存字典?

如何使用 PyQt5 保存任何文件?

PyQt5 - 无法从流中播放视频

如何使用On_Press更改动态创建的小部件的BG颜色并使用Pickle保存? (与Kivy的Python)