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
Forum Updated to NodeBB v4.3 + New Features

Explicitly running a procedure in a different QThread

Scheduled Pinned Locked Moved Unsolved Qt for Python
13 Posts 3 Posters 914 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 Offline
    N Offline
    nutrx
    wrote on last edited by
    #1

    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 1 Reply Last reply
    0
    • 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