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. More elegant design pattern than ignoring signals?
Forum Updated to NodeBB v4.3 + New Features

More elegant design pattern than ignoring signals?

Scheduled Pinned Locked Moved Solved General and Desktop
7 Posts 4 Posters 523 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.
  • E Offline
    E Offline
    elveatles
    wrote on last edited by
    #1

    Hi,
    I was wondering if anyone knows of a better way to handle situations where I would normally ignore signals.
    Let me give an example: I'm making a CollapsibleWidget that shows/hides content. Here is Python code, but it's just as applicable to C++.

    from Qt import QtWidgets
    
    class CollapsibleWidget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(CollapsibleWidget, self).__init__(self, parent=parent)
    
            self._is_toggling = False
    
            self.toggle_check_box = QtWidgets.QCheckBox()
            self.content_widget = QtWidgets.QWidget()
            self.setLayout(QtWidgets.QVBoxLayout())
            self.layout().addWidget(self.toggle_check_box)
            self.layout().addWidget(self.content_widget)
    
            self.toggle_check_box.stateChanged.connect(self._on_toggle)
    
        def is_collapsed(self):
            return self.toggle_check_box.isChecked()
    
        def set_collapsed(self, value):
            self._is_toggling = True
            self.toggle_check_box.setChecked(value)
            self.content_widget.setVisible(not value)
            self._is_toggling = False
    
        def _on_toggle(self, state):
            if self._is_toggling:
                return
    
            self.set_collapsed(self.toggle_check_box.isChecked())
    

    The widget can be expanded or collapsed by API or from the GUI. Because it can be set via API, this is why self.toggle_check_box.setChecked(value) is needed in set_collapsed.
    Without self._is_toggling, the _on_toggle method would be called, which calls set_collapsed, which calls self.toggle_check_box.setChecked, which emits the stateChanged signal, which calls _on_toggle, and around and around it goes.
    Is there a design pattern that would be better than having to add a boolean flag or call blockSignals any time this situation comes up?

    1 Reply Last reply
    0
    • E elveatles

      @SGaist @fcarney I suppose that's true. I was trying to show a very simple case, but could this be applied to something more complicated?
      For example, a property editor. There would be some model object and a PropertyEditor widget which would have a setModel method.
      If the model were to change, it would emit valueChanged signals, which the PropertyEditor has slots for and updates the widgets that display the model values.
      If a user edits the widgets in the PropertyEditor, then these emit widgetChanged signals which updates the values in the model.
      So you can see how this can be a loop of signals and slots.

      SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #5

      @elveatles typically, the widget/model/whatever that receives the new value starts by checking it against the current and if they are the same stops there. In the other case, the internal value is updated and the notification signal is emitted.

      That's the standard technique and also why you do not get signal storms when you connect for example a QSlider to a QSpinBox and vice versa. Each will trigger the update to the other but will halt after one round trip.

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

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

        Hi,

        Shouldn't the API trigger the toggle_check_box change and it will thus trigger the rest of the chain ?

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

        E 1 Reply Last reply
        1
        • fcarneyF Offline
          fcarneyF Offline
          fcarney
          wrote on last edited by
          #3

          @elveatles said in More elegant design pattern than ignoring signals?:

          self.toggle_check_box.setChecked(value)

          Why are you call this? Isn't the check box already in the state you set it to?

          from Qt import QtWidgets
          
          class CollapsibleWidget(QtWidgets.QWidget):
              def __init__(self, parent=None):
                  super(CollapsibleWidget, self).__init__(self, parent=parent)
          
                  #self._is_toggling = False
          
                  self.toggle_check_box = QtWidgets.QCheckBox()
                  self.content_widget = QtWidgets.QWidget()
                  self.setLayout(QtWidgets.QVBoxLayout())
                  self.layout().addWidget(self.toggle_check_box)
                  self.layout().addWidget(self.content_widget)
          
                  self.toggle_check_box.stateChanged.connect(self._on_toggle)
          
              def is_collapsed(self):
                  return self.toggle_check_box.isChecked()
          
              def _on_toggle(self, value):
                  #self._is_toggling = True
                  #self.toggle_check_box.setChecked(value)
                  self.content_widget.setVisible(not value)
                  #self._is_toggling = False
          
              # I assume you want to call this externally for some reason?
              def set_collapsed(self, state):
                  self.toggle_check_box.setChecked(state)
          

          C++ is a perfectly valid school of magic.

          1 Reply Last reply
          0
          • SGaistS SGaist

            Hi,

            Shouldn't the API trigger the toggle_check_box change and it will thus trigger the rest of the chain ?

            E Offline
            E Offline
            elveatles
            wrote on last edited by elveatles
            #4

            @SGaist @fcarney I suppose that's true. I was trying to show a very simple case, but could this be applied to something more complicated?
            For example, a property editor. There would be some model object and a PropertyEditor widget which would have a setModel method.
            If the model were to change, it would emit valueChanged signals, which the PropertyEditor has slots for and updates the widgets that display the model values.
            If a user edits the widgets in the PropertyEditor, then these emit widgetChanged signals which updates the values in the model.
            So you can see how this can be a loop of signals and slots.

            SGaistS JonBJ 2 Replies Last reply
            0
            • E elveatles

              @SGaist @fcarney I suppose that's true. I was trying to show a very simple case, but could this be applied to something more complicated?
              For example, a property editor. There would be some model object and a PropertyEditor widget which would have a setModel method.
              If the model were to change, it would emit valueChanged signals, which the PropertyEditor has slots for and updates the widgets that display the model values.
              If a user edits the widgets in the PropertyEditor, then these emit widgetChanged signals which updates the values in the model.
              So you can see how this can be a loop of signals and slots.

              SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #5

              @elveatles typically, the widget/model/whatever that receives the new value starts by checking it against the current and if they are the same stops there. In the other case, the internal value is updated and the notification signal is emitted.

              That's the standard technique and also why you do not get signal storms when you connect for example a QSlider to a QSpinBox and vice versa. Each will trigger the update to the other but will halt after one round trip.

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

              E 1 Reply Last reply
              2
              • SGaistS SGaist

                @elveatles typically, the widget/model/whatever that receives the new value starts by checking it against the current and if they are the same stops there. In the other case, the internal value is updated and the notification signal is emitted.

                That's the standard technique and also why you do not get signal storms when you connect for example a QSlider to a QSpinBox and vice versa. Each will trigger the update to the other but will halt after one round trip.

                E Offline
                E Offline
                elveatles
                wrote on last edited by
                #6

                @SGaist Thanks for your thoughts. And I like the term signal storm

                1 Reply Last reply
                0
                • E elveatles

                  @SGaist @fcarney I suppose that's true. I was trying to show a very simple case, but could this be applied to something more complicated?
                  For example, a property editor. There would be some model object and a PropertyEditor widget which would have a setModel method.
                  If the model were to change, it would emit valueChanged signals, which the PropertyEditor has slots for and updates the widgets that display the model values.
                  If a user edits the widgets in the PropertyEditor, then these emit widgetChanged signals which updates the values in the model.
                  So you can see how this can be a loop of signals and slots.

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

                  @elveatles
                  I wondered about this in the past. What @SGaist has said is the key: only emit ...Changed signals when a value/property changes from its current value, not just gets updated (to the same value).

                  The QDataWidgetMapper Class is a two-way mapper between widgets and values: change the value and the widget changes, change the widget and the value changes. This exemplifies the behaviour of the "only emit signal on change", else it would ping-pong as you talk about.

                  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