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. Explicitly running a procedure in a different QThread
Qt 6.11 is out! See what's new in the release blog

Explicitly running a procedure in a different QThread

Scheduled Pinned Locked Moved Unsolved Qt for Python
13 Posts 3 Posters 2.2k Views 1 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.
  • N nutrx

    Hi. I am stuck with the problem of explicitly executing something (in the end creating certain GUI elements) in the main thread from a QObject that runs in its own thread. I don't know much about threading, but I know that the communication must be set up using Qt's signals and slots. However, the signals seem to get "lost", the following code reproduces this, the do_something method isn't triggered at all. The goal is to reach 'success' after ThreadBridge.do_something() has finished executing.

    import sys
    from PySide2.QtWidgets import QMainWindow, QApplication
    from PySide2.QtCore import QObject, Signal, QThread
    
    
    class ThreadBridge(QObject):
    
        done = Signal()
    
        def do_something(self):
            print('doing something!')
            self.done.emit()
    
    
    class Controller(QObject):
    
        some_request = Signal()
    
        def create(self, main_thread):
            bridge = ThreadBridge()
            bridge.moveToThread(main_thread)
    
            self.some_request.connect(bridge.do_something)
            bridge.done.connect(self.finish)
    
            self.some_request.emit()  # nothing happens
    
        def finish(self):
            print('success')
    
    
    class MainWindow(QMainWindow):
        init_controller_signal = Signal(object)
    
        def __init__(self):
            super().__init__()
    
            self.controller_thread = QThread()
            self.controller_thread.start()
    
            self.controller = Controller()
            self.init_controller_signal.connect(self.controller.create)
            self.controller.moveToThread(self.controller_thread)
            self.init_controller_signal.emit(self.thread())
    
    
    if __name__ == "__main__":
        app = QApplication()
        mw = MainWindow()
        mw.show()
        sys.exit(app.exec_())
    
    

    Help appreciated!

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

    @nutrx said in Explicitly running a procedure in a different QThread:

    def create(self, main_thread):
    bridge = ThreadBridge()

    bridge is a local variable and is destroyed as soon as create() method terminates...

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

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

      Hi,

      bridge is only valid for the lifetime of the create method so it's likely destroyed before it can even start to do something.

      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
      • N Offline
        N Offline
        nutrx
        wrote on last edited by nutrx
        #4

        oh of course, classic I guess, thank you very much, I was actually stuck on that. One more thing that's closely related to this: in the following code I passed the controller itself, so the bridge can set an attribute after completing computation. Assuming I only have these two threads, would this be considered bad design?

        import sys
        import time
        import random
        
        from PySide2.QtWidgets import QMainWindow, QApplication
        from PySide2.QtCore import QObject, Signal, QThread
        
        
        class ThreadBridge(QObject):
        
            def do_something(self, obj):
                obj.tmp_data_ref = random.random()
        
        
        class Controller(QObject):
        
            some_request = Signal(object)
        
            def __init__(self, main_thread):
                super().__init__()
                self.tmp_data_ref = None
                self.bridge = ThreadBridge()
                self.bridge.moveToThread(main_thread)
        
            def create(self):
                self.tmp_data_ref = None
        
                self.some_request.connect(self.bridge.do_something)
                self.some_request.emit(self)
        
                while self.tmp_data_ref is None:
                    time.sleep(0.001)
        
                data = self.tmp_data_ref
                print('success!', data)
        
        
        class MainWindow(QMainWindow):
            init_controller_signal = Signal()
        
            def __init__(self):
                super().__init__()
        
                self.controller_thread = QThread()
                self.controller_thread.start()
        
                self.controller = Controller(self.thread())
                self.init_controller_signal.connect(self.controller.create)
                self.controller.moveToThread(self.controller_thread)
                self.init_controller_signal.emit()
        
        
        if __name__ == "__main__":
            app = QApplication()
            mw = MainWindow()
            mw.show()
            sys.exit(app.exec_())
        
        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #5

          Yes it is for several reasons:

          1. you modify a GUI object from a different thread which is something you should not do.
          2. you create a useless tight coupling between your worker object and your MainWindow. The worker object does not need to know anything about the use of the data it might generate.

          Clean separation of responsibilities allows you to more easily maintain your software.

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

          N 1 Reply Last reply
          1
          • SGaistS SGaist

            Yes it is for several reasons:

            1. you modify a GUI object from a different thread which is something you should not do.
            2. you create a useless tight coupling between your worker object and your MainWindow. The worker object does not need to know anything about the use of the data it might generate.

            Clean separation of responsibilities allows you to more easily maintain your software.

            N Offline
            N Offline
            nutrx
            wrote on last edited by
            #6

            @SGaist

            1. where do I modify GUI? I only modify the controller, no?
            2. I think I don't really understand. Why do I have mixed resposibilities there? In my actual application I am trying to use this to make an object (like the controller) in the separate thread wait until the initialization of a GUI element that it triggered finished in the main thread, to then start a computation.
            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #7
              1. My bad ! The code scrolling made me misread what object you were modifying from your thread.
              2. Following your explanation of your goal, the idea of the separation of responsibility would be that your GUI should use a signal to let whoever is interested in that information know that it is ready. You would connect your controller to that signal so it can trigger the work you want to do.

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

              N 1 Reply Last reply
              0
              • SGaistS SGaist
                1. My bad ! The code scrolling made me misread what object you were modifying from your thread.
                2. Following your explanation of your goal, the idea of the separation of responsibility would be that your GUI should use a signal to let whoever is interested in that information know that it is ready. You would connect your controller to that signal so it can trigger the work you want to do.
                N Offline
                N Offline
                nutrx
                wrote on last edited by
                #8

                @SGaist Ah yes, that's how I implemented it most of the time. The reason I need something like this a few times is that it happens in an API method that is supposed to return data that can only be computed (by the controller in this case) after the corresponding GUI element has been initialized in the main thread (or, in the example above, after the ThreadBridge computed some data in the main thread). Since this is probably not an uncommon issue, is there a better solution to this?
                It's not that the controller depends on the GUI! It's the other way around, but I need to make sure the controller waits until the GUI is ready.

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

                  You can initialize everything and then you can use a single shot 0 delay QTimer to trigger the start of your controller. This will be done a soon as the event loop start in that case.

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

                  N 1 Reply Last reply
                  0
                  • SGaistS SGaist

                    You can initialize everything and then you can use a single shot 0 delay QTimer to trigger the start of your controller. This will be done a soon as the event loop start in that case.

                    N Offline
                    N Offline
                    nutrx
                    wrote on last edited by
                    #10

                    @SGaist apart from waiting for the thread's event loop to start, what would be the difference to just using signals and slots? This wouldn't solve the issue of waiting for initialized GUI to then (in the same method) start a computation whose result can then be returned, I guess

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

                      Do you mean the difference between the zero delay QTimer and using signals and slots ?

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

                      N 1 Reply Last reply
                      0
                      • SGaistS SGaist

                        Do you mean the difference between the zero delay QTimer and using signals and slots ?

                        N Offline
                        N Offline
                        nutrx
                        wrote on last edited by nutrx
                        #12

                        @SGaist yes, at least in case I have this requirement of waiting for the GUI to be fully initialized while the API method is executed, to then safely start the computation and return the results

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

                          Oh I see, I did not get the while part.

                          Just to sum up a bit so we are on the same line:

                          • you have API calls that are done while also building your GUI
                          • the result of that call shall trigger further widget creation
                          • these widget creation cannot happen before your main widget is fully initialized

                          Is that all correct ?
                          Will you have other widgets created after that ?

                          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 Extensions
                          • Unsolved