取消 QFileDialog 时如何停止子窗口关闭

Posted

技术标签:

【中文标题】取消 QFileDialog 时如何停止子窗口关闭【英文标题】:How to stop child window closing when a QFileDialog is cancelled 【发布时间】:2019-09-27 05:29:03 【问题描述】:

我有一个处理打开项目的父类。可以从调用父函数来处理打开项目的子窗口打开项目。但是,当从子窗口取消文件对话框时,整个应用程序都会退出。

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

我不明白为什么在文件拨号中按下取消时程序不会返回子窗口。有没有办法让父母负责打开项目并解决这个问题?

【问题讨论】:

我没听懂你的问题,你能解释一下吗 子窗口在按下按钮时创建一个 QFileDialog 实例。如果从 QFileDialog 中选择取消,则整个应用程序将退出。从 QFileDialog 按取消应该返回到上一个屏幕,即子窗口。 【参考方案1】:

问题可能出在hideshow 事件的不同事件时间上:我想,在open 函数返回之前,Qt 还没有将孩子“注册”为将检查的窗口反对QApplication.quitOnLastWindowClosed() 选项,这意味着即使子窗口显示了一小部分时间,它仍然“认为”只有一个窗口(父窗口)。

根据您的要求有两种可能:

在应用程序实例上使用setQuitOnLastWindowClosed(False),记住在父窗口(或您要在关闭时退出的任何其他窗口)的CloseEvent中调用quit; 使用QTimer.singleShot(1, self.hide),这应该延迟隐藏足以避免此问题;

第一个解决方案通常更好,我强烈建议您使用它。 我什至不确定使用一毫秒延迟实际上是否足以允许向应用程序发出“新窗口存在”通知:可能需要更高的数量,并且该值也可能是任意的,具体取决于各种条件(包括平台实现)。 根据source code,一旦***小部件关闭,它就会检查所有QApplication.topLevelWidgets(),但根据我的测试,该列表不会立即更新:ChildWindow 通常“出现”一些 show() 之后的时间,但有时(通常

【讨论】:

你不是说setQuitOnLastWindowClosed(False)吗? 另一种选择是使ChildWindow 成为***小部件而不是ParentWindow 的子小部件,并将父小部件存储为实例变量。 文件对话框取消后子窗口关闭的原因似乎是(1)因为它的父级不可见,(2)因为它没有被视为***窗口。正如 Heike 所说,解决这个问题的最简单方法是不给子窗口一个父窗口。使用setQuitOnLastWindowClosed(False) 会产生自己的问题,因为在子窗口关闭后将无法退出应用程序(因为它的父窗口仍然隐藏), 然而,这些都不能真正解释为什么取消文件对话框会导致子窗口关闭。它们是完全独立的窗口,那么为什么关闭一个窗口也要关闭另一个窗口呢?这种诡异的行为对我来说似乎非常违反直觉,并且看起来像是 Qt 中的一个错误(或者至少是一个错误功能)。这个问题在this QT Forum thread讨论过,但没有得出任何确定的结论。 @ekhumoro 在示例代码中,我注释掉了self.hide() 并尝试了两个QTimer.singleShot 的不同组合:一个实际上隐藏了父窗口,另一个返回显示@987654324 的结果@ 做。我想这是因为底层的异步平台事件,这可以部分解释为什么子窗口有时甚至不显示在我写的topLevelWidgets 列表中。【参考方案2】:

这是一个非常简单的解决方法:

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

甚至更简单:

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

这里的问题是,无论何时关闭一个窗口,Qt 都会检查是否还有其他窗口也应该关闭。大多数情况下,如果满足这两个条件,它会自动关闭一个窗口:

    WA_QuitOnClose 属性已设置, 没有父级,父级被隐藏

不幸的是,在您的示例中,文件对话框和子窗口都是如此,这会导致两个窗口都被关闭。另外,由于quitOnLastWindowClosed默认为true,应用也会自动退出。

上面的第一个修复通过确保至少一个窗口没有设置关闭时退出属性来工作,第二个通过确保文件对话框的父级始终是可见窗口来工作。

【讨论】:

以上是关于取消 QFileDialog 时如何停止子窗口关闭的主要内容,如果未能解决你的问题,请参考以下文章

取消调试停止时自动关闭控制台,取消webapi调试时关闭浏览器自动关闭程序

QFileDialog 取消

使用 QFileDialog 后 QMainWindow 进入后台

QVideoWidget 独立。窗口关闭时如何停止视频?

窗口关闭时停止模式(可可)

如何在动态子按钮上附加关闭功能?