PDA

View Full Version : How to get focus back to main window if non-modal dialog is closed?



falconium
20th April 2011, 21:49
DIALOG:

Extract from the constructor of dialog:


setWindowFlags(Qt::WindowTitleHint | Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint);
setModal(false);

cpp:

void FindDialog::closeEvent(QCloseEvent *e)
{
emit closing(true);
QDialog::closeEvent(e);
}

h:

protected:
void closeEvent(QCloseEvent *e);

signals:
void closing(bool visible);

MAIN WINDOW:

cpp:


findDialog = new FindDialog;
findDialog->setVisible(false);
findDialog->setMeasurements(&measurements);
connect(findDialog, SIGNAL(closing(bool)), this, SLOT(setShown(bool)));

h:

FindDialog *findDialog;

Even if I change setShown to setFocus it doesn't make any difference.
How could I get back the focus to main window? It is crucial if user meanwhile activated a different application and then clicked back on dialog and closes it...

findDialog->setVisible(true); is called when a menu item is triggered.

ChrisW67
21st April 2011, 01:50
See QWidget::activateWindow() and QWidget::raise (). Be aware the Windows places some restrictions on the activateWindow() call.

falconium
21st April 2011, 03:24
Thanks, Chris!

It looks like FindDialog::closeEvent(QCloseEvent *e) is not even called since it is a non-modal window. Anyway, it is the needed behavior since I don't want to lose search results in the dialog. So, anytime the user clicks on 'Hide' button, it is really makes it hidden, not closed. I thought that closeEvent is called even if the window is not destroyed, but user clicks on e.g. close (top right) button, but not.

However, I want to inform main window that either 'Hide' or top right close button is clicked, so it could take the control and visibility back. What can I do in this case?
Should I check close()?

Another problem: it looks like if a non-modal dialog is opened, then the main window is closed by the user, the non-modal dialog still stays open and the program keeps running.
In my case, the closeEvent is finally called if I close this last window of non-modal dialog, and it strangely gives control/visibility back to the earlier closed main window.

Very strange, however funny.

ChrisW67
21st April 2011, 06:54
The title bar close tool should trigger closeEvent() when clicked. I guess you want to intercept that and hide() the dialog then ignore the event. Your dialog also has a hideEvent() that you could use.

Closing the window does not destroy the object unless you have set the relevant window Qt::WA_DeleteOnClose attribute. In the typical main() the first main window is allocated on the stack and is not destroyed until the exec() loop exits after all the top-level windows close.

falconium
21st April 2011, 22:33
Thanks, Chris.
This is the problem, that closeEvent() is not called when I close this non-modal dialog by clicking on title bar close button. It is only called - as I wrote - in case of closing main window. :(
I have debugged it to see whether it goes into closeEvent(), but doesn't. Strange. I'm using the new Qt framework 4.7.3.

ChrisW67
22nd April 2011, 00:15
Works just fine here with this code:


#include <QtGui>
#include <QDebug>

class Dialog: public QDialog {
Q_OBJECT
public:
Dialog(QWidget *p = 0): QDialog(p) {
QVBoxLayout *layout = new QVBoxLayout;
QLabel *label1 = new QLabel("Dialog Label", this);
layout->addWidget(label1);
setLayout(layout);
}
protected:
void closeEvent(QCloseEvent *e) {
qDebug() << "Dialog closeEvent called";
}
public slots:
private:
};

class MainWindow: public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *p = 0): QMainWindow(p) {
QLabel *label = new QLabel("MainWindow label", this);
setCentralWidget(label);

QTimer::singleShot(3000, this, SLOT(openDialog()));
}
public slots:
void openDialog() {
// With parentless dialog main window closing will not terminate app
dialog = new Dialog;
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
dialog->show();

// With parented dialog the main window close terminates app
// dialog = new Dialog(this);
// dialog->show();
}
protected:
void closeEvent(QCloseEvent *e) {
qDebug() << "MainWindow closeEvent called";
}
private:
Dialog *dialog;
};

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

MainWindow m;
m.show();
return app.exec();
}
#include "main.moc"

Both closeEvents are called on clicking the title bar close icon (Qt4.6.3 Linux and 4.7.0 Windows)

falconium
22nd April 2011, 16:53
Hi Chris,

Yes, because you set Qt::WA_DeleteOnClose attribute, but my aim was not to delete the dialog, just to hide it. Thank you for your contribution however!
Meanwhile I have figured out what an evident mistake I made:

If user clicks on 'Hide' button of dialog box, then the following should be called for the dialog:

this->done(QDialog::Accepted);

I also forgot to add the following to closeEvent() if user clicks on title bar close button:

e->accept();

So, even if it is accepted, the dialog stays in memory and main window gets the focus back.

I still need to play with main window not to show when it was 'closed' and later user 'closes' Find dialog. If main window is closed, then the whole application should quit. Maybe I would need to force it in closeEvent... I'll get back with the solution.

ChrisW67
23rd April 2011, 02:29
I don't know what this "hide" button is. The dialog title bar has a close button, that does exactly what it is advertised to do. It calls the closeEvent(), which you were claiming it did not do. If that event is accepted (the default) then, to quote the QCloseEvent docs (emphasis mine):


Close events contain a flag that indicates whether the receiver wants the widget to be closed or not. When a widget accepts the close event, it is hidden (and destroyed if it was created with the Qt::WA_DeleteOnClose flag).

The corollary is that the dialog is not deleted on close if the Qt::WA_DeleteOnClose attribute is not set. The Qt::WA_DeleteOnClose is set only so the memory is not leaked if you close the parent-less dialog because you cannot rely a QObject parent to do it for you. If you want a mode-less top-level dialog that doesn't delete itself on close then leave out the attribute, but you have to manage the object lifetime elsewhere. Qt::WA_DeleteOnClose has no effect on whether the closeEvent() is called.

To make the whole application quit if the main window is closed then create the mode-less dialog as a child of the main window. There's no magic here.

This example demonstrates your requirement. Take careful note of when the dialog is destroyed.

Hides when its close button is clicked
Hides when its "Hide Me" button is clicked
Shows when the main window calls show()
Is not considered a top-level window that stops the main event loop exiting



#include <QtGui>
#include <QDebug>

class Dialog: public QDialog {
Q_OBJECT
public:
Dialog(QWidget *p = 0): QDialog(p) {
qDebug() << this << "created";
QPushButton *pb = new QPushButton("Hide Me", this);
connect(pb, SIGNAL(clicked()), this, SLOT(hide()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(pb);
setLayout(layout);
}
~Dialog() {
qDebug() << this << "destroyed";
}
protected:
void closeEvent(QCloseEvent *e) { qDebug() << this << "closeEvent called"; }
};

class MainWindow: public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *p = 0): QMainWindow(p), dialog(0) {
QPushButton *pb = new QPushButton("Show dialog", this);
connect(pb, SIGNAL(clicked()), this, SLOT(openDialog()));
setCentralWidget(pb);
}
~MainWindow() { qDebug() << "MainWindow destroyed:" << this; }
public slots:
void openDialog() {
if (!dialog)
dialog = new Dialog(this);
dialog->show();
}
protected:
void closeEvent(QCloseEvent *e) {
qDebug() << this << "closeEvent called";
}
private:
Dialog *dialog;
};

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

MainWindow m;
m.show();
return app.exec();
}
#include "main.moc"