Evan Brittain
Evan Brittain

Reputation: 587

How to stop child window closing when a QFileDialog is cancelled

I have a parent class which handles opening projects. Projects can be opened from a child window which calls the parent function to handle opening the project. However, when a file-dialog is cancelled from the child window, the entire application exits.

from PyQt5.QtCore import Qt, QDateTime
from PyQt5.QtWidgets import *
from PyQt5 import QtGui

class ParentWindow(QDialog):
    def __init__(self):
        super(ParentWindow, self).__init__()
        self.cw = None

        self.setFixedSize(300, 100)
        self.button = QPushButton('Open')
        self.button.clicked.connect(self.open)

        layout = QHBoxLayout()
        layout.addWidget(self.button)
        self.setLayout(layout)

        self.show()

    def open(self):
        fileDialog = QFileDialog(self, 'Projects')
        fileDialog.setFileMode(QFileDialog.DirectoryOnly)

        if fileDialog.exec():
            self.hide()
            name = fileDialog.selectedFiles()[0]
            if self.cw:
                self.cw.close()
            self.cw = ChildWindow(self, name)

class ChildWindow(QDialog):
    def __init__(self, parent, name):
        super(ChildWindow, self).__init__(parent)
        self.setFixedSize(500, 100)
        self.setWindowTitle(name)

        self.openButton = QPushButton('Open')
        self.openButton.clicked.connect(self.parent().open)

        layout = QHBoxLayout()
        layout.addWidget(self.openButton)
        self.setLayout(layout)

        self.show()

I can't figure out why the program won't return to the child window when cancel is pressed in the file-dialg. Is there a way to keep the parent responsible for opening projects and fix this issue?

Upvotes: 2

Views: 1707

Answers (2)

ekhumoro
ekhumoro

Reputation: 120598

Here is a very simple fix:

def open(self):
    fileDialog = QFileDialog(self, 'Projects')
    fileDialog.setAttribute(Qt.WA_QuitOnClose, False)

or even simpler:

def open(self):
    fileDialog = QFileDialog(self.sender(), 'Projects')

The issue here is that whenever a window is closed, Qt checks to see if any other windows should also be closed. In most cases, it will automatically close a window if these two conditions are met:

  1. the WA_QuitOnClose attribute is set, and
  2. there is no parent, or the parent is hidden

Unfortunately, in your example, this is true for both the file-dialog and the child window, which results in both windows being closed. In addition, since quitOnLastWindowClosed is true by default, the application will also automatically quit.

The first fix above works by ensuring at least one window does not have the quit-on-close attribute set, and the second works by ensuring the parent of the file-dialog is always a visible window.

Upvotes: 2

musicamante
musicamante

Reputation: 48260

The problem probably resides on the different event timings of both hide and show events: I suppose that, until the open function returns, Qt has not yet "registered" the child as a window that will check against the QApplication.quitOnLastWindowClosed() option, meaning that even if the child window is shown for a fraction of time, it still "thinks" that there is only one window (the parent).

According to your requirements there are two possibilities:

  • use setQuitOnLastWindowClosed(False) on the application instance, remembering to call quit in the CloseEvent of the parent window (or any other window for which you want to quit on close);
  • use a QTimer.singleShot(1, self.hide), which should delay the hiding enough to avoid this problem;

The first solution is usually better and I strongly suggest you to use it.
I'm not even sure that using a one-ms delay would be actually enough to allow the "a new window exists" notification to the application: it might be necessary a higher amount, and that value could also be arbitrary, depending on various conditions (including platform implementation).
According to the source code, as soon as a top level widget is closed it checks against all QApplication.topLevelWidgets(), but according to my tests the list is not immediately updated: the ChildWindow usually "appears" some time after show(), but sometimes (usually <2 ms after) it does not appear in the list at all.

Upvotes: 2

Related Questions