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. QWidget method not called on destroyed signal
Forum Updated to NodeBB v4.3 + New Features

QWidget method not called on destroyed signal

Scheduled Pinned Locked Moved Solved Qt for Python
13 Posts 3 Posters 877 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.
  • A Offline
    A Offline
    Anonimista
    wrote on last edited by
    #1

    This is my setup:

    1. Main window
    2. QWidged that contains a line chart that gets updated on timer tick called LineChart
    3. Worker object that is moved to a QThread to do the background work

    I am trying to quit the thread on main window close. I connected LineChart.destroyed event to a method named stop() that should quit the thread but the method doesnt get called. Thread exiting works from the main window closeEvent.

    Here is my setup

    import sys
    import psutil
    
    from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
        Signal, Slot)
    from PySide6.QtGui import QPainter
    from PySide6.QtWidgets import (QMainWindow, QApplication, 
        QWidget, QHBoxLayout, QLabel)
    from PySide6.QtCharts import QChart, QChartView, QLineSeries
    
    
    class Worker(QObject):
        
        done = Signal(int)
        
        def __init__(self, parent=None):
            super().__init__(parent)
            
        @Slot()    
        def get_cpu_load(self):
            
            if QThread.currentThread().isInterruptionRequested(): 
                return
            
            print('Worker: ', QThread.currentThread())
            self.done.emit(psutil.cpu_percent())
    
    
    class LineChart(QWidget):
        
        def __init__(self, parent=None):
            
            super().__init__(parent)
            
            self.worker = Worker()
            self.thread = QThread()
            self.worker.moveToThread(self.thread)
            self.thread.start()
    
            self.series = QLineSeries()
            
            for i in range(10):
                self.series.append(i, 0)
    
            self.chart = QChart()
            self.chart.legend().hide()
            self.chart.addSeries(self.series)
            self.chart.createDefaultAxes()
            self.chart.setTitle('CPU load')
            self.chart.layout().setContentsMargins(0, 0, 0, 0)
            self.chart.setBackgroundRoundness(0)
            
            self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
            self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
             
            self.horizontal_axis.setLabelsVisible(False)
            self.vertical_axis.setLabelsVisible(False)
            
            self.vertical_axis.setMin(0)
            self.vertical_axis.setMax(100)
            
            self.chart_view = QChartView(self.chart)
            self.chart_view.setRenderHint(QPainter.Antialiasing)
            
            self.setLayout(QHBoxLayout())
            self.layout().addWidget(self.chart_view)
            
            self.label = QLabel()
            self.label.setFixedWidth(20)
            self.label.setFixedHeight(200)
            self.label.setStyleSheet('background-color: lightsteelblue;')
            self.layout().addWidget(self.label)
            self.layout().setAlignment(self.label, Qt.AlignmentFlag.AlignBottom)
            
            self.destroyed.connect(self.stop)
            
            self.timer = QTimer()
            self.timer.timeout.connect(self.worker.get_cpu_load)
            self.worker.done.connect(self.update_chart)
            self.timer.start(500)
            
            self.current_time = 19
            
        def update_chart(self, load):
            
            print('Main: ', QThread.currentThread())
            self.series.append(self.current_time, load)
            self.label.setFixedHeight(load * self.chart_view.size().height() / 100)
            self.current_time += 1
            
            if self.series.count() > 20:
                self.series.remove(0)
                
            self.horizontal_axis.setMin(self.series.at(0).x())
            self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
            
        def stop(self):
            print('in stop method')
            self.timer.stop()
            self.thread.requestInterruption()
            self.thread.quit()
            self.thread.wait()
            
    
    class MainWindow(QMainWindow):
        
        def __init__(self):
            
            super().__init__()
            
            self.chart_widget = LineChart()   
            self.setCentralWidget(self.chart_widget)
        '''
        def closeEvent(self, event):
            print('window close event')
            try:
                pass
                #self.chart_widget.stop()
            except AttributeError as e:
                print(e)
    '''
    
    if __name__ == '__main__':
        
        app = QApplication(sys.argv)
        
        window = MainWindow()
        window.show()
        window.resize(440, 300)
        sys.exit(app.exec())
    
    JonBJ 1 Reply Last reply
    0
    • A Anonimista

      This is my setup:

      1. Main window
      2. QWidged that contains a line chart that gets updated on timer tick called LineChart
      3. Worker object that is moved to a QThread to do the background work

      I am trying to quit the thread on main window close. I connected LineChart.destroyed event to a method named stop() that should quit the thread but the method doesnt get called. Thread exiting works from the main window closeEvent.

      Here is my setup

      import sys
      import psutil
      
      from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
          Signal, Slot)
      from PySide6.QtGui import QPainter
      from PySide6.QtWidgets import (QMainWindow, QApplication, 
          QWidget, QHBoxLayout, QLabel)
      from PySide6.QtCharts import QChart, QChartView, QLineSeries
      
      
      class Worker(QObject):
          
          done = Signal(int)
          
          def __init__(self, parent=None):
              super().__init__(parent)
              
          @Slot()    
          def get_cpu_load(self):
              
              if QThread.currentThread().isInterruptionRequested(): 
                  return
              
              print('Worker: ', QThread.currentThread())
              self.done.emit(psutil.cpu_percent())
      
      
      class LineChart(QWidget):
          
          def __init__(self, parent=None):
              
              super().__init__(parent)
              
              self.worker = Worker()
              self.thread = QThread()
              self.worker.moveToThread(self.thread)
              self.thread.start()
      
              self.series = QLineSeries()
              
              for i in range(10):
                  self.series.append(i, 0)
      
              self.chart = QChart()
              self.chart.legend().hide()
              self.chart.addSeries(self.series)
              self.chart.createDefaultAxes()
              self.chart.setTitle('CPU load')
              self.chart.layout().setContentsMargins(0, 0, 0, 0)
              self.chart.setBackgroundRoundness(0)
              
              self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
              self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
               
              self.horizontal_axis.setLabelsVisible(False)
              self.vertical_axis.setLabelsVisible(False)
              
              self.vertical_axis.setMin(0)
              self.vertical_axis.setMax(100)
              
              self.chart_view = QChartView(self.chart)
              self.chart_view.setRenderHint(QPainter.Antialiasing)
              
              self.setLayout(QHBoxLayout())
              self.layout().addWidget(self.chart_view)
              
              self.label = QLabel()
              self.label.setFixedWidth(20)
              self.label.setFixedHeight(200)
              self.label.setStyleSheet('background-color: lightsteelblue;')
              self.layout().addWidget(self.label)
              self.layout().setAlignment(self.label, Qt.AlignmentFlag.AlignBottom)
              
              self.destroyed.connect(self.stop)
              
              self.timer = QTimer()
              self.timer.timeout.connect(self.worker.get_cpu_load)
              self.worker.done.connect(self.update_chart)
              self.timer.start(500)
              
              self.current_time = 19
              
          def update_chart(self, load):
              
              print('Main: ', QThread.currentThread())
              self.series.append(self.current_time, load)
              self.label.setFixedHeight(load * self.chart_view.size().height() / 100)
              self.current_time += 1
              
              if self.series.count() > 20:
                  self.series.remove(0)
                  
              self.horizontal_axis.setMin(self.series.at(0).x())
              self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
              
          def stop(self):
              print('in stop method')
              self.timer.stop()
              self.thread.requestInterruption()
              self.thread.quit()
              self.thread.wait()
              
      
      class MainWindow(QMainWindow):
          
          def __init__(self):
              
              super().__init__()
              
              self.chart_widget = LineChart()   
              self.setCentralWidget(self.chart_widget)
          '''
          def closeEvent(self, event):
              print('window close event')
              try:
                  pass
                  #self.chart_widget.stop()
              except AttributeError as e:
                  print(e)
      '''
      
      if __name__ == '__main__':
          
          app = QApplication(sys.argv)
          
          window = MainWindow()
          window.show()
          window.resize(440, 300)
          sys.exit(app.exec())
      
      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @Anonimista said in QWidget method not called on destroyed signal:

      I connected LineChart.destroyed event to a method named stop() that should quit the thread but the method doesnt get called.

      Just on this one. When do you think your self.chart_widget gets destroyed and so the destroyed/stop() signal/slot will get executed? For your window = MainWindow() the window variable does not go out of scope until after the sys.exit(app.exec()), as the main program actually finishes. I would guess the central widget does not get destroyed until then, and if it's after the Qt event loop has exited you presumably won't get the slot attached to the signal actually executed.

      You might find that setting the Qt.WA_DeleteOnClose attribute (usually used on a QDialog) on your window instance causes the destroyed signal to be emitted before the event loop is exited. Otherwise cleaning up in an overridden closeEvent() seems fine/maybe better. Or you could do the app.exec() before the sys.exit() and clean up explicitly (not signal/slot) between the two. Lots of possibilities.

      1 Reply Last reply
      3
      • A Offline
        A Offline
        Anonimista
        wrote on last edited by Anonimista
        #3

        That makes sense. I also tried to override LineChart.closeEvent() but that one doesn't seem to be called either. My goal here is to somehow stop the timer from LineChart itself so I don't have to do it from the main window. So far I have tried LineChart.closeEvent and LineChart.destroyed but neither seem to work

        So if I want to reuse a similar widget I don't have to remember to call the stop method from other widgets.

        I also forgot to mention the error I get is:

        QThread: Destroyed while thread is still running
        

        Which is why I am trying to exit the thread on main window close

        jsulmJ 1 Reply Last reply
        0
        • A Anonimista

          That makes sense. I also tried to override LineChart.closeEvent() but that one doesn't seem to be called either. My goal here is to somehow stop the timer from LineChart itself so I don't have to do it from the main window. So far I have tried LineChart.closeEvent and LineChart.destroyed but neither seem to work

          So if I want to reuse a similar widget I don't have to remember to call the stop method from other widgets.

          I also forgot to mention the error I get is:

          QThread: Destroyed while thread is still running
          

          Which is why I am trying to exit the thread on main window close

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @Anonimista said in QWidget method not called on destroyed signal:

          QThread: Destroyed while thread is still running

          Stop the thread in the destructor of the LineChart class and wait for it to stop (https://doc.qt.io/qt-6/qthread.html#wait-1)

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          JonBJ 1 Reply Last reply
          1
          • jsulmJ jsulm

            @Anonimista said in QWidget method not called on destroyed signal:

            QThread: Destroyed while thread is still running

            Stop the thread in the destructor of the LineChart class and wait for it to stop (https://doc.qt.io/qt-6/qthread.html#wait-1)

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by
            #5

            @jsulm said in QWidget method not called on destroyed signal:

            Stop the thread in the destructor of the LineChart and wait for it to stop (https://doc.qt.io/qt-6/qthread.html#wait-1)

            I haven't tested but am "concerned" that the way the code is written from Python the window = MainWindow() will keep a reference till after the event loop has exited (app.exec() returns). Can one then still call QThread.wait() with no event loop running? I don't know, OP may have to investigate.

            1 Reply Last reply
            1
            • A Offline
              A Offline
              Anonimista
              wrote on last edited by
              #6

              @jsulm It appears I don't know how to. I used LineChart.del() to call the stop() method but I still get the "QThread: Destroyed while thread is still running" error, while If i call stop() from MainWindow.closeEvent() it works fine.

              jsulmJ 1 Reply Last reply
              0
              • A Offline
                A Offline
                Anonimista
                wrote on last edited by
                #7

                @JonB You are right, If I use:

                if __name__ == '__main__':
                    
                    app = QApplication(sys.argv)
                    
                    window = MainWindow()
                    window.show()
                    res = app.exec()
                    del window
                    sys.exit(res)
                

                LineChart.del() gets called. So now I can use del window instead of MainWindow.closeEvent(). I actually remember del being used in the official PySide6 Qml examples like

                    view.show()
                    res = app.exec()
                    # Deleting the view before it goes out of scope is required to make sure all child QML instances
                    # are destroyed in the correct order.
                    del view
                    sys.exit(res)
                

                So maybe there isn't a better way to do this from Python . Thanks to the both of you

                1 Reply Last reply
                1
                • A Anonimista has marked this topic as solved on
                • A Anonimista

                  @jsulm It appears I don't know how to. I used LineChart.del() to call the stop() method but I still get the "QThread: Destroyed while thread is still running" error, while If i call stop() from MainWindow.closeEvent() it works fine.

                  jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  @Anonimista said in QWidget method not called on destroyed signal:

                  I used LineChart.del() to call the stop() method

                  Do you mean you implemented the destructor where you call stop()?

                  https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    Anonimista
                    wrote on last edited by
                    #9

                    @jsulm I used LineChart.__del__() to call stop(). I've since moved the whole stop() body into __del__() and use del window after app.exec() because without it __del__() does not get called.

                    Here's what it looks like right now

                    import sys
                    import psutil
                    
                    from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
                        Signal, Slot)
                    from PySide6.QtGui import QPainter
                    from PySide6.QtWidgets import (QMainWindow, QApplication, 
                        QWidget, QHBoxLayout, QLabel)
                    from PySide6.QtCharts import QChart, QChartView, QLineSeries
                    
                    
                    class Worker(QObject):
                        
                        done = Signal(int)
                        
                        def __init__(self, parent=None):
                            super().__init__(parent)
                            
                        @Slot()    
                        def get_cpu_load(self):
                            
                            if QThread.currentThread().isInterruptionRequested(): 
                                return
                            self.done.emit(psutil.cpu_percent())
                    
                    
                    class LineChart(QWidget):
                        
                        def __init__(self, parent=None):
                            
                            super().__init__(parent)
                            
                            self.worker = Worker()
                            self.thread = QThread()
                            self.worker.moveToThread(self.thread)
                            self.thread.start()
                    
                            self.series = QLineSeries()
                            
                            for i in range(10):
                                self.series.append(i, 0)
                    
                            self.chart = QChart()
                            self.chart.legend().hide()
                            self.chart.addSeries(self.series)
                            self.chart.createDefaultAxes()
                            self.chart.setTitle('CPU load')
                            self.chart.layout().setContentsMargins(0, 0, 0, 0)
                            self.chart.setBackgroundRoundness(0)
                            
                            self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
                            self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
                             
                            self.horizontal_axis.setLabelsVisible(False)
                            self.vertical_axis.setLabelsVisible(False)
                            
                            self.vertical_axis.setMin(0)
                            self.vertical_axis.setMax(100)
                            
                            self.chart_view = QChartView(self.chart)
                            self.chart_view.setRenderHint(QPainter.Antialiasing)
                            
                            self.setLayout(QHBoxLayout())
                            self.layout().addWidget(self.chart_view)
                            
                            self.timer = QTimer()
                            self.timer.timeout.connect(self.worker.get_cpu_load)
                            self.worker.done.connect(self.update_chart)
                            self.timer.start(500)
                            
                            self.current_time = 19
                            
                        def update_chart(self, load):
                            
                            self.series.append(self.current_time, load)
                            self.current_time += 1
                            
                            if self.series.count() > 20:
                                self.series.remove(0)
                                
                            self.horizontal_axis.setMin(self.series.at(0).x())
                            self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
                    
                        def __del__(self):
                            print('in LineChart.__del__()')
                            self.timer.stop()
                            self.thread.requestInterruption()
                            self.thread.quit()
                            self.thread.wait()        
                    
                    
                    class MainWindow(QMainWindow):
                        
                        def __init__(self):
                            
                            super().__init__()
                            
                            self.chart_widget = LineChart()   
                            self.setCentralWidget(self.chart_widget)
                    
                    
                    if __name__ == '__main__':
                        
                        app = QApplication(sys.argv)
                        
                        window = MainWindow()
                        window.resize(440, 300)
                        window.show()
                        res = app.exec()
                        del window
                        sys.exit(res)
                    
                    jsulmJ 1 Reply Last reply
                    0
                    • A Anonimista

                      @jsulm I used LineChart.__del__() to call stop(). I've since moved the whole stop() body into __del__() and use del window after app.exec() because without it __del__() does not get called.

                      Here's what it looks like right now

                      import sys
                      import psutil
                      
                      from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
                          Signal, Slot)
                      from PySide6.QtGui import QPainter
                      from PySide6.QtWidgets import (QMainWindow, QApplication, 
                          QWidget, QHBoxLayout, QLabel)
                      from PySide6.QtCharts import QChart, QChartView, QLineSeries
                      
                      
                      class Worker(QObject):
                          
                          done = Signal(int)
                          
                          def __init__(self, parent=None):
                              super().__init__(parent)
                              
                          @Slot()    
                          def get_cpu_load(self):
                              
                              if QThread.currentThread().isInterruptionRequested(): 
                                  return
                              self.done.emit(psutil.cpu_percent())
                      
                      
                      class LineChart(QWidget):
                          
                          def __init__(self, parent=None):
                              
                              super().__init__(parent)
                              
                              self.worker = Worker()
                              self.thread = QThread()
                              self.worker.moveToThread(self.thread)
                              self.thread.start()
                      
                              self.series = QLineSeries()
                              
                              for i in range(10):
                                  self.series.append(i, 0)
                      
                              self.chart = QChart()
                              self.chart.legend().hide()
                              self.chart.addSeries(self.series)
                              self.chart.createDefaultAxes()
                              self.chart.setTitle('CPU load')
                              self.chart.layout().setContentsMargins(0, 0, 0, 0)
                              self.chart.setBackgroundRoundness(0)
                              
                              self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
                              self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
                               
                              self.horizontal_axis.setLabelsVisible(False)
                              self.vertical_axis.setLabelsVisible(False)
                              
                              self.vertical_axis.setMin(0)
                              self.vertical_axis.setMax(100)
                              
                              self.chart_view = QChartView(self.chart)
                              self.chart_view.setRenderHint(QPainter.Antialiasing)
                              
                              self.setLayout(QHBoxLayout())
                              self.layout().addWidget(self.chart_view)
                              
                              self.timer = QTimer()
                              self.timer.timeout.connect(self.worker.get_cpu_load)
                              self.worker.done.connect(self.update_chart)
                              self.timer.start(500)
                              
                              self.current_time = 19
                              
                          def update_chart(self, load):
                              
                              self.series.append(self.current_time, load)
                              self.current_time += 1
                              
                              if self.series.count() > 20:
                                  self.series.remove(0)
                                  
                              self.horizontal_axis.setMin(self.series.at(0).x())
                              self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
                      
                          def __del__(self):
                              print('in LineChart.__del__()')
                              self.timer.stop()
                              self.thread.requestInterruption()
                              self.thread.quit()
                              self.thread.wait()        
                      
                      
                      class MainWindow(QMainWindow):
                          
                          def __init__(self):
                              
                              super().__init__()
                              
                              self.chart_widget = LineChart()   
                              self.setCentralWidget(self.chart_widget)
                      
                      
                      if __name__ == '__main__':
                          
                          app = QApplication(sys.argv)
                          
                          window = MainWindow()
                          window.resize(440, 300)
                          window.show()
                          res = app.exec()
                          del window
                          sys.exit(res)
                      
                      jsulmJ Offline
                      jsulmJ Offline
                      jsulm
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      @Anonimista said in QWidget method not called on destroyed signal:

                      I used LineChart.del() to call stop()

                      Calling del() will not magically call stop(), that was my point

                      https://forum.qt.io/topic/113070/qt-code-of-conduct

                      JonBJ 1 Reply Last reply
                      1
                      • jsulmJ jsulm

                        @Anonimista said in QWidget method not called on destroyed signal:

                        I used LineChart.del() to call stop()

                        Calling del() will not magically call stop(), that was my point

                        JonBJ Offline
                        JonBJ Offline
                        JonB
                        wrote on last edited by
                        #11

                        @jsulm
                        I presume OP means he still has the signal on destroyed() to call his slot which calls stop(). And with explicit call to LineChart.del() wherever he has it does result in the slot being called successfully. At least that's my guess...?

                        A 1 Reply Last reply
                        0
                        • JonBJ JonB

                          @jsulm
                          I presume OP means he still has the signal on destroyed() to call his slot which calls stop(). And with explicit call to LineChart.del() wherever he has it does result in the slot being called successfully. At least that's my guess...?

                          A Offline
                          A Offline
                          Anonimista
                          wrote on last edited by Anonimista
                          #12

                          @JonB No, I have the whole script posted in my previous comment. I copied the code from stop() into __del__() and had to call del window at the end of the script.

                          Since the last comment I did some more searching and I was able to use atexit.register to execute the QThread cleanup code so it is competely contained in the LineChart class. Here is the current iteration:

                          import sys
                          import psutil
                          import atexit
                          
                          from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
                              Signal, Slot)
                          from PySide6.QtGui import QPainter
                          from PySide6.QtWidgets import (QMainWindow, QApplication, 
                              QWidget, QHBoxLayout)
                          from PySide6.QtCharts import QChart, QChartView, QLineSeries
                          
                          
                          class Worker(QObject):
                              
                              done = Signal(int)
                              
                              def __init__(self, parent=None):
                                  super().__init__(parent)
                                  
                              @Slot()    
                              def get_cpu_load(self):
                                  
                                  if QThread.currentThread().isInterruptionRequested(): 
                                      return
                                  self.done.emit(psutil.cpu_percent())
                          
                          
                          class LineChart(QWidget):
                              
                              def __init__(self, parent=None):
                                  
                                  super().__init__(parent)
                                  
                                  atexit.register(self.cleanup)
                                  
                                  self.worker = Worker()
                                  self.thread = QThread()
                                  self.worker.moveToThread(self.thread)
                                  self.thread.start()
                          
                                  self.series = QLineSeries()
                                  
                                  for i in range(10):
                                      self.series.append(i, 0)
                          
                                  self.chart = QChart()
                                  self.chart.legend().hide()
                                  self.chart.addSeries(self.series)
                                  self.chart.createDefaultAxes()
                                  self.chart.setTitle('CPU load')
                                  self.chart.layout().setContentsMargins(0, 0, 0, 0)
                                  self.chart.setBackgroundRoundness(0)
                                  
                                  self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
                                  self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
                                   
                                  self.horizontal_axis.setLabelsVisible(False)
                                  self.vertical_axis.setLabelsVisible(False)
                                  
                                  self.vertical_axis.setMin(0)
                                  self.vertical_axis.setMax(100)
                                  
                                  self.chart_view = QChartView(self.chart)
                                  self.chart_view.setRenderHint(QPainter.Antialiasing)
                                  
                                  self.setLayout(QHBoxLayout())
                                  self.layout().addWidget(self.chart_view)
                                  
                                  self.timer = QTimer()
                                  self.timer.timeout.connect(self.worker.get_cpu_load)
                                  self.worker.done.connect(self.update_chart)
                                  self.timer.start(500)
                                  
                                  self.current_time = 19
                                  
                              def update_chart(self, load):
                                  
                                  self.series.append(self.current_time, load)
                                  self.current_time += 1
                                  
                                  if self.series.count() > 20:
                                      self.series.remove(0)
                                      
                                  self.horizontal_axis.setMin(self.series.at(0).x())
                                  self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
                          
                              def cleanup(self):
                                  
                                  print('in cleanup')
                                  self.timer.stop()
                                  self.thread.requestInterruption()
                                  self.thread.quit()
                                  self.thread.wait()        
                          
                          
                          class MainWindow(QMainWindow):
                              
                              def __init__(self):
                                  
                                  super().__init__()
                                  
                                  self.chart_widget = LineChart()   
                                  self.setCentralWidget(self.chart_widget)
                          
                          
                          if __name__ == '__main__':
                              
                              app = QApplication(sys.argv)
                              
                              window = MainWindow()
                              window.resize(440, 300)
                              window.show()
                              sys.exit(app.exec())
                          
                          
                          A 1 Reply Last reply
                          0
                          • A Anonimista

                            @JonB No, I have the whole script posted in my previous comment. I copied the code from stop() into __del__() and had to call del window at the end of the script.

                            Since the last comment I did some more searching and I was able to use atexit.register to execute the QThread cleanup code so it is competely contained in the LineChart class. Here is the current iteration:

                            import sys
                            import psutil
                            import atexit
                            
                            from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
                                Signal, Slot)
                            from PySide6.QtGui import QPainter
                            from PySide6.QtWidgets import (QMainWindow, QApplication, 
                                QWidget, QHBoxLayout)
                            from PySide6.QtCharts import QChart, QChartView, QLineSeries
                            
                            
                            class Worker(QObject):
                                
                                done = Signal(int)
                                
                                def __init__(self, parent=None):
                                    super().__init__(parent)
                                    
                                @Slot()    
                                def get_cpu_load(self):
                                    
                                    if QThread.currentThread().isInterruptionRequested(): 
                                        return
                                    self.done.emit(psutil.cpu_percent())
                            
                            
                            class LineChart(QWidget):
                                
                                def __init__(self, parent=None):
                                    
                                    super().__init__(parent)
                                    
                                    atexit.register(self.cleanup)
                                    
                                    self.worker = Worker()
                                    self.thread = QThread()
                                    self.worker.moveToThread(self.thread)
                                    self.thread.start()
                            
                                    self.series = QLineSeries()
                                    
                                    for i in range(10):
                                        self.series.append(i, 0)
                            
                                    self.chart = QChart()
                                    self.chart.legend().hide()
                                    self.chart.addSeries(self.series)
                                    self.chart.createDefaultAxes()
                                    self.chart.setTitle('CPU load')
                                    self.chart.layout().setContentsMargins(0, 0, 0, 0)
                                    self.chart.setBackgroundRoundness(0)
                                    
                                    self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
                                    self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
                                     
                                    self.horizontal_axis.setLabelsVisible(False)
                                    self.vertical_axis.setLabelsVisible(False)
                                    
                                    self.vertical_axis.setMin(0)
                                    self.vertical_axis.setMax(100)
                                    
                                    self.chart_view = QChartView(self.chart)
                                    self.chart_view.setRenderHint(QPainter.Antialiasing)
                                    
                                    self.setLayout(QHBoxLayout())
                                    self.layout().addWidget(self.chart_view)
                                    
                                    self.timer = QTimer()
                                    self.timer.timeout.connect(self.worker.get_cpu_load)
                                    self.worker.done.connect(self.update_chart)
                                    self.timer.start(500)
                                    
                                    self.current_time = 19
                                    
                                def update_chart(self, load):
                                    
                                    self.series.append(self.current_time, load)
                                    self.current_time += 1
                                    
                                    if self.series.count() > 20:
                                        self.series.remove(0)
                                        
                                    self.horizontal_axis.setMin(self.series.at(0).x())
                                    self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
                            
                                def cleanup(self):
                                    
                                    print('in cleanup')
                                    self.timer.stop()
                                    self.thread.requestInterruption()
                                    self.thread.quit()
                                    self.thread.wait()        
                            
                            
                            class MainWindow(QMainWindow):
                                
                                def __init__(self):
                                    
                                    super().__init__()
                                    
                                    self.chart_widget = LineChart()   
                                    self.setCentralWidget(self.chart_widget)
                            
                            
                            if __name__ == '__main__':
                                
                                app = QApplication(sys.argv)
                                
                                window = MainWindow()
                                window.resize(440, 300)
                                window.show()
                                sys.exit(app.exec())
                            
                            
                            A Offline
                            A Offline
                            Anonimista
                            wrote on last edited by
                            #13

                            @Anonimista

                            atexit.register registered functions/methods are executed on the Python interpreter termination a better solution would be to use weakref.finalize which is called when a LineChart is garbage collected. Here is the current code:

                            import sys
                            import psutil
                            import weakref
                            
                            from PySide6.QtCore import (Qt, QTimer, QThread,QObject, 
                                Signal, Slot)
                            from PySide6.QtGui import QPainter
                            from PySide6.QtWidgets import (QMainWindow, QApplication, 
                                QWidget, QHBoxLayout)
                            from PySide6.QtCharts import QChart, QChartView, QLineSeries
                            
                            
                            class Worker(QObject):
                                
                                done = Signal(int)
                                
                                def __init__(self, parent=None):
                                    super().__init__(parent)
                                    
                                @Slot()    
                                def get_cpu_load(self):
                                    
                                    if QThread.currentThread().isInterruptionRequested(): 
                                        return
                                    self.done.emit(psutil.cpu_percent())
                            
                            
                            class LineChart(QWidget):
                                
                                def __init__(self, parent=None):
                                    
                                    super().__init__(parent)
                                    
                                    weakref.finalize(self, self.cleanup)
                                    
                                    self.worker = Worker()
                                    self.thread = QThread()
                                    self.worker.moveToThread(self.thread)
                                    self.thread.start()
                            
                                    self.series = QLineSeries()
                                    
                                    for i in range(10):
                                        self.series.append(i, 0)
                            
                                    self.chart = QChart()
                                    self.chart.legend().hide()
                                    self.chart.addSeries(self.series)
                                    self.chart.createDefaultAxes()
                                    self.chart.setTitle('CPU load')
                                    self.chart.layout().setContentsMargins(0, 0, 0, 0)
                                    self.chart.setBackgroundRoundness(0)
                                    
                                    self.horizontal_axis = self.chart.axes(Qt.Orientation.Horizontal)[0]
                                    self.vertical_axis = self.chart.axes(Qt.Orientation.Vertical)[0]
                                     
                                    self.horizontal_axis.setLabelsVisible(False)
                                    self.vertical_axis.setLabelsVisible(False)
                                    
                                    self.vertical_axis.setMin(0)
                                    self.vertical_axis.setMax(100)
                                    
                                    self.chart_view = QChartView(self.chart)
                                    self.chart_view.setRenderHint(QPainter.Antialiasing)
                                    
                                    self.setLayout(QHBoxLayout())
                                    self.layout().addWidget(self.chart_view)
                                    
                                    self.timer = QTimer()
                                    self.timer.timeout.connect(self.worker.get_cpu_load)
                                    self.worker.done.connect(self.update_chart)
                                    self.timer.start(500)
                                    
                                    self.current_time = 19
                                    
                                def update_chart(self, load):
                                    
                                    self.series.append(self.current_time, load)
                                    self.current_time += 1
                                    
                                    if self.series.count() > 20:
                                        self.series.remove(0)
                                        
                                    self.horizontal_axis.setMin(self.series.at(0).x())
                                    self.horizontal_axis.setMax(self.series.at(self.series.count() - 1).x())
                                    
                                def cleanup(self):
                                    
                                    print('in cleanup')
                                    self.timer.stop()
                                    self.thread.requestInterruption()
                                    self.thread.quit()
                                    self.thread.wait()   
                            
                            
                            
                            class MainWindow(QMainWindow):
                                
                                def __init__(self):
                                    
                                    super().__init__()
                                    
                                    self.chart_widget = LineChart()  
                                    self.setCentralWidget(self.chart_widget)
                            
                            
                            if __name__ == '__main__':
                                
                                app = QApplication(sys.argv)
                                
                                window = MainWindow()
                                window.resize(440, 300)
                                window.show()
                                sys.exit(app.exec())
                            
                            
                            1 Reply Last reply
                            0

                            • Login

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