Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt Signal Slot Architecture Unwanted Infinite Loop

I've problem with qt signal-slot system.

First I've created a class which is called System in Singleton pattern, so I can access it's instance where I want. System has a signal SelectionChanged.

I've a list widget and I am connecting it's itemSelectionChanged signal to my custom slot which is called onSelectionChanged. In onSelectionChanged slot, I am emitting System's SelectionChanged signal. There is no problem yet.

In my software design, a selection of object(s) can be used by many GUI widgets or custom classes and System's SelectionChanged signal can be emited by widgets other then the list widget.

So I am creating a slot called OnSystemSelectionChanged in the list widget then connect it to the System's SelectionChanged signal. The OnSystemSelectionChangedSlot is like this.

void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
    if (sender == this) return;
    // Then I want to get a list of selected objects and set them as selection of this widget like this:
    this->SetSelection(System::Instance()->GetSelectedObjects());
}

But the problem is when I start to set the list widget's selected items, it is going to emit itemSelectionChanged signal and my onSelectionChanged slot will be called. Then the slot will emit System's SelectionChanged signal and then OnSystemSelectionChanged will be called too. It will stop through sender parameter but there is no method for setting list widget's selected items at once.

How can I figure this problem out.

I hope I did explain my problem well. Thanks in advance.

Edit: Spelling and grammer errors are corrected.

like image 398
Cahit Burak Küçüksütcü Avatar asked Dec 23 '13 12:12

Cahit Burak Küçüksütcü


1 Answers

There are a few ways of dealing with this in Qt.

Idioms

  1. Use multiple views with one underlying model. This handles propagation of changes to multiple view controls automatically and you don't need to do anything extra. You can use QDataWidgetMapper to link "plain old" widgets to the data elements in a model. I'd say that this should be the preferred way of doing things. Having an underlying model for all of your UI is a step in the direction of good software design anyway.

  2. When propagating changes between data models, implement both a DisplayRole and an EditRole. The views will nominally modify the models using one of the roles (say, the EditRole), while you can, programmatically, modify the models using the other role (say, the DisplayRole). You handle the dataChanged signals from the model in your own slot, properly dealing with the roles, and call setData on the other models with the other role. This prevents the loops.

  3. For controls that are not QAbstractItemViews, implement two signals: one emitted on any change, another one emitted only on changes based on keyboard/mouse input. This is the interface exposed by QAbstractButton, for example: the toggled(bool) signal is the former, the clicked() is the latter. You then only connect to the input-based signals.

    Your own code must propagate programmatic changes to all the interlinked controls, since changing one control from your code won't modify the others. This should not be a problem, since well designed code should encapsulate the implementation details of UI controls from rest of the code. Your dialog/window class will thus expose its properties in a way that's not coupled to the number of controls showing a particular property.

Hackish Let's-Hope-They-Won't-Become Idioms

  1. Use a flag inhibiting signal emission (Bartosz's answer).

  2. Break the signal/slot connections for the duration of the change (Bartosz's answer).

  3. Use QObject::blockSignals().

like image 76
Kuba hasn't forgotten Monica Avatar answered Sep 23 '22 02:09

Kuba hasn't forgotten Monica