2016-10-20 3 views
1

Ich entwerfe eine GUI, die mehrere zu aktivierende QThreads erzeugt. Jeder Thread erstellt eine Excel-Arbeitsmappe mit Pandas Excelwriter und erstellt eine Heatmap mit Seaborn und speichert diese Heatmap (für spätere Verwendung durch den Benutzer für was auch immer) und speichert sie dann in der Excel-Arbeitsmappe.Warum hat Pyplot Probleme mit PyQt4's QThread?

Ich glaube, der Fehler ist, dass pyplot nicht in eine eigene Instanz für den Thread, der erstellt wird .. noch eine Ressource, die alle Threads zeigen auf .. Wenn ich nur einen Thread ausführen, gibt es kein Problem .. Zwei oder mehr Threads..in diesem Beispiel 4 gibt es interne Pyplot-Fehler, die auf eine Änderung der Dictionary-Größe hinweisen.

Das Problem, das ich habe, ist, wenn pyplot ins Spiel gebracht wird. Muss ich etwas speziell für pyplot tun, um das richtige Backend für Matplotlib zu bekommen? Ich dachte, die Änderungen, die ich für Matplotlib vorgenommen habe, sind in pyplot enthalten.

--- --- main.py

import sys 
from MAIN_GUI import * 
from PyQt4 import QtGui, QtCore 
from excel_dummy import * 


df1 = pd.DataFrame(np.array([[1,22222,33333],[2,44444,55555],[3,44444,22222],[4,55555,33333]]),columns=['hour','input','out']) 
df2 = pd.DataFrame(np.array([[1,22233,33344],[2,44455,55566],[3,44455,22233],[4,55566,33344]]),columns=['hour','input','out']) 
df3 = pd.DataFrame(np.array([[1,23456,34567],[2,98765,45674],[3,44444,22222],[4,44455,34443]]),columns=['hour','input','out']) 
df4 = pd.DataFrame(np.array([[1,24442,33443],[2,44444,54455],[3,45544,24442],[4,54455,33443]]),columns=['hour','input','out']) 

df_list = [df1,df2,df3,df4] 

if __name__=="__main__": 
    app = QtGui.QApplication(sys.argv) 


class MAIN_GUI(QtGui.QMainWindow): 
    def __init__(self): 
     super(MAIN_GUI, self).__init__() 
     self.uiM = Ui_MainWindow() 
     self.uiM.setupUi(self) 
     self.connect(self.uiM.updateALL_Button,QtCore.SIGNAL('clicked()'),self.newThread) 

    def newThread(self): 

     count = 0 
     for df in df_list: 
      count += 1 
      Excelify = excelify(df,count) 
      self.connect(Excelify,QtCore.SIGNAL('donethread(QString)'),(self.done)) 
      Excelify.start() 


    def done(self): 
     print('done') 


main_gui = MAIN_GUI() 
main_gui.show() 
main_gui.raise_() 
sys.exit(app.exec_()) 

--- --- excel_dummy.py

import pandas as pd 

import numpy as np 
from PyQt4 import QtCore, QtGui 
from PyQt4.QtCore import QThread 
import time 
import matplotlib as mpl 
mpl.use('Agg') 
from matplotlib.backends.backend_agg import FigureCanvas 
from matplotlib.figure import Figure 
import matplotlib.pyplot as plt 
import seaborn.matrix as sm 

class excelify(QThread): 
    def __init__(self,df,count): 
     QThread.__init__(self) 
     self.df = df 
     self.count = count 

    def run(self): 

     heatit = self.heatmap() 

     self.emit(QtCore.SIGNAL('donethread(QString)'),'') 

    def heatmap(self): 

     dfu = pd.DataFrame(self.df.groupby([self.df.input,self.df.hour]).size()) 
     dfu.reset_index(inplace=True) 
     dfu.rename(columns={'0':'Count'}) 
     dfu.columns=['input','hour','Count'] 
     dfu_2 = dfu.copy() 

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

     sm.heatmap(df_heatmap,ax=ax,square=True,annot=False,mask=mask) 

     plt.ylabel('ID') 
     plt.xlabel('Hour') 
     plt.title('heatmap for df' + str(self.count)) 
     plt.savefig(path + '/' + 'heat' + str(self.count) + '.png') 
     plt.close() 

--- --- MAIN_GUI.py

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)) 
+0

* "Das Problem, das ich habe, ist, wenn pyplot ins Spiel gebracht wird" * ist keine ausreichende Beschreibung des Problems. Es sagt uns, wann das Problem auftritt, aber nicht, was das Problem ist. – ImportanceOfBeingErnest

+0

@ImportanceOfBeingErnest Ich habe weitere Beschreibung hinzugefügt. Ich dachte, dass meine Frage erklärte, dass ich nicht genau weiß, was das Problem ist ... Wenn Sie bitte das Programm ausführen können, würden Sie das Problem auftreten sehen. Ich benutze Python2.7. Ich hoffe du kannst helfen. – Daniel

+0

Vielleicht möchten Sie auf [Wie man eine gute Frage stellt] (http://stackoverflow.com/help/how-to-ask) und auch beim Erstellen eines [Minimal Working Beispiel] (http://stackoverflow.com) lesen/hilfe/mcve). Ihr Code ist weder minimal noch funktioniert er (es gibt Syntaxfehler und undefinierte Methoden). Legen Sie alles in eine Datei und entfernen Sie das Zeug, das nicht benötigt wird, um das Problem zu reproduzieren (z. B. ExcelWriter wird wahrscheinlich überhaupt nicht benötigt). – ImportanceOfBeingErnest

Antwort

1

Während der Code in der Frage immer noch nicht wirklich eine minimal example (einige undefinierte Variable) ist es viel klarer, wo das Problem liegt.

Erstens, ein Problem könnte sein, dass die MAIN_GUI Klasse einen Verweis auf den Thread verliert, so dass es Müll gesammelt wird, bevor es fertig ist. Man kann dies verhindern, indem man alle Threads in eine Liste stellt.

Zweiter [MAIN_GUI Code siehe unten], Sie nicht direkt auf einmal auf verschiedene Figuren zu bedienen verwenden pyplot können. Oder mit anderen Worten, wie soll pyplot wissen, in welcher Figur der von plt.ylabel('ID') gesetzte Ylabel gesetzt wird, wenn mehrere gleichzeitig existieren?
Der Weg, um dies zu lösen, ist es, verschiedene Zahlen zu erstellen und arbeiten nur innerhalb dieser Zahlen mit dem object oriented Ansatz. [Siehe excelify-Code unten]

Hier ist der relevante Teil des Codes, wo ich auch das Signal geändert habe, um die Plot-Nummer für einfacheres Debugging zurückzugeben.

MAIN_GUI:

class MAIN_GUI(QtGui.QMainWindow): 
    def __init__(self): 
     super(MAIN_GUI, self).__init__() 
     self.uiM = Ui_MainWindow() 
     self.uiM.setupUi(self) 
     self.connect(self.uiM.updateALL_Button,QtCore.SIGNAL('clicked()'),self.newThread) 
     self.threats=[] 

    def newThread(self): 
     count = 0 
     for df in df_list: 
      count += 1 
      Excelify = excelify(df,count) 
      self.connect(Excelify,QtCore.SIGNAL('donethread(int)'),(self.done)) 
      # appending all threats to a class attribute, 
      # such that they will persist and not garbage collected 
      self.threats.append(Excelify) 
      Excelify.start() 

    def done(self, val=None): 
     print('done with {nr}'.format(nr=val)) 

excelify:

class excelify(QThread): 
    def __init__(self,df,count): 
     QThread.__init__(self) 
     self.df = df 
     self.count = count 

    def run(self): 
     heatit = self.heatmap() 
     self.emit(QtCore.SIGNAL('donethread(int)'),self.count) 

    def heatmap(self): 
     print ("{nr} started".format(nr=self.count)) 
     dfu = pd.DataFrame(self.df.groupby([self.df.input,self.df.hour]).size()) 
     dfu.reset_index(inplace=True) 
     dfu.rename(columns={'0':'Count'}) 
     dfu.columns=['input','hour','Count'] 
     dfu_2 = dfu.copy() 
     mask=0 

     # create a figure and only work within this figure 
     # no plt.something inside the threat 
     fig = Figure() 
     ax = fig.add_subplot(1,1,1) 
     fig.set_canvas(FigureCanvas(fig)) 
     df_heatmap = dfu_2.pivot('input','hour','Count').fillna(0) 

     sm.heatmap(df_heatmap,ax=ax,square=True,annot=False,mask=mask) 

     ax.set_ylabel('ID') 
     ax.set_xlabel('Hour') 
     ax.set_title('heatmap for df' + str(self.count)) 
     fig.savefig('heat' + str(self.count) + '.png') 
     fig.clear() 
     del fig