如何在取消 QFileDialog 时停止 child window 关闭
How to stop child window closing when a QFileDialog is cancelled
我有一个 parent class 来处理打开的项目。可以从 child window 打开项目,它调用 parent 函数来处理打开项目。但是,当从 child window 取消 file-dialog 时,整个应用程序退出。
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()
我不明白为什么在 file-dialg 中按下取消时程序不会 return 到 child window。有没有办法让 parent 负责打开项目并解决这个问题?
问题可能在于 hide
和 show
事件的不同事件时间:我想,直到 open
函数 returns,Qt 还没有然而 "registered" 作为 window 的 child 将检查 QApplication.quitOnLastWindowClosed()
选项,这意味着即使 child window 显示为一小部分时间,它仍然 "thinks" 只有一个 window(parent)。
根据您的要求有两种可能:
- 在应用程序实例上使用
setQuitOnLastWindowClosed(False)
,记得在 parent window(或任何其他 window 的 CloseEvent 中调用 quit
你想在关闭时退出);
- 使用
QTimer.singleShot(1, self.hide)
,应该 足够延迟隐藏以避免这个问题;
第一种解决方案通常更好,我强烈建议您使用它。
我什至不确定使用 one-ms 延迟实际上是否足以允许向应用程序发出 "a new window exists" 通知:可能需要更高的数量,并且该值也可以是任意的,具体取决于各种条件(包括平台实施)。
根据 source code,一旦顶级小部件关闭,它就会检查所有 QApplication.topLevelWidgets()
,但根据我的测试,列表不会立即更新:ChildWindow 通常 "appears" 一些 时间在 show()
之后,但有时(通常 <2 毫秒之后)它根本没有出现在列表中。
这是一个非常简单的修复方法:
def open(self):
fileDialog = QFileDialog(self, 'Projects')
fileDialog.setAttribute(Qt.WA_QuitOnClose, False)
或更简单:
def open(self):
fileDialog = QFileDialog(self.sender(), 'Projects')
这里的问题是,每当 window 关闭时,Qt 会检查是否还有其他 windows 也应该关闭。在大多数情况下,如果满足以下两个条件,它将自动关闭 window:
- 设置了
WA_QuitOnClose
属性,和
- 没有父级,或父级被隐藏
不幸的是,在您的示例中,文件对话框和子 window 都是如此,这导致 windows 都被关闭。另外,由于quitOnLastWindowClosed默认为真,应用也会自动退出。
上面的第一个修复通过确保至少一个 window 没有设置 quit-on-close 属性来实现,第二个修复通过确保文件对话框的父级始终可见 window.
我有一个 parent class 来处理打开的项目。可以从 child window 打开项目,它调用 parent 函数来处理打开项目。但是,当从 child window 取消 file-dialog 时,整个应用程序退出。
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()
我不明白为什么在 file-dialg 中按下取消时程序不会 return 到 child window。有没有办法让 parent 负责打开项目并解决这个问题?
问题可能在于 hide
和 show
事件的不同事件时间:我想,直到 open
函数 returns,Qt 还没有然而 "registered" 作为 window 的 child 将检查 QApplication.quitOnLastWindowClosed()
选项,这意味着即使 child window 显示为一小部分时间,它仍然 "thinks" 只有一个 window(parent)。
根据您的要求有两种可能:
- 在应用程序实例上使用
setQuitOnLastWindowClosed(False)
,记得在 parent window(或任何其他 window 的 CloseEvent 中调用quit
你想在关闭时退出); - 使用
QTimer.singleShot(1, self.hide)
,应该 足够延迟隐藏以避免这个问题;
第一种解决方案通常更好,我强烈建议您使用它。
我什至不确定使用 one-ms 延迟实际上是否足以允许向应用程序发出 "a new window exists" 通知:可能需要更高的数量,并且该值也可以是任意的,具体取决于各种条件(包括平台实施)。
根据 source code,一旦顶级小部件关闭,它就会检查所有 QApplication.topLevelWidgets()
,但根据我的测试,列表不会立即更新:ChildWindow 通常 "appears" 一些 时间在 show()
之后,但有时(通常 <2 毫秒之后)它根本没有出现在列表中。
这是一个非常简单的修复方法:
def open(self):
fileDialog = QFileDialog(self, 'Projects')
fileDialog.setAttribute(Qt.WA_QuitOnClose, False)
或更简单:
def open(self):
fileDialog = QFileDialog(self.sender(), 'Projects')
这里的问题是,每当 window 关闭时,Qt 会检查是否还有其他 windows 也应该关闭。在大多数情况下,如果满足以下两个条件,它将自动关闭 window:
- 设置了
WA_QuitOnClose
属性,和 - 没有父级,或父级被隐藏
不幸的是,在您的示例中,文件对话框和子 window 都是如此,这导致 windows 都被关闭。另外,由于quitOnLastWindowClosed默认为真,应用也会自动退出。
上面的第一个修复通过确保至少一个 window 没有设置 quit-on-close 属性来实现,第二个修复通过确保文件对话框的父级始终可见 window.