Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Blocking Signals to child widgets
Forum Updated to NodeBB v4.3 + New Features

Blocking Signals to child widgets

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 5 Posters 2.5k Views 3 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.
  • S Offline
    S Offline
    Strangelove
    wrote on last edited by Strangelove
    #1

    In this post, I read: "If you have to block signals then it usually means your design is somehow screwed up."

    I'm trying to get my mind around the following scenario:

    When the user selects an item with the mouse, I show a floating inspector widget with attributes of the item, which can be edited. The inspector is just a subclass of QGroupBox with some QSpinBox and QLineEdit in it.

    The widgets are connected to a method of the inspector, e.g.: self._edit_distance.valueChanged.connect(self._value_changed).

    Since there are many widgets in the inspector, I didn't want to connect each and every one of them to a separate method. I figured I could update the entire source object when one of the values in the inspector changes. So all the widgets are connected to the same self._value_changed method.

    In that method, I update the object with the values from the widgets, like this:

    self.gridpoint.source.distance = self._edit_distance.value()
    self.gridpoint.source.someothervalue = self._edit_someothervalue.value()
    

    I am overriding the inspector's show() method, which updates the inspector widgets to show the values of the selected item (source), like this:

    self._edit_distance.setValue(source.distance)
    self._edit_someothervalue.setValue(source.someothervalue)
    

    Problem is, when the inspector is shown and all its values are set, valueChanged is emitted for every widget, which triggers the self._value_changed method, which in turn sets the value of yet-undrawn source attributes to zero. The object which is supposed to be modified through the inspector gets modified by showing the values to be modified, not by user interaction.

    I thought of using blockSignals but apparently it's impossible to block a group of widgets. And anyway, this seems indeed to be a design problem on my end. But this is my first semi-complex GUI and I'm not sure what the correct design pattern should be here.

    I am working in Python but I can translate C++ if it's simple enough, anyway this is an abstract question.

    Thank you for your help.

    JonBJ 1 Reply Last reply
    0
    • S Strangelove

      In this post, I read: "If you have to block signals then it usually means your design is somehow screwed up."

      I'm trying to get my mind around the following scenario:

      When the user selects an item with the mouse, I show a floating inspector widget with attributes of the item, which can be edited. The inspector is just a subclass of QGroupBox with some QSpinBox and QLineEdit in it.

      The widgets are connected to a method of the inspector, e.g.: self._edit_distance.valueChanged.connect(self._value_changed).

      Since there are many widgets in the inspector, I didn't want to connect each and every one of them to a separate method. I figured I could update the entire source object when one of the values in the inspector changes. So all the widgets are connected to the same self._value_changed method.

      In that method, I update the object with the values from the widgets, like this:

      self.gridpoint.source.distance = self._edit_distance.value()
      self.gridpoint.source.someothervalue = self._edit_someothervalue.value()
      

      I am overriding the inspector's show() method, which updates the inspector widgets to show the values of the selected item (source), like this:

      self._edit_distance.setValue(source.distance)
      self._edit_someothervalue.setValue(source.someothervalue)
      

      Problem is, when the inspector is shown and all its values are set, valueChanged is emitted for every widget, which triggers the self._value_changed method, which in turn sets the value of yet-undrawn source attributes to zero. The object which is supposed to be modified through the inspector gets modified by showing the values to be modified, not by user interaction.

      I thought of using blockSignals but apparently it's impossible to block a group of widgets. And anyway, this seems indeed to be a design problem on my end. But this is my first semi-complex GUI and I'm not sure what the correct design pattern should be here.

      I am working in Python but I can translate C++ if it's simple enough, anyway this is an abstract question.

      Thank you for your help.

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

      @Strangelove said in Blocking Signals to child widgets:

      Problem is, when the inspector is shown and all its values are set, valueChanged is emitted for every widget

      I am not sure I fully understand what you are saying, but....

      I never use blockSignals(), believing it to be "semi-evil" because (a) it applies globally to all widgets and (b) I am never sure of the consequences of blocking signals which should be emitted.

      When I need to update all the widgets --- such as loading a model, or presumably your initial showing of all widgets --- I set a class variable like modelBeingLoaded = true;, load all data, call some refreshAll() at the end, finally reset modelBeingLoaded = false;. While modelBeingLoaded == true I do not spawn extra signals or do extra work/redrawing etc. If you like, it's kind of a blockSignals(), but implemented by me.

      Does this apply to your situation?

      S jeremy_kJ 2 Replies Last reply
      1
      • JonBJ JonB

        @Strangelove said in Blocking Signals to child widgets:

        Problem is, when the inspector is shown and all its values are set, valueChanged is emitted for every widget

        I am not sure I fully understand what you are saying, but....

        I never use blockSignals(), believing it to be "semi-evil" because (a) it applies globally to all widgets and (b) I am never sure of the consequences of blocking signals which should be emitted.

        When I need to update all the widgets --- such as loading a model, or presumably your initial showing of all widgets --- I set a class variable like modelBeingLoaded = true;, load all data, call some refreshAll() at the end, finally reset modelBeingLoaded = false;. While modelBeingLoaded == true I do not spawn extra signals or do extra work/redrawing etc. If you like, it's kind of a blockSignals(), but implemented by me.

        Does this apply to your situation?

        S Offline
        S Offline
        Strangelove
        wrote on last edited by Strangelove
        #3

        @JonB Thank you. I could certainly try and do did what you suggest, which should apply certainly applies in my case. Since I'm still learning all this stuff, I'm just curious if this would be considered a hack or not... I'm trying to figure out what the best practices are (since this is a very common scenario with GUI applications).

        S 1 Reply Last reply
        0
        • JonBJ JonB

          @Strangelove said in Blocking Signals to child widgets:

          Problem is, when the inspector is shown and all its values are set, valueChanged is emitted for every widget

          I am not sure I fully understand what you are saying, but....

          I never use blockSignals(), believing it to be "semi-evil" because (a) it applies globally to all widgets and (b) I am never sure of the consequences of blocking signals which should be emitted.

          When I need to update all the widgets --- such as loading a model, or presumably your initial showing of all widgets --- I set a class variable like modelBeingLoaded = true;, load all data, call some refreshAll() at the end, finally reset modelBeingLoaded = false;. While modelBeingLoaded == true I do not spawn extra signals or do extra work/redrawing etc. If you like, it's kind of a blockSignals(), but implemented by me.

          Does this apply to your situation?

          jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by
          #4

          @JonB said in Blocking Signals to child widgets:

          I never use blockSignals(), believing it to be "semi-evil" because (a) it applies globally to all widgets and (b) I am never sure of the consequences of blocking signals which should be emitted.

          I'm note sure what globally means here.
          QObject::blockSignals(true) will prevent this or self object from emitting signals other than QObject::destroyed. It shouldn't interfere with signals originating from other QObject instances.

          I agree that using it can be problematic without complete knowledge of all slots that are connected.

          Asking a question about code? http://eel.is/iso-c++/testcase/

          JonBJ 1 Reply Last reply
          0
          • jeremy_kJ jeremy_k

            @JonB said in Blocking Signals to child widgets:

            I never use blockSignals(), believing it to be "semi-evil" because (a) it applies globally to all widgets and (b) I am never sure of the consequences of blocking signals which should be emitted.

            I'm note sure what globally means here.
            QObject::blockSignals(true) will prevent this or self object from emitting signals other than QObject::destroyed. It shouldn't interfere with signals originating from other QObject instances.

            I agree that using it can be problematic without complete knowledge of all slots that are connected.

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

            @jeremy_k
            By "globally" I mean it blocks all signals from the object. I'm never sure of what the consequences of that might be, there might be some signals (either my own slots or even internally-handled Qt ones) which really need to be handled/acted upon and this will prevent that happening.

            1 Reply Last reply
            0
            • S Strangelove

              @JonB Thank you. I could certainly try and do did what you suggest, which should apply certainly applies in my case. Since I'm still learning all this stuff, I'm just curious if this would be considered a hack or not... I'm trying to figure out what the best practices are (since this is a very common scenario with GUI applications).

              S Offline
              S Offline
              SimonSchroeder
              wrote on last edited by
              #6

              @Strangelove said in Blocking Signals to child widgets:

              I'm trying to figure out what the best practices are

              I would guess that connecting signals individually is considered best practice. However, I do know of cases where there could be a circular connection between two or more widgets. If you only initialize the widgets once you can do that before connecting any signals and you will have a lot less problems.

              @JonB's suggestion is a little bit better than using blockSignals(). In your specific case where there is a single method reacting to changes in any widget it makes sense to have a boolean variable which is checked and set at the beginning and unset at the end. Then, changes triggered within this method will not result in an endless loop. (This is the suggestion to use modelBeingLoaded.)

              One further possiblity (which we have in some places in our code) is to use a seperate wrapper class to block signals. This is, in our code, a class called SignalBlocker. The constructor(s) call blockSignals(true) on the object handed to it and the destructor calls blockSignals(false). In a addition to this operator->() is overloaded (this is C++, though), which allows for simple syntax:

              SignalBlocker(this->_edit_distance)->setValue(source.distance);
              SignalBlocker(this->_edit_someothervalue)->setValue(source.someothervalue);
              

              I am not sure how this could be translated to Python. Maybe it is possible to write an apply method which then allows a syntax similar to this?:

              SignalBlocker(self._edit_distance).apply(setValue, source.distance)
              SignalBlocker(self._edit_someothervalue).apply(setValue, source.someothervalue)
              
              jeremy_kJ 1 Reply Last reply
              0
              • S SimonSchroeder

                @Strangelove said in Blocking Signals to child widgets:

                I'm trying to figure out what the best practices are

                I would guess that connecting signals individually is considered best practice. However, I do know of cases where there could be a circular connection between two or more widgets. If you only initialize the widgets once you can do that before connecting any signals and you will have a lot less problems.

                @JonB's suggestion is a little bit better than using blockSignals(). In your specific case where there is a single method reacting to changes in any widget it makes sense to have a boolean variable which is checked and set at the beginning and unset at the end. Then, changes triggered within this method will not result in an endless loop. (This is the suggestion to use modelBeingLoaded.)

                One further possiblity (which we have in some places in our code) is to use a seperate wrapper class to block signals. This is, in our code, a class called SignalBlocker. The constructor(s) call blockSignals(true) on the object handed to it and the destructor calls blockSignals(false). In a addition to this operator->() is overloaded (this is C++, though), which allows for simple syntax:

                SignalBlocker(this->_edit_distance)->setValue(source.distance);
                SignalBlocker(this->_edit_someothervalue)->setValue(source.someothervalue);
                

                I am not sure how this could be translated to Python. Maybe it is possible to write an apply method which then allows a syntax similar to this?:

                SignalBlocker(self._edit_distance).apply(setValue, source.distance)
                SignalBlocker(self._edit_someothervalue).apply(setValue, source.someothervalue)
                
                jeremy_kJ Offline
                jeremy_kJ Offline
                jeremy_k
                wrote on last edited by
                #7

                @SimonSchroeder said in Blocking Signals to child widgets:

                One further possiblity (which we have in some places in our code) is to use a seperate wrapper class to block signals. This is, in our code, a class called SignalBlocker. The constructor(s) call
                blockSignals(true) on the object handed to it and the destructor calls blockSignals(false). In a addition to this operator->() is overloaded (this is C++, though), which allows for simple syntax:

                SignalBlocker(this->_edit_distance)->setValue(source.distance);
                SignalBlocker(this->_edit_someothervalue)->setValue(source.someothervalue);
                

                There's QSignalBlocker that accomplishes the same task with slightly different syntax.
                PyQt5's documentation mentions the class.

                Asking a question about code? http://eel.is/iso-c++/testcase/

                1 Reply Last reply
                1
                • M mpergand referenced this topic on
                • G Offline
                  G Offline
                  Grigory
                  Banned
                  wrote last edited by Grigory
                  #8
                  This post is deleted!
                  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