Flavio Mosquito
Flavio Mosquito

Reputation: 15

Plots created within a thread do not appear

I am new in Python and I would like to ask you a help to solve a problem in my code. I used QtDesigner to create my UI and then I converted using pyuic4 to a test.py file. I also have the mplwidget.py and the modules.py files as shown bellow:

test.py

from PyQt4 import QtCore, QtGui
from numpy import *
import threading, thread
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

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(617, 588)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
        self.tab = QtGui.QWidget()
        self.tab.setObjectName(_fromUtf8("tab"))
        self.verticalLayout_2 = QtGui.QVBoxLayout(self.tab)
        self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
        self.pushButton = QtGui.QPushButton(self.tab)
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.verticalLayout_2.addWidget(self.pushButton)
        self.pushButton_2 = QtGui.QPushButton(self.tab)
        self.pushButton_2.setObjectName(_fromUtf8("pushButton_2"))
        self.verticalLayout_2.addWidget(self.pushButton_2)
        self.tabWidget.addTab(self.tab, _fromUtf8(""))
        self.tab_2 = QtGui.QWidget()
        self.tab_2.setObjectName(_fromUtf8("tab_2"))
        self.verticalLayout_3 = QtGui.QVBoxLayout(self.tab_2)
        self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
        self.widget = MplWidget(self.tab_2)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
        self.widget.setSizePolicy(sizePolicy)
        self.widget.setObjectName(_fromUtf8("widget"))
        self.verticalLayout_3.addWidget(self.widget)
        self.tabWidget.addTab(self.tab_2, _fromUtf8(""))
        self.verticalLayout.addWidget(self.tabWidget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.pushButton.setText(_translate("MainWindow", "PushButton I", None))
        self.pushButton_2.setText(_translate("MainWindow", "PushButton II", None))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1", None))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Tab 2", None))


        QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("pressed()")), self.ploting1)
        QtCore.QObject.connect(self.pushButton_2, QtCore.SIGNAL(_fromUtf8("pressed()")), self.calling)


    def calling(self):
        from modules import MyForm
        arg="yes",
        tmain=threading.Thread(target=MyForm().ploting2,args=(arg))
        tmain.start()


    def ploting1(self,plot="yes"):
        if plot != "no":
            t = logspace(1E-6,1,132,base=1E-6)
            y1 = exp(-t/1E-3)
            self.widget.canvas.fig.clear()
            self.widget.canvas.ax = self.widget.canvas.fig.add_axes([0.15, 0.15, 0.7, 0.7])
            self.widget.canvas.ax.set_xlabel('t')
            self.widget.canvas.ax.set_ylabel('corr')
            label1='y1'
            self.widget.canvas.ax.semilogx(t,y1,'ro-',label=label1)
            self.widget.canvas.ax.legend(loc='lower left')
            self.widget.canvas.ax.tick_params(axis='y', colors='red')
            self.widget.canvas.draw()
            self.tabWidget.setCurrentIndex(1)

from mplwidget import MplWidget

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

modules.py

from numpy import *
from test import Ui_MainWindow
from mplwidget import MplWidget

class MyForm(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyForm, self).__init__(parent)
        self.setupUi(self)

    def ploting2(self,plot="yes"):
        if plot != "no":
            t = logspace(1E-6,1,132,base=1E-6)
            y1 = exp(-t/1E-3)
            self.widget.canvas.fig.clear()
            self.widget.canvas.ax = self.widget.canvas.fig.add_axes([0.15, 0.15, 0.7, 0.7])
            self.widget.canvas.ax.set_xlabel('t')
            self.widget.canvas.ax.set_ylabel('corr')
            label1='y1'
            self.widget.canvas.ax.semilogx(t,y1,'ro-',label=label1)
            self.widget.canvas.ax.legend(loc='lower left')
            self.widget.canvas.ax.tick_params(axis='y', colors='red')
            self.widget.canvas.draw()
            self.tabWidget.setCurrentIndex(1)

mplwidget.py

from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

class MplCanvas(FigureCanvas):
    """Class to represent the FigureCanvas widget"""

    def __init__(self, parent=None, name=None, width=5, height=4, dpi=100, bgcolor=None):
    self.parent = parent
    if self.parent:
        bgc = parent.backgroundBrush().color()
        bgcolor = float(bgc.red())/255.0, float(bgc.green())/255.0, float(bgc.blue())/255.0

        self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor=bgcolor, edgecolor=bgcolor)
        self.ax = self.fig.add_subplot(111)
        self.ax.hold(False)

        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self,
                                   QtGui.QSizePolicy.Expanding,
                                   QtGui.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)


class MplWidget(QtGui.QWidget):
    """Widget defined in Qt Designer"""
    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)

Well, as you can see in the main window I have two buttons. The first is connected to the function (ploting1) defined in test.py and plots a graphic in tab2. The problem arises in button 2 where I call, using a Thread, the function (ploting2) defined in modules.py. I am using Thread because I need to perform some calculations at same time plotting the curve. Both functions are the same, but the one called from modules.py doesn’t show the plot. Any hint on what I am doing wrong?

Thanks in advance for the help.

PS: I get no error message, everything works fine, apart from button 2 that doesn’t show the plot.

Upvotes: 0

Views: 172

Answers (1)

three_pineapples
three_pineapples

Reputation: 11849

You have several issues here. The first two centre around this line:

tmain=threading.Thread(target=MyForm().ploting2,args=(arg))
  1. tmain is a local variable. When the calling method finishes, the thread will be garbage collected. So it won't run
  2. You instantiate MyForm() in an argument to the theading.Thread call, but don't save a reference. to the object created. As such, the GUI contained within your object will be garbage collected, and will cease to exist.

So you could fix these problems by storing the variables in the parent object (E.g. self.tmain = ..., etc.) but this is pointless as you will see from the next problem

  1. You should never access Qt widgets from a thread. Doing so is expressly forbidden and will cause horrible horrible crashes at random in your program.

I strongly suggest you reconsider your use of threads. If you must use threads, do not call any Qt methods from the thread (this includes matplotlib plotting calls).

Update in response to comment

Since I am performing a very long calculation I thought the best way was using Threading. But I would also like to plot the parameters in real time obtained during calculation. Do you have any idea about the best way to do that?

You can still do your long running calculation in a thread, so that the rest of your GUI stays responsive. You will then need to communicate the results of the calculation to the main thread, where you will call the actual plotting functions. You can pass data to the main thread in a number of ways. Either by emitting a Qt signal (requires the use of QThreads), explicitly posting a Qt event to the main thread (use QCoreApplication.postEvent() or a putting data in a Python Queue.

Some relevant links (not necessarily giving plotting examples, but should give the general idea):

Upvotes: 1

Related Questions