Pandas 如何使用matplotlib和pyplot正确实现QThread

Pandas 如何使用matplotlib和pyplot正确实现QThread,pandas,matplotlib,pyqt4,seaborn,qthread,Pandas,Matplotlib,Pyqt4,Seaborn,Qthread,我知道还有一两个问题是相关的,但不完全是我需要的。我正在构建这个gui,它通过单击一个按钮来激活一个模块。这个通过按下按钮激活的python模块从多个pandas数据帧生成热图并保存这些图像,然后使用pandas ExcelWriter将这些图像保存到xlsx中 我曾尝试实现QThread,正如其他stackoverflow示例试图解释类似的问题一样,但我继续遇到以下错误:“在GUI线程之外使用pixmaps是不安全的”。我知道从技术上讲,我不是在主gui线程内创建热图,但我认为使用QThrea

我知道还有一两个问题是相关的,但不完全是我需要的。我正在构建这个gui,它通过单击一个按钮来激活一个模块。这个通过按下按钮激活的python模块从多个pandas数据帧生成热图并保存这些图像,然后使用pandas ExcelWriter将这些图像保存到xlsx中

我曾尝试实现QThread,正如其他stackoverflow示例试图解释类似的问题一样,但我继续遇到以下错误:
“在GUI线程之外使用pixmaps是不安全的”
。我知道从技术上讲,我不是在主gui线程内创建热图,但我认为使用QThread,我仍然在“a”gui线程内。热图所基于的这些数据帧有时会很大,我在某种程度上掌握了在创建热图时向主gui线程发送信号的概念,并且在主gui类中具有热图功能……但我担心这会在以后传递如此多的数据时带来麻烦……这更像流水线比线程更重要。我只希望这个工作线程创建这些图像并保存它们,然后将这些保存的文件保存到xlsx中,而不中断主gui

(注意:这是一个简化版本,在实际程序中,将几乎同时创建多个线程,每个线程内将创建多个热图)

---main.py---

---excel_dummy.py---

---MAIN_GUI.py---


即使您明确使用
Agg
后端生成您的体形,看起来
Seaborn
仍在使用系统上的默认后端,这很可能是一个交互式后端
Qt4Agg
。我们希望Seaborn使用
非交互式后端
,以避免任何错误(有关后端的更多详细信息,请参阅)。为此,请在导入中告诉
Matplotlib
使用
Agg
后端,并在Matplotlib之后导入Seaborn

您还需要将图形保存为png,因为
Agg
后端不支持jpg。除非您有使用jpg的特定原因,否则png通常是图形的更好格式

最后,您可以使用内存缓冲区,而不是在将图像保存到Excel工作簿之前将其保存到临时文件中。我还没有测试过它,但是如果您使用的是大文件,它可能会更快

下面是我编写的一个MWE,其中包括上述要点,并且在Python3.4中没有给出我的系统上的任何错误:

import pandas as pd
import time
from pandas import ExcelWriter
import numpy as np
from PyQt4 import QtCore, QtGui

import matplotlib as mpl
mpl.use('Agg')
from matplotlib.backends.backend_agg import FigureCanvas
import seaborn.matrix as sm

try:  # Python 2 (not tested)
    from cStringIO import StringIO as BytesIO
except ImportError:  # Python 3
    from io import BytesIO


class MAIN_GUI(QtGui.QWidget):
    def __init__(self):
        super(MAIN_GUI, self).__init__()

        self.worker = Excelify()

        btn = QtGui.QPushButton('Run')
        disp = QtGui.QLabel()

        self.setLayout(QtGui.QGridLayout())
        self.layout().addWidget(btn, 0, 0)
        self.layout().addWidget(disp, 2, 0)
        self.layout().setRowStretch(1, 100)

        btn.clicked.connect(self.worker.start)
        self.worker.figSaved.connect(disp.setText)


class Excelify(QtCore.QThread):

    figSaved = QtCore.pyqtSignal(str)

    def run(self):
        self.figSaved.emit('Saving figure to Workbook.')    
        t1 = time.clock()

        image_data = self.heatmap()
        with ExcelWriter('final.xlsx', engine='xlsxwriter') as writer:
            wb = writer.book
            ws = wb.add_worksheet()
            ws.insert_image('C3', 'heat.png', {'image_data': image_data})
            writer.save()

        t2 = time.clock()    
        self.figSaved.emit('Done in %f sec.' % (t2-t1))

    def heatmap(self):    
        df = pd.DataFrame(np.array([[1, 22222, 33333], [2, 44444, 55555],
                                    [3, 44444, 22222], [4, 55555, 33333]]),
                          columns=['hour', 'in', 'out'])
        dfu = pd.DataFrame(df.groupby([df.out, df.hour]).size())
        dfu.reset_index(inplace=True)
        dfu.rename(columns={'0': 'Count'})
        dfu.columns = ['in', 'hour', 'Count']

        fig = mpl.figure.Figure()
        fig.set_canvas(FigureCanvas(fig))
        ax = fig.add_subplot(111)

        df_heatmap = dfu.pivot('in', 'hour', 'Count').fillna(0)
        sm.heatmap(df_heatmap, ax=ax, square=True, annot=False, mask=0)

        buf= BytesIO()
        fig.savefig(buf, format='png')

        return(buf)


if __name__ == '__main__':
    import sys

    app = QtGui.QApplication(sys.argv)
    w = MAIN_GUI()
    w.show()
    w.setFixedSize(200, 100)
    sys.exit(app.exec_())
import os, pandas as pd
from pandas import ExcelWriter
import numpy as np
import seaborn.matrix as sm

from PyQt4 import QtCore
from PyQt4.QtCore import QThread
from matplotlib.backends.backend_agg import FigureCanvas
from matplotlib.figure import Figure
import time

class excelify(QThread):
    def __init__(self):
        QThread.__init__(self)

    def run(self):
        path = 'home/desktop/produced_files'
        with ExcelWriter(path + '/final.xlsx', engine='xlsxwriter') as writer:
            workbook = writer.book
            worksheet = workbook.add_worksheet()
            heatit = self.heatmap()
            worksheet.insert_image('C3',path + '/' + 'heat.jpg')
            worksheet.write(2, 2, 'just write something')
            writer.save()
        print('file size: %s "%s"' % (os.stat(path).st_size, path))
        time.slee(0.3)
        self.emit(QtCore.SIGNAL('donethread(QString)'),'')

    def heatmap(self):
        df = pd.DataFrame(np.array([[1,22222,33333],[2,44444,55555],[3,44444,22222],[4,55555,33333]]),columns=['hour','in','out'])
        dfu = pd.DataFrame(df.groupby([df.in,df.hour]).size())
        dfu.reset_index(inplace=True)
        dfu.rename(columns={'0':'Count'})
        dfu.columns=['in','hour','Count']
        dfu_2 = dfu.copy()

        mask=0
        fig = Figure()
        ax = fig.add_subplot(1,1,1)
        canvas = FigureCanvas(fig)
        df_heatmap = dfu_2.pivot('in','hour','Count').fillna(0)

        sm.heatmap(df_heatmap,ax=ax,square=True,annot=False,mask=mask)
        fig.savefig(path + '/' + heat.jpg')
from PyQt4 import QtCore,QtGui
try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.unicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(320,201)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.updateALL_Button = QtGui.QPushButton(self.centralwidget)
        self.updateALL_Button.setGeometry(QtCore.QRect(40,110,161,27))
        self.updateALL_Button.setFocusPolicy(QtCore.Qt.NoFocus)
        self.updateALL_Button.setObjectName(_fromUtf8("Options_updateALL_Button"))
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 320, 24))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self,MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.updateALL_Button.setText(_translate("MainWindow", "updateALL", None))
import pandas as pd
import time
from pandas import ExcelWriter
import numpy as np
from PyQt4 import QtCore, QtGui

import matplotlib as mpl
mpl.use('Agg')
from matplotlib.backends.backend_agg import FigureCanvas
import seaborn.matrix as sm

try:  # Python 2 (not tested)
    from cStringIO import StringIO as BytesIO
except ImportError:  # Python 3
    from io import BytesIO


class MAIN_GUI(QtGui.QWidget):
    def __init__(self):
        super(MAIN_GUI, self).__init__()

        self.worker = Excelify()

        btn = QtGui.QPushButton('Run')
        disp = QtGui.QLabel()

        self.setLayout(QtGui.QGridLayout())
        self.layout().addWidget(btn, 0, 0)
        self.layout().addWidget(disp, 2, 0)
        self.layout().setRowStretch(1, 100)

        btn.clicked.connect(self.worker.start)
        self.worker.figSaved.connect(disp.setText)


class Excelify(QtCore.QThread):

    figSaved = QtCore.pyqtSignal(str)

    def run(self):
        self.figSaved.emit('Saving figure to Workbook.')    
        t1 = time.clock()

        image_data = self.heatmap()
        with ExcelWriter('final.xlsx', engine='xlsxwriter') as writer:
            wb = writer.book
            ws = wb.add_worksheet()
            ws.insert_image('C3', 'heat.png', {'image_data': image_data})
            writer.save()

        t2 = time.clock()    
        self.figSaved.emit('Done in %f sec.' % (t2-t1))

    def heatmap(self):    
        df = pd.DataFrame(np.array([[1, 22222, 33333], [2, 44444, 55555],
                                    [3, 44444, 22222], [4, 55555, 33333]]),
                          columns=['hour', 'in', 'out'])
        dfu = pd.DataFrame(df.groupby([df.out, df.hour]).size())
        dfu.reset_index(inplace=True)
        dfu.rename(columns={'0': 'Count'})
        dfu.columns = ['in', 'hour', 'Count']

        fig = mpl.figure.Figure()
        fig.set_canvas(FigureCanvas(fig))
        ax = fig.add_subplot(111)

        df_heatmap = dfu.pivot('in', 'hour', 'Count').fillna(0)
        sm.heatmap(df_heatmap, ax=ax, square=True, annot=False, mask=0)

        buf= BytesIO()
        fig.savefig(buf, format='png')

        return(buf)


if __name__ == '__main__':
    import sys

    app = QtGui.QApplication(sys.argv)
    w = MAIN_GUI()
    w.show()
    w.setFixedSize(200, 100)
    sys.exit(app.exec_())