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 as parent

    void MainWindow::onShowPlotClick()
    {
        PlotWindow* plot_window = new PlotWindow(this);
        ....
        plot_window->show();
    }
    

    In my PlotWindow constructor I set window flags

    PlotWindow::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 child QWidgets

    As you can see in this screenshot, the QMainWindow has focus, but is hidden behind the plot windows.

    img1

    And in this screenshot you can see that each plot window can float above the other, it's just the QMainWindow which can't.

    img2

    If I set the parent of my PlotWindow to nullptr, then i can bring QMainWindow above it.

    void MainWindow::onShowPlotClick()
    {
        PlotWindow* plot_window = new PlotWindow(nullptr);
        ....
        plot_window->show();
    }
    

    Here you can see QMainWindow can come to the front:

    img3

    but now I've lost the ability to close all child windows when QMainWindow closes. Here you can see I've closed QMainWindow and the PlotWindow stays visible

    img4


  • Moderators

    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();
    }
    


  • Awesome, thanks!!


  • Moderators

    @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.


  • Qt Champions 2016

    @JKSH

    This will automatically close all windows.

    But will it free the memory associated with each of them?


  • Moderators

    @kshegunov said:

    @JKSH

    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.


  • Qt Champions 2016

    @JKSH

    I know that there's an ongoing debate ... I'm in the camp that thinks this is OK

    I don't know of such debate, but I suppose I'd be on the side opposite to yours. However, I'll grant you, it should be okay either way.


  • Moderators

    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!


  • Moderators

    @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!


  • Qt Champions 2016

    @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.


  • Qt Champions 2016

    @Wurgl

    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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.