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 816 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 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