PDA

View Full Version : Slot function's concurrent multiple executions



MavenTux
13th February 2015, 19:35
Hi,

In my application's (running on Android) main window (QMainWindow) I have a push button (QPushButton) whose signal 'clicked()' has been connected to a slot. In that slot function I am displaying a QMessageBox::about(...). The issue is that when I press that button again and again (in a very short duration) I see the message box popping up more than once. Even before the user gets rid of the first message box the second one pops up. I understand that this is because of multiple concurrent executions of the slot function. How should I manage this situation in QT? What I want is that the user should not be able to initiate another clicked() signal till the first call to slot function returns.

Thanks

ChrisW67
13th February 2015, 20:27
On Windows and Linux the about() function gives you an application modal dialog. On OS X the dialog is not modal. On Android the docs do not say explicitly, but it looks like you have a non-modal dialog. If you do not want the button activated again then call setDisabled() on it when you enter the slot, and setEnabled() when you exit. If you want a modal dialog then construct your own and call exec() on it instead of using the convenience function. Bear in mind that there may be a good reason for the original non-modal dialog... but I do not know it.

MavenTux
13th February 2015, 20:48
Thanks for the reply. I tried two things: First as you suggested setDisabled/setEnabled and second disconnecting the signal-slot on entry and reconnecting on exit. These seem to work but I think there will be a race condition in both these solutions? One scenario for such a race is: first two clicks were so fast that both the slots were entered before the first one could complete the execution of setDisabled or disconnect.

The QT documentation says "The about box has a single button labelled "OK". On Mac OS X, the about box is popped up as a modeless window; on other platforms, it is currently application modal.". I think for android also it should be application modal as only for OS X they have specifically mentioned it. Assuming that it is application modal, (as when the about box is displayed, I am not able to give input to the main window until I close the about box) I think what is happening is that before the first about message box is in displayed, the second click is already initiated causing another box to popup. order of events first_click->second_click->slot1->slot2. So even if I use an application modal dialog, the same issue may come up? ... Secondly, using the same code I am not able to reproduce the issue on Windows, I m not sure why.

wysota
13th February 2015, 21:37
These seem to work but I think there will be a race condition in both these solutions?
No, because there is only one thread involved.


One scenario for such a race is: first two clicks were so fast that both the slots were entered before the first one could complete the execution of setDisabled or disconnect.
No, that will never happen, there is no "concurrent execution" of the slots, they are serial.

ChrisW67
14th February 2015, 01:51
Secondly, using the same code I am not able to reproduce the issue on Windows, I m not sure why.
You cannot reproduce it on Windows because the about() dialog is application modal on Windows: it blocks all input to the parent application while the dialog is active. You can do it on Android because the about() dialog is not modal: it does not block the parent application input so you can click the button multiple times and get multiple non-modal dialogs. I suspect the docs have not kept up with the expanded mobile platform support.

To convince yourself of the serial nature of the slot executions in the single UI thread of execution can I suggest:


void class::slot() {
qDebug() << "In :" << Q_FUNC_INFO;
// show non-modal dialog
qDebug() << "Out:" << Q_FUNC_INFO;
}

You will see an "Out" for the each "In" before you see another "In" (and before you click OK on the dialog I expect)... even when you click furiously.

wysota
14th February 2015, 08:32
even when you click furiously.

Is that a formal name of a testing methodology? :)

ChrisW67
14th February 2015, 20:10
In some programs I have to deal with daily this is the only state of mind available to someone expecting sane behaviour from the program. :(

MavenTux
15th February 2015, 18:19
Thanks guys. As you mentioned the possibility of concurrency is not there as only one thread is involved. I'll try to implement a custom dialog with preferred modality for the same functionality. For using disable/enable strategy, I'll have to disable all the buttons in response to one click because furious clicking may be spread across multiple buttons. I don't want to do this.

MavenTux
16th February 2015, 07:40
I modified my slot function to


void slotFunction()
{
QMessageBox message;
message.setWindowModality(Qt::ApplicationModal);
message.setWindowTitle("....");
message.setDetailedText("....");
message.exec();
}

But I see the same behavior on furious clicking of the button. Multiple message boxes pop up. "message.setWindowModality(Qt::ApplicationModal)" doesn't seem to have any effect on android. What could be happening here?

wysota
16th February 2015, 07:51
It could be happening that Android does not support application modal dialogs. Why don't you disable the UI yourself as was already suggested?

MavenTux
16th February 2015, 09:28
I tried two things

1. Created the messageBox in the constructor and assigned parent to the main window


constructor()
{
messageBox = new QMessageBox(this);
messageBox->setWindowModality(Qt::ApplicationModal);
}

void slotFunction()
{
messageBox->setWindowTitle("....");
messageBox->setDetailedText("....");
messageBox->exec();
}

This seems to solve the problem of multiple boxes opening. But still on furious clicking, flickering is observed in the message box as if one opened over another even though there is only one box opened in effect. Why should this behavior be there?

2. disable/enable the UI. The components become grayed out on disable. Sometime I observe that on closing the message box, the components take a while to return from grayed out state to normal state, which looks odd. Is there a way to control how a widget looks in disabled state?

wysota
16th February 2015, 09:47
Is there a way to control how a widget looks in disabled state?

Yes but that would be an overkill if you ask me.

And your first approach is dangerous as you are execing the dialog while already in exec. Better set a flag before calling exec and bail out from the function if at the beginning the flag is already set.


void slotFunction()
{
if(dialogExecuting) return;
dialogExecuting = true;
messageBox->setWindowTitle("....");
messageBox->setDetailedText("....");
messageBox->exec();
dialogExecuting = false;
}

MavenTux
16th February 2015, 16:16
"you are execing the dialog while already in exec".. I don't understand this. The mentioned constructor is of the mainwindow and messagebox is a member of the mainwindow and slotFunction corresponds to a pushbutton in the mainwindow. MessageBox->setWindowModality(Qt::ApplicationModal) takes effect only when the messagebox's parent is set to the calling widget.

To the first approach, I added disableUI before executing messagebox and enableUI after the messagebox is done. This works fine.. And regarding the disabled state of UI components, I was able to control them through the styleSheet (QPushButton:disabled {....}). But some other issues are observed. The moment I add a standardbutton say 'Ok' to the messagebox the BackKey which is supposed to quit the messagebox doesnt work. And if the text in the messagebox is too long and doesn't fit in the screen, the same can be viewed by scrolling. The issue is, to reach the 'Ok' button also I need to scroll down completely. It isn't visible on the screen until I scroll through the text completely. Weird behavior. Any ideas?

wysota
16th February 2015, 16:37
"you are execing the dialog while already in exec".. I don't understand this. The mentioned constructor is of the mainwindow and messagebox is a member of the mainwindow and slotFunction corresponds to a pushbutton in the mainwindow. MessageBox->setWindowModality(Qt::ApplicationModal) takes effect only when the messagebox's parent is set to the calling widget.
There is an article on this on one of the KDE blogs.