0

I am developing Qt application (Qt version 4.7.3) on SBC6000x board.

I have a MessageBox class derived from QDialog. I have made this class singleton.

Whenever a messagebox is to be show I am using .exec method to show it.

There are few places where I need to show messageboxes one after another.

So, to show new messagebox, I have to close previous one and show new one.

e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.

To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.

Inside this CloseDlg I am emitting finished signal.

void CMsgBox::CloseDlg()
{
    if (NULL != CMsgBox::m_msgBox)
    {
        if(CMsgBox::m_msgBox->isVisible())
        {
            emit CMsgBox::m_msgBox->finished(0);
            //QApplication::processEvents();
        }
    }
}

and calling it as

CMsgBox::CloseDlg();

My show method is :-

int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
    if (CMsgBox::m_msgBox == NULL)
    {
        CMsgBox::m_msgBox = new CMsgBox();
    }
    CMsgBox::m_msgBox->setText(textMsg);
    CMsgBox::m_msgBox->setIcon(icon);
    CMsgBox::m_msgBox->setOkBtnText(okBtnText);
    CMsgBox::m_msgBox->exec();

    return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}

Again when I call showMsgBox,it is showing me following warning. QDialog::exec: Recursive call detected

Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).

I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.

What is the way to return from previous exe call and achieve above scenario? Any help is welcome.

Kai
  • 38,985
  • 14
  • 88
  • 103
RajendraW
  • 55
  • 1
  • 2
  • 11
  • 1
    If your application displays so many error messages that will require a singletoned message box to display them, due to performance reasons... What an user will see? last error? error after error? It is called ErrorOnlyApplication? – joy Dec 11 '12 at 09:19
  • Error is a rare case. User should be able to see the latest messagebox with specified error. – RajendraW Dec 11 '12 at 09:25
  • Then why do you keep the message box allocated? You have memory fragmentation issues? – joy Dec 11 '12 at 09:28
  • Instead if creating windows at runtime I am creating all of them at the start of application and showing them as and when required. This also includes a messagebox. (I know this is not perfect approach, I am saving runtime processing of creating screens and investing into memory usage) – RajendraW Dec 11 '12 at 09:46
  • Have you tried changing the text of the message box at runtime? (No clue if this works). As a side node, I recommend reading [Singleton I love you but you're bringing me down](http://www.codingwithoutcomments.com/), it really changed my mind about singletons – Tim Meyer Dec 11 '12 at 10:46
  • If you have background operations, use open(), not exec. exec() opens a local event loop and will give the joy of endless reentrancy/consistency issues when operation B is triggered while operation A is not completed yet, because A is stuck in exec() etc. The local event loop is a devil any righteous Qt developer must renounce to keep his sanity. – Frank Osterfeld Dec 12 '12 at 21:01

3 Answers3

1

What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.

When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.

What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.

NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.

Fred
  • 4,894
  • 1
  • 31
  • 48
  • Thank you Fred, I tried this. I tried to call exec after receiving 'finished' event, still it is giving me 'QDialog::exec: Recursive call detected'. I think, it is not able to come out of its state immediately and being a singleton object it hampers next call. We need to find a logical point at which exec is callable on object, It is free from all waiting conditions. – RajendraW Dec 12 '12 at 07:15
  • 1
    @RajendraW: When attaching to the 'finished' signal, did you make sure to make that succession a queued one instead of the default direct connection type? – Fred Dec 12 '12 at 14:47
  • Using the words "multithreaded" and "concurrency" here are a bit misleading. The local event loop is executed perfectly synchronously inside exec(), but as any event can occur and trigger arbitrary event handlers/slots, anything can be executed (in the same thread) before exec() returns and destroy any assumptions made about the application state. This is similar to critical sections, yes. OTOH it's also much simpler, and multithreading concepts like locking won't help to solve it. The easiest is: don't mess with the event loop: do not use exec(), processEvents() or explicit local event loops. – Frank Osterfeld Dec 12 '12 at 21:14
  • @Fred: Your solution worked perfectly, the connect call with Qt::QueuedConnection helps to close first messagebox with 'done' function. Once 'finished' signal is received for first one, we can show next messagebox. – RajendraW Dec 14 '12 at 04:39
0

So, you need modeless dialog box. As explained in their documentation :

Modeless dialogs are displayed using show(), which returns control to the caller immediately.

Therefore, instead of showing the box with exec(), show it with show().

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Thank you BЈовић, I want to keep it modal only. Keeping modeless return immediately. If I have 2 buttons OK and Cancel and Cancel is my default value. It returns immediately with that value, which is not expected.I want to keep it modal only. Keeping modeless return immediately. If I have 2 buttons OK and Cancel and Cancel is my default value. It returns immediately with that value, which is not expected. – RajendraW Dec 11 '12 at 09:21
0

Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.

hyde
  • 60,639
  • 21
  • 115
  • 176