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

QThread vs QRunnable

Scheduled Pinned Locked Moved Solved Qt for Python
14 Posts 2 Posters 6.0k 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.
  • B Offline
    B Offline
    borisruna
    wrote on 16 Jan 2022, 09:38 last edited by
    #1

    Hello everyone!
    Reading about QRunnable, I understand that they don't support signals/slots. However I found this https://www.pythonguis.com/tutorials/multithreading-pyqt-applications-qthreadpool/, where the author creates a custom WorkerSignals object that inherits from QObject and defines the signals there.
    Is it ok to use this pattern also for a QThreadPool, so that, in a module you define the ThreadPool and the Workers and communicate with the mainprocess through QThreadPool's signals?
    I.e. a QThreadPool would be created and initialized in the main thread, it will create a Worker (QRunnable) for every task needed, the Worker will emit a signal on finish that will be connected to a QThreadPool method, that in turn emit a signal through its own custom ThreadPoolSignal (QObject). This way the main thread will be notified by the QThreadPool that was created in the first step.
    Notifying the MainWindow results in that all UI components will be notified only through MainWindow that is acting as a Controller.
    An example would be the following:

    import sys
    from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot
    from PyQt5.QtWidgets import QApplication, QMainWindow
    
    
    class WorkerSignals(QObject):
        result = pyqtSignal(str)
    
    
    class Worker(QRunnable):
        def __init__(self):
            super().__init__()
            self.signals = WorkerSignals()
    
        @pyqtSlot()
        def run(self):
            self.signals.result.emit('test')
    
    
    class PoolSignals(QObject):
        result = pyqtSignal(str)
    
    
    class ThreadPool(QThreadPool):
        def __init__(self):
            super().__init__()
            self.signal = PoolSignals()
    
        def spawn_workers(self):
            worker = Worker()
            worker.signals.result.connect(self.emit_worker_output)
            self.start(worker)
    
        def emit_worker_output(self, output):
            self.signal.result.emit(output)
    
    
    class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()
            self.thread_pool = ThreadPool()
            self.register_actions()
            self.do_work()
    
        def register_actions(self):
            self.thread_pool.signal.result.connect(self.print_output)
    
        def do_work(self):
            for i in range(0, 10):
                self.thread_pool.spawn_workers()
    
        @pyqtSlot(str)
        def print_output(self, text):
            print(text)
    
    
    def main():
        app = QApplication(sys.argv)
        main_window = MainWindow()
        sys.exit(app.exec_())
    
    
    if __name__ == '__main__':
        main()
    

    Is this pattern acceptable, or an overkill and I should move to QThreads that natively support signals?

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 16 Jan 2022, 19:28 last edited by
      #2

      Hi,

      What kind of work are your QRunnable going 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

      1 Reply Last reply
      0
      • B Offline
        B Offline
        borisruna
        wrote on 17 Jan 2022, 04:49 last edited by
        #3

        The user should be able to capture screenshots from the GUI. Every time a screenshot is captured, a QRunnable should be spawned that classifies the image using Tensorflow and returns the result to the user in a QListWidget.

        1 Reply Last reply
        0
        • S Offline
          S Offline
          SGaist
          Lifetime Qt Champion
          wrote on 17 Jan 2022, 09:04 last edited by
          #4

          Sounds like QtConcurrent::run already provides what you need with the help of QFutureWatcher

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

          B 1 Reply Last reply 17 Jan 2022, 16:37
          2
          • S SGaist
            17 Jan 2022, 09:04

            Sounds like QtConcurrent::run already provides what you need with the help of QFutureWatcher

            B Offline
            B Offline
            borisruna
            wrote on 17 Jan 2022, 16:37 last edited by
            #5

            @SGaist Sorry, I cant find anything related to QtConcurrent in pyqt / pyside. Am I missing something ? Thank you !

            1 Reply Last reply
            0
            • S Offline
              S Offline
              SGaist
              Lifetime Qt Champion
              wrote on 17 Jan 2022, 18:49 last edited by SGaist
              #6

              Sorry, my bad, at some point I lost the fact that you are coding in Python. So no, you did not miss anything.

              See this stack overflow answer. There's a link for a Python implementation in the comments that could be of use to you.

              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
              1
              • B Offline
                B Offline
                borisruna
                wrote on 17 Jan 2022, 18:57 last edited by
                #7

                Thank you very much !
                You think I should go with https://gist.github.com/ales-erjavec/5ef43c5b3907a9e4924eec09bfb9d9c6, than by QThreadPool using the implementation I posted in my initial post?
                Also what about QThread? Is there any implementation I could use to limit the maximum threads being used to reproduce somehow the functionality of QThreadPool maybe? Or is it safe just use QThreads ?

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 17 Jan 2022, 19:19 last edited by
                  #8

                  For your use case, QThreadPool would be more indicated as it will do the QThread handling for you (i.e. queue your tasks if you start more of them than available threads).

                  However, since you are using signals and slots for these tasks, I would recommend you to create your own class for handling the results rather than customizing QThreadPool so you keep a clean separation of concerns.

                  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
                  • B Offline
                    B Offline
                    borisruna
                    wrote on 17 Jan 2022, 19:40 last edited by borisruna
                    #9

                    This class handling the events, should support signaling though, right?
                    From what I understand this part self.signal = PoolSignals() is what you don't like in my ThreadPool, yes? However,I would need a class likePoolSignals to handle the events and signal the main thread, correct?

                    Please forgive my ignorance!

                    1 Reply Last reply
                    0
                    • S Offline
                      S Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on 17 Jan 2022, 19:56 last edited by
                      #10

                      If you want to be really clean, you don't use an external object to generate signals. Signals shall be emitted from within the class that they are declared in.

                      For example in your case, the task class should inherit from QObject and QRunnable. Then you can directly declare/emit the signals.

                      There's no need for your ThreadPoolSignal object as QThreadPool is derived from QObject.

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

                      B 1 Reply Last reply 17 Jan 2022, 20:54
                      0
                      • B Offline
                        B Offline
                        borisruna
                        wrote on 17 Jan 2022, 20:44 last edited by
                        #11

                        Ah very nice ! Let me test this

                        1 Reply Last reply
                        0
                        • S SGaist
                          17 Jan 2022, 19:56

                          If you want to be really clean, you don't use an external object to generate signals. Signals shall be emitted from within the class that they are declared in.

                          For example in your case, the task class should inherit from QObject and QRunnable. Then you can directly declare/emit the signals.

                          There's no need for your ThreadPoolSignal object as QThreadPool is derived from QObject.

                          B Offline
                          B Offline
                          borisruna
                          wrote on 17 Jan 2022, 20:54 last edited by
                          #12

                          @SGaist said in QThread vs QRunnable:

                          If you want to be really clean, you don't use an external object to generate signals. Signals shall be emitted from within the class that they are declared in.

                          For example in your case, the task class should inherit from QObject and QRunnable. Then you can directly declare/emit the signals.

                          There's no need for your ThreadPoolSignal object as QThreadPool is derived from QObject.

                          I used the following :

                          import sys
                          from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot
                          from PyQt5.QtWidgets import QApplication, QMainWindow
                          
                          
                          class WorkerSignals(QObject):
                              result = pyqtSignal(str)
                          
                          
                          class Worker(QRunnable):
                              def __init__(self):
                                  super().__init__()
                                  self.signals = WorkerSignals()
                          
                              @pyqtSlot()
                              def run(self):
                                  self.signals.result.emit('test')
                          
                          
                          
                          class ThreadPool(QThreadPool):
                              result = pyqtSignal(str)
                          
                              def __init__(self):
                                  super().__init__()
                          
                              def spawn_workers(self):
                                  worker = Worker()
                                  worker.signals.result.connect(self.emit_worker_output)
                                  self.start(worker)
                          
                              def emit_worker_output(self, output):
                                  self.result.emit(output)
                          
                          
                          class MainWindow(QMainWindow):
                              def __init__(self):
                                  super().__init__()
                                  self.thread_pool = ThreadPool()
                                  self.register_actions()
                                  self.do_work()
                          
                              def register_actions(self):
                                  self.thread_pool.result.connect(self.print_output)
                          
                              def do_work(self):
                                  for i in range(0, 10):
                                      self.thread_pool.spawn_workers()
                          
                              @pyqtSlot(str)
                              def print_output(self, text):
                                  print(text)
                          
                          
                          def main():
                              app = QApplication(sys.argv)
                              main_window = MainWindow()
                              sys.exit(app.exec_())
                          
                          
                          if __name__ == '__main__':
                              main()
                          

                          which works great! I didn't use multiple inheritance for now as this makes me feel a bit nervous!
                          If you be so kind and let me know what you think of the code above, would be greatly appreciated!

                          1 Reply Last reply
                          1
                          • S Offline
                            S Offline
                            SGaist
                            Lifetime Qt Champion
                            wrote on 17 Jan 2022, 21:20 last edited by
                            #13

                            You don't need to be nervous, multiple inheritance is supported in Python as well. The only thing you have to take into account is that QObject must be the first in the list.

                            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
                            • B Offline
                              B Offline
                              borisruna
                              wrote on 17 Jan 2022, 21:36 last edited by
                              #14

                              Much appreciated @SGaist !

                              1 Reply Last reply
                              0

                              5/14

                              17 Jan 2022, 16:37

                              topic:navigator.unread, 9
                              • Login

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