简体   繁体   English

取消 QFileDialog 时如何停止子 window 关闭

[英]How to stop child window closing when a QFileDialog is cancelled

I have a parent class which handles opening projects.我有一个处理开放项目的父 class。 Projects can be opened from a child window which calls the parent function to handle opening the project.可以从调用父 function 来处理打开项目的子 window 打开项目。 However, when a file-dialog is cancelled from the child window, the entire application exits.但是,当从子 window 取消文件对话框时,整个应用程序将退出。

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.我无法弄清楚为什么在文件拨号中按下取消时程序不会返回子 window。 Is there a way to keep the parent responsible for opening projects and fix this issue?有没有办法让父母负责打开项目并解决这个问题?

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). 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,这意味着即使子 window 显示了一小部分时间,它仍然“认为”只有一个 window(父)。

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);在应用程序实例上使用setQuitOnLastWindowClosed(False) ,记住在父 window(或任何其他您想要在关闭时退出的 window)的 CloseEvent 中调用quit
  • use a QTimer.singleShot(1, self.hide) , which should delay the hiding enough to avoid this problem;使用QTimer.singleShot(1, self.hide) ,它应该足够延迟隐藏以避免这个问题;

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).我什至不确定使用一毫秒的延迟实际上是否足以允许向应用程序发出“新的 window 存在”通知:可能需要更高的数量,并且该值也可能是任意的,具体取决于各种条件(包括平台实现)。
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.根据源代码,一旦顶级小部件关闭,它就会检查所有QApplication.topLevelWidgets() ,但根据我的测试,该列表不会立即更新: ChildWindow 通常在show()一段时间后“出现”,但有时(通常 <2 ms 之后)它根本不会出现在列表中。

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.这里的问题是,每当关闭 window 时,Qt 就会检查是否还有其他 windows 也应该关闭。 In most cases, it will automatically close a window if these two conditions are met:在大多数情况下,如果满足这两个条件,它会自动关闭一个 window:

  1. the WA_QuitOnClose attribute is set, and WA_QuitOnClose属性已设置,并且
  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.不幸的是,在您的示例中,文件对话框和子 window 都是如此,这导致 windows 都被关闭。 In addition, since quitOnLastWindowClosed is true by default, the application will also automatically quit.另外,由于quitOnLastWindowClosed默认为true,应用程序也会自动退出。

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.上面的第一个修复通过确保至少一个 window 没有设置关闭时退出属性来工作,第二个通过确保文件对话框的父级始终是可见的 window 来工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM