Question

In the GUI application I'm working on I have a number of custom (composite) widgets that I've created, with each widget having an update_gui function where I access my SQLite database so I can fill in text areas, set checkbox status, etc

I constantly run into the problem of events being fired when the gui_update code is run, for example when I update the state of a checkbox and the change event for the checkbox is fired

What can I do to avoid this problem? (Or to work around it? Do I need to rethink my whole approach?) I don't want the event handling code to be run when the state of the checkbox (or other widget) is updated programmatically

One approach that I've tried is to handle the click or mousedown event (or keyboard for text) and use that instead of the event that is fired when the checkbox changes state. The problem with this approach is that there may be other ways to the change the state (for example navigating to the checkbox with the keyboard and pressing space)

I am currently working with Qt (with the PyQt5 bindings) but I've had the same problem on another platform so the problem seems more general, though perhaps the solution could be specific for Qt

Grateful for help and with kind regards, Tord

Était-ce utile?

La solution 2

An alternative to ymoreau's suggestion of using QSignalBlocker is to set a flag inside the composite widget

Example code in Python:

class CompositeWidget(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()
        self.gui_update_in_progress_bl = False

        self.widget_one = QtWidgets.QListWidget()
        self.widget_one.itemSelectionChanged.connect(
            self.on_widget_selection_changed)

    def on_widget_selection_changed(self):
        if self.gui_update_in_progress_bl:
            return

        # - writing to database/model here -

    def update_gui(self):
        self.gui_update_in_progress_bl = True

        # - update of widget_one here -

        self.gui_update_in_progress_bl = False

This should work in general as well I think, the only thing I'm worried about is if the signal that is sent when widget_one is updated is not processed right away, but put in a queue (maybe because of threading) and instead is processed after the end of the update_gui method. (But I'm guessing this is a case that I only need to worry about if I use multi-threading inside Qt, please comment if you know more!)

An advantage with this approach is that I can choose myself which of the signal handlers to exit (e.g. on_widget_selection_changed in the example above)

Autres conseils

As said in the comment you can use QSignalBlocker to prevent the events to be fired.
Down side : there is no way to choose what to block, in case you need some other signals from the same object.

Sometimes you also have an equivalent signal that is not fired when the value is changed programmatically. Like QLineEdit::textChanged (always fired) and QLineEdit::textEdited (only fired by a user edit).
It is better when you do not have control over the emitter (thus cannot block the signals).

Finally, depending on your architecture you can also fill all your widgets during the initialization of your GUI and only connect the signals to the slots after that.

But, for example if you have a form widget that is changing depending on a combobox value, you want the combobox to emit the signal and set up the form widget, whether the change was made by the user or by the program.
Then I believe you should try to have a data-model that can handle those programmatical changes (check that the value is the same as the database and ignore it for example) and not block the signals. Because if a part of the code rely on those signals in the future, you could end with weird behaviour not always easy to spot.

Licencié sous: CC-BY-SA avec attribution
scroll top