[PyQt] Multithreading and plotting

Fabien Lafont lafont.fabien at gmail.com
Tue Jan 15 15:46:27 GMT 2013


Hi David, I'm sorry to answer so late. I was in Holyday.

I've changed the program according to your remarks. It works fine! I have
only one problem. I want to start the new thread when I clik on a
QPushButton but If I just remove

    thread = AThread()
    thread.start()

from

if __name__ == "__main__":
    qApp = QtGui.QApplication([" "])
    aw = ApplicationWindow()
    aw.showMaximized()
    thread = AThread()
    thread.start()
    sys.exit(qApp.exec_())


and I create a single button :

def demarrer(self):
      thread = AThread()
      thread.start()


The program crashes and "say":    QThread: Destroyed while thread is still
running


Do you have any idea?

Thanks again!

Fabien



2012/12/21 David Hoese <dhoese at gmail.com>

>  Hey Fabien,
>
> See comments below and the bottom for my final changes. Please CC me in
> replies.
>
> On 12/21/12 5:06 AM, lafont.fabien at gmail.com wrote:
>
> Hello everyone,
>
> I'm trying to plot live datas using matplotlib and PyQt. I need a
> multithreaded program beacause I use time.sleep and it freeze completely
> the app during that moment. I've tried that but it crash immediatly:
>
>
>  Do you get a seg. fault or some other error? I'm pretty sure I've helped
> you with this type of Qt application before, but I'll see what I can clear
> up.
>
> from PyQt4 import QtCore, QtGui
>
> import time
>
> import sys
>
> from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as
> FigureCanvas
>
> from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as
> NavigationToolbar
>
> from matplotlib.figure import Figure
>
> # Subclassing QThread
>
> # http://doc.qt.nokia.com/latest/qthread.html
>
> class Graph(FigureCanvas):
>
> def __init__(self,parent):
>
> self.fig = Figure()
> self.ax = self.fig.add_subplot(111)
>
> FigureCanvas.__init__(self, self.fig)
>
> self.R1 = []
>
> self.l_R1, = self.ax.plot([], self.R1,"-o")
>
> self.fig.canvas.draw()
>
> FigureCanvas.updateGeometry(self)
>
> class ApplicationWindow(QtGui.QMainWindow):
>
> """Example main window"""
>
> def __init__(self):
>
> QtGui.QMainWindow.__init__(self)
>
> self.main_widget = QtGui.QWidget(self)
>
> vbl = QtGui.QGridLayout(self.main_widget)
>
> qmc = Graph(self.main_widget)
>
> vbl.addWidget(qmc,0,0,1,11)
>
> self.setCentralWidget(self.main_widget)
>
>  First, a small thing, I'm not sure if this matters but I've always
> created layouts by doing:
>
> vbl = QtGui.QGridLayout()
> # addWidget
> self.main_widget.setLayout(vbl)
>
> I got this pattern from the documentation:
>
> http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qwidget.html#setLayout
>
>
> class AThread(QtCore.QThread):
>
> def run(self):
>
> count = 0
>
> while count < 5:
>
> time.sleep(1)
>
> print "Increasing"
>
> count += 1
>
> aw.l_R1.set_data(count, count)
>
>  I don't approve of subclassing the "run" method of QThread, but what you
> have should work a little bit for now.  I really don't approve of using a
> global in another thread affinity and I highly doubt this will work
> correctly.  You should be using Qt signals to communicate between threads,
> but using signals you may have to get a little more complex with how you
> are using QThreads to free up the event loops.
>
> You are also calling "aw.l_R1" which does not exist.  You need to have a
> way to access the graph.  As a quick hack I just set the graph to
> "self.qmc" and then in the run method I did "aw.qmc.l_R1.set_data(count,
> count)".  You will also want to update the limits to show the new data as
> it comes in or set it to a certain range from the start. You will need to
> redraw the canvas every time you update it.  You will likely run into
> problems in the future if you draw in a different thread from the GUI
> thread, but for some reason it works in your example.
>
> I'm not sure if this was intended, but since you are not saving the
> previous "count" you are only plotting points not a line. One way to do
> this is if you want to keep the last N records (lets say N=200) then you
> can plot originally "self.ax.plot(numpy.arange(200), numpy.zeros(200))" and
> then update the y-data only like this "aw.qmc.set_ydata(<length 200 array
> of previous counts>)".
>
>
> def usingQThread():
>
> app = QtCore.QCoreApplication([])
>
> thread = AThread()
>
> thread.finished.connect(app.exit)
>
> thread.start()
>
> sys.exit(app.exec_())
>
>  I'm really confused at this point.  How have you been calling this
> module? As "python test.py"? You are creating 2 QApplications, which isn't
> the easiest to follow.  You should create 1 application, then make the
> window, then make the threads.
>
> Another big thing is that when you run "thread.finished.connect(app.exit)"
> you are connecting the end of the thread to the app closing.  Do you want
> the Application to completely close when the thread finishes (as you have
> it which I've never done so not sure if this will cause problems in more
> complex applications) or do you want the thread to stop, leaving the window
> open? The application would then close when the last window is closed.
>
>
> if __name__ == "__main__":
>
> #
>
>  qApp = QtGui.QApplication(sys.argv)
>
> aw = ApplicationWindow()
>
> aw.showMaximized()
>
> usingQThread()
>
> sys.exit(qApp.exec_())
>
>
>  I removed "usingQThread" method and moved that logic to the if statement
> so it now looks like this:
>
> if __name__ == "__main__":
>     qApp = QtGui.QApplication([" "])
>     aw = ApplicationWindow()
>     aw.showMaximized()
>     thread = AThread()
>     thread.finished.connect(qApp.exit)
>     thread.start()
>     sys.exit(qApp.exec_())
>
>
> I also added a "self.qmc = qmc" in the ApplicationWindow and forced the
> y-limits in the Graph.__init__ method using the following:
>
> self.ax.set_xlim(-1, 6)
> self.ax.set_ylim(-1, 6)
>
> Then in the run method the last 2 lines in the while loop now say:
>
>  aw.qmc.l_R1.set_data(count, count)
>  aw.qmc.draw()
>
>
> This got it running for me, although it had the plotting points problem I
> mentioned earlier.  Hopefully this will get you somewhere. But I can not
> stress enough that you need to research QThreads and how they should be
> used correctly and how threads work in general.  As I noted above you
> should not be drawing in non-GUI threads and you should not be magically
> using a global variable that lives in another thread (use signals and
> slots).  This is bad practice and will likely lead to obscure errors and a
> lot of headache when you're short on time (assuming this is for a work
> project like mine was).
>
> Good luck.
>
> -Dave
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20130115/31fb5f9b/attachment.html>


More information about the PyQt mailing list