Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
qtws26_logo

The Qt Event of the Year: Qt World Summit 2026

Save Your Spot >
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Unable to get QTimer to call slot from non-GUI thread.
Qt 6.12 LTS is out! See what's new in the release blog

Unable to get QTimer to call slot from non-GUI thread.

Scheduled Pinned Locked Moved Unsolved General and Desktop
10 Posts 2 Posters 6.4k Views 2 Watching
  • 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.
  • enjoysmathE Offline
    enjoysmathE Offline
    enjoysmath
    wrote on last edited by
    #1

    For QTimer to work, you must have an event loop in your application; that is, you must call QCoreApplication::exec() somewhere. Timer events will be delivered only while the event loop is running. In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit thetimeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.

    I have a main GUI thread where business objects are created. These business objects need timers to run because they are are Button (with timed press events), Timer, and Graph (which runs over time).

    I have a non-GUI thread called RunnerThread that calls the methods of the business objects which depend on QTimer. For instance:

    def play(self):
            if self._paused or self._timer is None: 
                if self._timer is None:
                    self._pauseTime = 0.0
                    self._loopCounter = 1
                else:
                    #self._timer.cancel()
                    self._timer.killTimer()
                self._paused = False
                self._elapsed = QElapsedTimer()
                self._timer = QTimer()
                self._timer.moveToThread(QThread.thread(self))
                self._timer.timeout.connect(self._displayOnLEDs)
                self._timer.setSingleShot(False)
                self._timer.setInterval(10)
                self._timer.start()
                #self._timer = threading.Timer(10/1000, self._displayOnLEDs)
                #self._startThread()
                self._time = 0
                self._elapsed.start()
    

    is the play() method of Graph which essentially gets called by the RunnerThread. This timer does not work (slot never gets called). I've tried all available solutions on Google that were applicable to my setup including called QCoreApplication.processEvents() and QThread.exec() within RunnerThread.

    I've also tried moving over to threading.Timer but this causes issues: too many repeated creations of a Timer will crash my app with "too many threads" type error even though I cancel() the previously created timer. So I cannot use threading.Timer.

    Please show me how to get QTimer to work when created / started on a non-GUI thread.

    Thanks.

    https://github.com/enjoysmath
    https://math.stackexchange.com/users/26327/exercisingmathematician

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Please provide the full class implementation. With the function you wrote, it's really not clear what you are trying to implement.

      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
      • enjoysmathE Offline
        enjoysmathE Offline
        enjoysmath
        wrote on last edited by
        #3

        Here is minimal running code which demonstrates my bug. "playing" will never be seen on the console:

        from PyQt5.QtCore import QThread, QCoreApplication, QTimer, QElapsedTimer, QObject, pyqtSignal, Qt
        from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
        import sys
        
        
        class RunnerThread(QThread):
            def __init__(self, obj, thread):
                super().__init__()
                self._thread = thread
                self._obj = obj
                
            def run(self):
                self._obj.play(self._thread)
                
        
        class MyObject(QObject):
            def __init__(self):
                super().__init__()
                
            def play(self, thread):
                self._timer = QTimer(self)
                self._timer.moveToThread(thread)
                self._timer.setInterval(300)
                self._timer.timeout.connect(self._play)
                self._timer.setSingleShot(True)
                self._timer.start()
                print("starting")
                
            def _play(self):
                print("playing")
                
        
        if __name__ == "__main__":
            app = QApplication([])
            
            window = QMainWindow()
            window.show()
            
            button = QPushButton("play")
            window.setCentralWidget(button)
            
            obj = MyObject()
            thread = QThread.currentThread()
            runner = RunnerThread(obj, thread)  
            button.clicked.connect(runner.start)
            
            sys.exit(app.exec_())
        

        https://github.com/enjoysmath
        https://math.stackexchange.com/users/26327/exercisingmathematician

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Your obj instance doesn't belong to the correct thread.

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

          enjoysmathE 1 Reply Last reply
          0
          • SGaistS SGaist

            Your obj instance doesn't belong to the correct thread.

            enjoysmathE Offline
            enjoysmathE Offline
            enjoysmath
            wrote on last edited by
            #5

            @SGaist How do I fix that?

            https://github.com/enjoysmath
            https://math.stackexchange.com/users/26327/exercisingmathematician

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              I just realised something more, you are creating a new thread, where you store the current thread (main thread in this case) then in your play function you move the timer to the main thread.

              That's wrong in many ways.

              Don't call moveToThread like you do.

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

              enjoysmathE 1 Reply Last reply
              0
              • SGaistS SGaist

                I just realised something more, you are creating a new thread, where you store the current thread (main thread in this case) then in your play function you move the timer to the main thread.

                That's wrong in many ways.

                Don't call moveToThread like you do.

                enjoysmathE Offline
                enjoysmathE Offline
                enjoysmath
                wrote on last edited by
                #7

                @SGaist That was only an attempt to remedy the problem. I wouldn't do that normally.

                I have an editor where the business objects are created in the gui thread, so how do I make their timers work on another thread?

                https://github.com/enjoysmath
                https://math.stackexchange.com/users/26327/exercisingmathematician

                1 Reply Last reply
                0
                • enjoysmathE Offline
                  enjoysmathE Offline
                  enjoysmath
                  wrote on last edited by
                  #8

                  This code seems to work. Solution: call exec() within QThread run() (which starts its own event loop and takes control of the thread), which means you can't have a main while loop in run(). I will research how to work with Qt event loops.

                  from PyQt5.QtCore import QThread, QCoreApplication, QTimer, QElapsedTimer, QObject, pyqtSignal, Qt
                  from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
                  import sys
                  
                  
                  class RunnerThread(QThread):
                      def __init__(self, obj):
                          super().__init__()
                          self._obj = obj
                  
                          
                      def run(self):
                          self._obj.play()
                          self.exec()
                              
                          
                  
                  class MyObject(QObject):
                      def __init__(self):
                          super().__init__()
                          
                      def play(self):
                          self._timer = QTimer(self)
                          self._timer.setInterval(300)
                          self._timer.timeout.connect(self._play)
                          self._timer.setSingleShot(True)
                          self._timer.start()
                          print("starting")
                          
                      def _play(self):
                          print("playing")
                          
                  
                  if __name__ == "__main__":
                      app = QApplication([])
                      
                      window = QMainWindow()
                      window.show()
                      
                      button = QPushButton("play")
                      window.setCentralWidget(button)
                      
                      obj = MyObject()
                      runner = RunnerThread(obj)
                      button.clicked.connect(runner.start)
                      
                      sys.exit(app.exec_())
                  

                  https://github.com/enjoysmath
                  https://math.stackexchange.com/users/26327/exercisingmathematician

                  1 Reply Last reply
                  0
                  • enjoysmathE Offline
                    enjoysmathE Offline
                    enjoysmath
                    wrote on last edited by
                    #9

                    int QCoreApplication::exec()


                    Enters the main event loop and waits until exit() is called. Returns the value that was set to exit() (which is 0 if exit() is called via quit()).
                    It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets.
                    To make your application perform idle processing (i.e. executing a special function whenever there are no pending events), use a QTimer with 0 timeout. More advanced idle processing schemes can be achieved using processEvents().

                    So I'll use a Qtimer with 0 timeout for my own event loop code.

                    https://github.com/enjoysmath
                    https://math.stackexchange.com/users/26327/exercisingmathematician

                    1 Reply Last reply
                    0
                    • SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      I missed that you weren't calling exec in your QThread subclass.

                      You should take a look again at the QThread documentation about the various possibilities of its use.

                      What do you mean by "main while loop" ?

                      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

                      • Login

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