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. Pyqt Signals connecting multiple slots
Forum Updated to NodeBB v4.3 + New Features

Pyqt Signals connecting multiple slots

Scheduled Pinned Locked Moved Unsolved Qt for Python
6 Posts 3 Posters 1.0k 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.
  • D Offline
    D Offline
    Dwarrel
    wrote on last edited by
    #1

    I am working on a small program. I noticed significant slowdowns when connecting multiple functions to a single emit call. When I just have one or two functions connected the function call to emit the calls takes around 0.0004 seconds to finish. However when 8 functions are connected this drops to 0.37 seconds a significant time for the UI to be unresponsive.

    Am I using signals wrong and should you only connect one or two calls to them? It is something that can be solved with a function that merges the separate calls, I am just mostly interested in improving my understanding of how signals should be used.

    Kind regards,
    Dwarrel

    Quick test showed this:

    0 connected 0.0008
    1 connected 0.0004
    4 connected 0.148
    8 connected 0.37

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

      Hi and welcome to devnet,

      Did you benchmark these functions ?
      Are they all fast or some take more times than others ?
      Do they depend on some IOs ?
      Which version of PyQt ?
      On which OS ?

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

        Sorry I kept this more general because I assumed it was a general design question.
        I did not do a official benchmarking however this has been consistently noticeable. The numbers above are also consistent when manually testing the code.

        So the time it takes is dependant on the number of functions that have connected to the signal. When I have more "listeners" the "emitter" becomes slower.

        Following is snipped of code for the emitter (cycle_widget is a QTreeWidget, with less than 10 items, is is also slow with just 2 items). This treewidget is where we notice the slowdown, the highlighting of the selected cycle has a significant delay, which leads to bad user experience.

            def update_cycle_selection(self):
                self.selected_cycle_ids = list([item.id for item in self.cycle_selector_widget.selectedItems()])
                start = time.time()
                signals.plottab_updated_cycle_selection.emit(self.selected_cycle_ids)
                print(time.time()-start)
        

        We have 8 plots that are linked and should be updated when cycles are changed. When I reduce this to 1 the speed is significant better.

        signals.plottab_updated_cycle_selection.connect(self.force_time_plot.update_selection)
        signals.plottab_updated_cycle_selection.connect(self.force_extension_plot.update_selection)
        signals.plottab_updated_cycle_selection.connect(self.contour_time_plot.update_selection)
        

        "Do they depend on some IOs ?" Both emitter function and connected functions do not use IO. However, should that matter? I thought the entire idea of emitter and listeners is that a long lasting task in the listener is not interfering with the execution time of the emitter?

        PyQt: 6.7.1
        OS tested both Linux & Windows

        Kind regards,
        Tycho

        1 Reply Last reply
        0
        • JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #4

          I am not sure what you are expecting or finding here.

          When a signal is emitted, and both signal and slot are in the same thread, emitting a signal directly executes the connected slot(s). (Are you expecting this, or are you thinking that the slots will be executed "in the background/at a later time" while the emit returns immediately?) The time taken principally depends on what the slot does. There is some overhead for a signal but it is apparently "small" (there is some article on this I once found). If there are multiple slots attached to a signal then all of these slots must be executed. However, I would not expect the time taken would rise much just because you have multiple slots attached, other than the time in the slots themselves. And in the "common" case the slot content code probably takes much longer than the signal emitting code.

          However, we discovered in a recent-ish thread in this forum that the Python bindings can take surprisingly long just to call a function, which would be "instantaneous" in C++. The example there was calling something like QList.count() IIRC. That thread may have been for PySide, I cannot be sure whether it is similar for PyQt. The overhead has nothing to do with signals/slots per se, it's for calling any function from Python. That OP was calling it thousands or millions of times for it to become noticeable, but it did then become a "considerable" overhead.

          Your original quoted timings are far too short/quick to give any meaningful statistics. When something takes "0.0008" or "0.0004" (what units? you do not say. seconds??) that is way too small to rely on your timings. Try calling these functions hundreds or thousands of times to get more meaningful timings; even then I am not sure you can rely on accumulation of such a short time period.

          1 Reply Last reply
          1
          • D Offline
            D Offline
            Dwarrel
            wrote on last edited by
            #5

            "I am not sure what you are expecting or finding here."
            Well exactly what you gave me, thank you. My assumption was indeed that the emit would be independent of the execution time of a listener (connected function). Knowing that that is not the case completely explains the observed behaviour.
            I will optimize the connected functions and see if the connects are always needed.

            JonBJ 1 Reply Last reply
            0
            • D Dwarrel

              "I am not sure what you are expecting or finding here."
              Well exactly what you gave me, thank you. My assumption was indeed that the emit would be independent of the execution time of a listener (connected function). Knowing that that is not the case completely explains the observed behaviour.
              I will optimize the connected functions and see if the connects are always needed.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #6

              @Dwarrel said in Pyqt Signals connecting multiple slots:

              My assumption was indeed that the emit would be independent of the execution time of a listener (connected function). Knowing that that is not the case completely explains the observed behaviour.

              OIC!

              The behaviour depends on the parameters to connect() and whether the signalling object and the slot object are in the same or different threads.

              Assuming default --- no extra parameter to connect() ---

              • If they are in the same thread signal/slot is executed directly, upon emit signal call, i.e. it behaves just like a direct call to the slot(s). Slots are executed consecutively per the order they were connect()ed. Signaller code does not continue until all slots have completed.
              • If they are in different threads signal causes slot(s) to be executed queued. When slot object thread next enters Qt event loop slot(s) get executed. Order is actually undetermined. Signaller code continues immediately after emit, it does not wait.
              • Extra parameter to connect() can be used to alter default behaviour, see docs. You still should not try DirectConnection across threads, you could choose QueuedConnection if same thread. But non-default is unusual, unless you know what you are doing.

              P.S.
              I just noticed you are Python. the above is still correct, but there can be extra restrictions with multiple threads to do with Python GIL. But nothing special if all same thread.

              1 Reply Last reply
              1

              • Login

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