Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Threaded plotting with pyqtgraph
Forum Update on Monday, May 27th 2025

Threaded plotting with pyqtgraph

Scheduled Pinned Locked Moved Unsolved Qt for Python
4 Posts 2 Posters 2.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mika-L
    wrote on 13 Apr 2021, 17:30 last edited by
    #1

    Dear all,

    I try to plot data from a thread and I have issues. I use pyqtgraph as plotter.

    • I use a worker (AcquisitionWorker in the code below) object run by a thread, through moveToThread process. This worker generates random data in order to be plotted. The data are stored in the main window, data generation process is protected by a QMutex.
    • I also have a timer in the main loop, for test.

    Both time-sharing techniques work well in my code (checked by some print).

    When I move the plot code in the timer of the main event loop, everything works. However, if I move this code to the worker thread, just close to the data generation code, the plot does not refresh well, and I get :

    QObject::startTimer: Timers cannot be started from another thread
    QObject::startTimer: Timers cannot be started from another thread
    QObject::startTimer: Timers cannot be started from another thread
    QObject::startTimer: Timers cannot be started from another thread
    QObject::startTimer: Timers cannot be started from another thread
    QObject::startTimer: Timers cannot be started from another thread
    

    Any Idea ?

    Working Code

    here in timer mode, please read comments of "runProc" method for Thread/Worker mode. Click on the "start" button to launch the worker thread.

    #!/opt/miniconda3/bin/python3.7
    
    from PyQt5.QtCore import QObject, QThread, pyqtSignal,QTimer,QMutex
    from PyQt5 import QtGui,QtWidgets
    import pyqtgraph as pg
    import random
    import numpy as np
    from datetime import datetime
    import time
    import sys
    
    class GenericAcquisitionWorker(QObject):
        progress = pyqtSignal()
    
        def __init__(self,*args,**kwargs):
            super().__init__(*args,**kwargs)
            self.mutex = QMutex()
            self.pleaseRunFlag = None
    
        def pleaseRun(self):
            if self.pleaseRunFlag == False:
                if self.mutex.tryLock():
                    self.mutex.unlock()
                else:
                    self.mutex.unlock()
                self.pleaseRunFlag = True
    
        def pleasePause(self):
            self.pleaseRunFlag = False
    
        def pleaseRunStatus(self):
            return self.pleaseRunFlag
    
        def run(self):
            while True:
                if self.mutex.tryLock():
                    if self.pleaseRunFlag == True :
                        self.runningProcess()
                        self.progress.emit()
                        self.mutex.unlock()
                else:
                    self.idlingProcess()
    
        def runningProcess(self): pass
    
        def idlingProcess(self):pass
    
    
    class AcquisitionWorker(GenericAcquisitionWorker):
        def __init__(self,parent,*args,**kwargs):
            super().__init__(*args,**kwargs)
            self.parent = parent
    
        def runningProcess(self):
            self.parent.runProc()
    
        def idlingProcess(self):
            time.sleep(0.1)
    
    
    def showThread(name):
        print("%s : %s Thread 0x%x"%(datetime.now().strftime("%H:%M:%S"),name,int(QThread.currentThreadId())))
    
    class Window(QtWidgets.QMainWindow):
        def __init__(self,*args,**kwargs):
            super().__init__(*args,**kwargs)
            self.buildGUILayout()
            self.buildPausedAcquisitionWorker()
            self.buildTimer()
            self.x = np.linspace(0,10,1000)
            self.y = np.linspace(0,10,1000)
            self.thePen = {'color':'r','width': 2}
    
        def workerProgress(self):
            self.GuiMainLayout['pbar'].setValue(random.randint(0, 100))
            showThread('Report')
            print("\n")
    
        def mainAppProgress(self):
            showThread('Timer')
            # DEACTIVATE CODE BELOW FOR TIMER-BASED PLOTTING
            if(self.acquisitionWorker.pleaseRunStatus()):
                self.GuiMainLayout['timePlot'].plot(x=self.x,y=self.y,  clear=True, _callSync='off')#,pen=self.thePen)
    
        def runProc(self):
            self.x = np.linspace(0,10,10000)
            self.y = np.random.rand(*self.x.shape)
            time.sleep(0.1)
            showThread('Acquisition Worker')
            # DEACTIVATE CODE BELOW FOR WORKER-THREAD-BASED PLOTTING
            #if(self.acquisitionWorker.pleaseRunStatus()):
            #   self.GuiMainLayout['timePlot'].plot(x=self.x,y=self.y,  clear=True, _callSync='off')#,pen=self.thePen)
    
    
    
        def buildPausedAcquisitionWorker(self):
            self.thread = QThread()
            self.acquisitionWorker = AcquisitionWorker(self)
            self.acquisitionWorker.moveToThread(self.thread)
            self.thread.started.connect(self.acquisitionWorker.run)
            self.acquisitionWorker.progress.connect(self.workerProgress)
            self.acquisitionWorker.pleasePause()
            self.thread.start()
    
    
    
    
    
        def buildTimer(self):
            self.timer = QTimer()
            self.timer.setInterval(100)
            self.timer.timeout.connect(self.mainAppProgress)
            self.timer.start()
    
        def buildGUILayout(self):
            self.GuiMainLayout={}
            # all individual widgets
            self.GuiMainLayout['label'] = QtWidgets.QLabel('test')
            self.GuiMainLayout['start'] = QtWidgets.QPushButton("Start")
            self.GuiMainLayout['start'].clicked.connect(self.signal_start)
            self.GuiMainLayout['stop'] = QtWidgets.QPushButton("Stop")
            self.GuiMainLayout['stop'].clicked.connect(self.signal_stop)
            self.GuiMainLayout['pbar'] = QtWidgets.QProgressBar()
            self.GuiMainLayout['pbar'].setGeometry(30, 40, 200, 25)
    
            pg.setConfigOption('antialias',True)
            #pg.setConfigOption('background', 'w')
            #pg.setConfigOption('foreground', 'k')
            pg.setConfigOption('useOpenGL',True)
            #pg.setConfigOption('leftButtonPan',False)
    
    
            self.GuiMainLayout['timePlot'] = pg.PlotWidget()
    
            # the layout itself
            self.GuiMainLayout['mainL'] = QtWidgets.QVBoxLayout()
            self.GuiMainLayout['mainL'].addWidget(self.GuiMainLayout['label'])
            self.GuiMainLayout['mainL'].addWidget(self.GuiMainLayout['timePlot'])
            self.GuiMainLayout['mainL'].addWidget(self.GuiMainLayout['start'])
            self.GuiMainLayout['mainL'].addWidget(self.GuiMainLayout['stop'])
            self.GuiMainLayout['mainL'].addWidget(self.GuiMainLayout['pbar'])
            self.GuiMainLayout['main'] = QtWidgets.QWidget()
            self.GuiMainLayout['main'].setLayout(self.GuiMainLayout['mainL'])
            # attribute the layout to this window
            self.setCentralWidget(self.GuiMainLayout['main'])
    
        def signal_start(self):
            self.acquisitionWorker.pleaseRun()
    
        def signal_stop(self):
            self.acquisitionWorker.pleasePause()
    
    app = QtWidgets.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec())
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on 13 Apr 2021, 18:17 last edited by
      #2

      Hi,

      Do not access GUI element from different threads. It's only allowed in the main thread aka GUI thread.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1
      • M Offline
        M Offline
        Mika-L
        wrote on 14 Apr 2021, 08:25 last edited by
        #3

        So there is no solution ? may it be possible to make "offscreen drawing" of a pyqtgraph in a thread, then blitting it in the event loop ? Is there information about offscreen drawing with Qt ? (I did not find).

        Thanks a lot,
        Mike

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on 14 Apr 2021, 19:35 last edited by
          #4

          You can check the dvg-pyqtgraph-threadsafe package. (I haven't used it though).

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          0

          1/4

          13 Apr 2021, 17:30

          • Login

          • Login or register to search.
          1 out of 4
          • First post
            1/4
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved