1
votes

How to create a QWidget (QDialog?) that stays on top of the parent Widgt but not other Applications like Qt::WindowsStaysOnTopHint? I want to show a little "loading" message.

EDIT: for clarification

The message should stay in centered place of the widget when moving the main window around. Do I need to connect the move signals from the main window to manually keep the widget in the right place?

I don't want to block the whole application itself (no dialog.exec()), I just want to say the user should wait and prevent user input (maybe not to all widgets, but parent widget)

2
So you want other windows(of your program) not to accept inputs?s4eed
And have you tried QProgressDialog?s4eed
@saeed Yes, but I don't want to block the whole application itself (no exec()), just want to say the user should wait and prevent user input (maybe not to all widgets, but parent widget).Beachwalker

2 Answers

2
votes

If you read documentation of QDialog::setModal() carefully then you should find proper solution. Just Use QDialog with paret set to your window and set its windowModality to Qt::WindowModal.

Docs says:

Qt::WindowModal - The window is modal to a single window hierarchy and blocks input to its parent window, all grandparent windows, and all siblings of its parent and grandparent windows.

0
votes

Here is my solution:

I don't used setModal and something like that. Just show() and hide() the dialog and it should stay on top of it's parent. I added push and pop (like a stack) because different threads might be interested in showing/hiding the dialog... so it should be guaranteed that only the last call hides the dialog. Other solutions are welcome... this is just quick and dirty.

class WaitDialog : public QDialog
{
    Q_OBJECT

    public:
        WaitDialog(const QString& message, QWidget *parent = 0);

public slots:
    void show();
    void hide();
    void push();
    void pop();

signals:
        void visibilityChanged(bool enable);

private:
    int stack;
        QLabel* msgLabel;
};

WaitDialog::WaitDialog(const QString &message, QWidget *parent)
: QDialog(parent, Qt::FramelessWindowHint),
      msgLabel(new QLabel(message, this)),
      stack(0)
{
    QVBoxLayout* mainLayout = new QVBoxLayout;
    mainLayout->setContentsMargins(30, 30, 30, 30);
    mainLayout->addWidget(msgLabel);
    setLayout(mainLayout);
}

void WaitDialog::show()
{
    QDialog::show();

        emit visibilityChanged(true);
}

void WaitDialog::hide()
{
    QDialog::hide();
    emit visibilityChanged(false);
}

void WaitDialog::push()
{
    stack++;
    show();

    emit visibilityChanged(true);
}

void WaitDialog::pop()
{
    if (stack > 0)
        --stack;
    if (stack == 0)
    {
        hide();
    }
}

So anything I do is just

connect(someObject, SIGNAL(someProcessingFinished()), waitDialog, SLOT(pop()));

to automatically hide the dialog and manually call waitDialog->push() when start processing (or wire it against some someProcessingStarted() method).

The dialog itself is instantiated this way:

WaitDialog* waitDialog = new WaitDialog(tr("Loading data... please wait."), this);

and got its parent in the second param (this in the example).

Works for me to let the main window pop up a message that stays as long as required to stop ui interaction but keeping the application running instead of waiting for the dialog to return. Not used modality flags etc. I just connected interested widgets to the visibilityChanged() signal emitted by the WaitDialog.

connect(waitDialog, SIGNAL(visibilityChanged(bool)), someControl, SLOT(doSomething(bool)));

... and for example the following code do disable/enable the control whenever the WaitDialog changes visibility...

void SomeControl::doSomething(bool enabled)
{
    setEnabled(!enabled);
}

Look my question and answer here to get know how to keep it centered.