QMainWindow can't appear in front of child QWidget
-
I have a desktop application with a single
QMainWindow
.The user can being up multiple supplementary windows (derived from
QWidget
)Closing a supplementary window has no effect on the rest of the application.
However, closing the
QMainWindow
should close all other windows and shut down the application.As such, when creating my new supplementary windows I pass my
QMainWindow
asparent
void MainWindow::onShowPlotClick() { PlotWindow* plot_window = new PlotWindow(this); .... plot_window->show(); }
In my
PlotWindow
constructor I set window flagsPlotWindow::PlotWindow(QWidget* parent) : QWidget(parent) { setWindowFlags(windowFlags() | Qt::Window); QVBoxLayout* layout = new QVBoxLayout; setLayout(layout); .... }
This all works well, and has the desired effect of closing my
QMainWindow
also closes all supplementary windows.However, it is not possible to bring my
QMainWindow
in front of it's childQWidgets
As you can see in this screenshot, the
QMainWindow
has focus, but is hidden behind the plot windows.And in this screenshot you can see that each plot window can float above the other, it's just the
QMainWindow
which can't.If I set the
parent
of myPlotWindow
tonullptr
, then i can bringQMainWindow
above it.void MainWindow::onShowPlotClick() { PlotWindow* plot_window = new PlotWindow(nullptr); .... plot_window->show(); }
Here you can see
QMainWindow
can come to the front:but now I've lost the ability to close all child windows when
QMainWindow
closes. Here you can see I've closedQMainWindow
and thePlotWindow
stays visible -
This is standard behavior. It is intended that child windows don't get obscured by their parents.
The solution is not to set the parent as you did. With an extra line of code you can still have windows destroyed when the main window goes away:
QWidget* foo = new QWidget; connect(mainWindow, &MainWindow::destroyed, foo, &QWidget::deleteLater); foo->show();
There's just a little problem here. By default the application loop exits when the last window is closed. With the above, closing main window will no longer exit the loop (as there are other windows opened). This means that the main window won't be destroyed when it's closed and so the children won't be either (until you manually close all the windows).
An easy fix for this is to create the main window on the heap and set an attribute for it, so it would look something like this:int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow* mainWindow = new MainWindow; mainWindow->setAttribute(Qt::WA_DeleteOnClose); mainWindow->show(); QWidget* foo = new QWidget; QObject::connect(mainWindow, &MainWindow::destroyed, foo, &QWidget::deleteLater); foo->show(); return a.exec(); }
-
@Chris-Kawa said:
With an extra line of code you can still have windows destroyed when the main window goes away:
QWidget* foo = new QWidget; connect(mainWindow, &MainWindow::destroyed, foo, &QWidget::deleteLater); foo->show();
Alternatively, instead of connecting every single supplementary window, you can simply quit the application when the main window is destroyed:
connect(mainWindow, &MainWindow::destroyed, qApp, &QApplication::quit);
This will automatically close all windows.
-
@kshegunov said:
This will automatically close all windows.
But will it free the memory associated with each of them?
Hmm... not directly, I guess.
I know that there's an ongoing debate about whether or not it's appropriate to let the OS reclaim some unfreed memory upon program shutdown. I'm in the camp that thinks this is OK.
-
-
This could probably be cleaned up very easily with something like this (haven't tested):
... a.exec(); qDeleteAll(a.topLevelWidgets());
assuming all the windows, including main window, are created on the heap and have no parents.
-
@JKSH said:
But will it free the memory associated with each of them?
Hmm... not directly, I guess.
I know that there's an ongoing debate about whether or not it's appropriate to let the OS reclaim some unfreed memory upon program shutdown. I'm in the camp that thinks this is OK.
This won't work for me, as I have various RAII style classes which save settings to disk, etc in their destructors.
If the containing window's destructor doesn't get called, then these classes won't destruct, and the settings won't get saved.
Some windows are opened and closed during the app's lifetime - so I also don't want to wait until application shutdown to reclaim memory.
Thanks for the input though - it is appreciated, even if it doesn't fit my needs on this particular occasion exactly!
-
@Chris-Kawa said:
This could probably be cleaned up very easily with something like this (haven't tested):
... a.exec(); qDeleteAll(a.topLevelWidgets());
assuming all the windows, including main window, are created on the heap and have no parents.
I just discovered that direct deletions can cause QComboBox (and possibly other complex widgets) to crash.
I think part of the reason is that the QComboBox instantiates a QComboBoxPrivateContainer, which also becomes a top-level widget. There's probably a double-deletion somewhere if we use a direct deletion instead of
deleteLater()
(I haven't dug too deep to find out).So, manually deleting widgets after the event loop ends is a no-go :-/
@skebanga said:
Thanks for the input though - it is appreciated, even if it doesn't fit my needs on this particular occasion exactly!
No worries, that's what discussions are for :-) All the best with your project!
-
@JKSH said:
I just discovered that direct deletions can cause QComboBox (and possibly other complex widgets) to crash.
I think part of the reason is that the QComboBox instantiates a QComboBoxPrivateContainer, which also becomes a top-level widget.
This is certainly curious. Maybe I can ask something ... suppose you create a widget on the stack and don't give it a parent. And after the constructor runs you add it to a layout (which I believe should take the ownership of said widget away). Does this widget count as top-level, and does it acquire a native handle (i.e. it is a native widget)? I should think not, or am I in error? Am I correct in assuming that its alien/native status is undefined until
QWidget::create
is run? -
@kshegunov It does not matter. What if an application crashes? Who cleans up the memory in that case? Who closes the file descriptors? Who closes all network connections? Who frees up file locks or locks of devices like a serial device, an USB-port …?
Guess? It is easy!
It is the operating system. It is always the operating system, yo do not have to free all resources before exiting the application.
If the operating system would not, your computer/smartphone/whatever would get filled up with chunk after a few crashes and would refuse to start new applications due to lack of memory or due to locked files ...
And no! MS-DOS was not an operating system. MS-DOS was some kind of runtime library, since it did not clean up those resources, at least not all.
So do not waste your time with such discussions.
-
It does not matter.
It does, and it's proven in the OP's final post, where he notes he has clean-up code running in the destructors of his objects.
What if an application crashes?
Then the application has a bug and that needs to be addressed.
Who cleans up the memory in that case? Who closes the file descriptors? Who closes all network connections? Who frees up file locks or locks of devices like a serial device, an USB-port …?
The programmer should do that. Some OS-es do it as a safety net to leave things consistent, but certainly not all and certainly not always!
yo do not have to free all resources before exiting the application.
This claim is a bit presumptuous, to put it mildly. And if you ask me it is a lazy, and not very smart thing to do. No one referenced MSDOS at any of the posts, but if you had ever used some code that uses global resources, you'd know that it's a good idea to clean up your mess, especially if someone is trying to shut you down forcefully!
What do you think happens if an application crashes (or is terminated) while holding a reference to a global mutex? Do you think the OS knows that the global mutex must be unlocked? Suppose it's a real-time sensitive program used for air traffic meteorological information, so what do we do then? (I had such a case where someone writing the controlling UI decided it's a good idea to terminate me forcefully) Are we saying "well, wait a bit until I restart the OS"? It can make all the difference in the world if the pilot is flying through a storm, you should think about it next time you board.
-
@kshegunov Yes, there are cases where the application crashed caused by some cleanup code which gets called within exit() (which is still the application and not the OS itself).
And yes, globally shared things like Shared Memory, Semaphores, Named Pipes and such are not cleaned up, but those are intended to be used by multiple processes to communicate. They cannot be cleaned, because they are not owned exclusive by a program.
I mentioned DOS, because of history. Back in DOS-times you had to clean up everything before exiting, otherwise you had to reboot the machine very often. That time I hacked a few Terminate-and-Stay-Resident programs, it was a hell!
-
IMO it's a code smell to rely on the OS to clean up for you.
We run all of our unit tests through valgrind as part of our build process, so that a memory leak breaks the build.
QT leaves a ridiculous amount of memory lying around, which of course kicks off valgrind.
We've got a huge suppressions file which we have to install for every GUI related unit test, and the sheer number of suppressions makes valgrind run slower, so has resulted in longer build times.
So yeah, in my opinion, it would be nice if QT didn't do that.