打开 QFileDialog 后,Qt5 应用程序未在 QMainWindow.close() 上终止

Qt5 application not terminating on QMainWindow.close() after opening QFileDialog

我有一个不是很复杂但也不简单的 Qt5 python 应用程序(完整的东西在这里:https://gitlab.com/rulrich/pydatagrab 请参阅下面的最小示例)

我的问题如下:

我有一个 QMainWindow,在构造函数中我做了很多事情,而且:

        self.exitAct = QAction("Exit", self, shortcut="Ctrl+Q", triggered=self.close)
        ...
        self.fileMenu = QMenu("File", self)
        self.fileMenu.addAction(self.exitAct)

此外,我在构造函数中调用了一个文件对话框

          options = QFileDialog.Options()
          fileName, _ = QFileDialog.getOpenFileName(self, 'QFileDialog.getOpenFileName()', '',
                                                    'Images (*.png *.jpeg *.jpg *.bmp *.gif *.yaml)',
                                                    options=options)

当我按下 Ctrl+Q 或单击“文件”->“退出”时,同样的事情发生了:GUI 消失了,没有任何错误。但进程保持活动状态,不会终止。在 Linux 上,当我在 shell 中启动它时,使用 Ctrl+C (SIGINT) 终止进程甚至不起作用(很奇怪),我必须使用 kill 终止它( SIGTERM).

我的应用程序中没有任何特殊的“关闭”方法或功能。这个直接QMainWindow.close().

最初我 100% 不清楚从哪里开始寻找问题。但是,借助这里的这个问题和回复,我发现是 QFileDialog 本身导致了这种行为。如果我把它去掉,应用程序就会正常运行。

最小复制代码在这里:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, os

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class MainWindow(QMainWindow):

     def __init__(self, fileName=None):
         
          super().__init__()

          self.exitAct = QAction("Exit", self, shortcut="Ctrl+Q", triggered=self.close)
          self.fileMenu = QMenu("File", self)
          self.fileMenu.addAction(self.exitAct)
          self.menuBar().addMenu(self.fileMenu)

          self.openFile()

    
     def openFile(self):
          options = QFileDialog.Options()
          fileName, _ = QFileDialog.getOpenFileName(self, 'QFileDialog.getOpenFileName()', '',
                                                    'Images (*.png *.jpeg *.jpg *.bmp *.gif *.yaml)',
                                                    options=options)
          # load some data ...
          # ...
                        

        
if __name__ == '__main__':
     app = QApplication(sys.argv)
     window = MainWindow()
     window.show()
     sys.exit(app.exec_())

=================================

print(app.allWidgets()) 的输出(修复问题后):

[<PyQt5.QtWidgets.QMenu object at 0x7fb8e6ae49d0>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6ae4b80>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6ae4c10>, <PyQt5.QtWidgets.QMenu object at 0x7fb8e6ae4ca0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6ae4d30>, <PyQt5.QtWidgets.QPushButton object at 0x7fb8e6ae4160>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6ae4dc0>, <PyQt5.QtWidgets.QLineEdit object at 0x7fb8ee6ea790>, <PyQt5.QtWidgets.QMenu object at 0x7fb8e6ae4790>, <PyQt5.QtWidgets.QLineEdit object at 0x7fb8ee6eae50>, <PyQt5.QtWidgets.QFrame object at 0x7fb8e6ae4e50>, <PyQt5.QtWidgets.QScrollArea object at 0x7fb8ee6ea5e0>, <PyQt5.QtWidgets.QLabel object at 0x7fb8ee6ea670>, <PyQt5.QtWidgets.QLabel object at 0x7fb8e6ae40d0>, <PyQt5.QtWidgets.QToolButton object at 0x7fb8e6ae4ee0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6ae4f70>, <PyQt5.QtWidgets.QLineEdit object at 0x7fb8ee6eac10>, <PyQt5.QtWidgets.QFrame object at 0x7fb8e6af5040>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af50d0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5160>, <PyQt5.QtWidgets.QLineEdit object at 0x7fb8ee6ea9d0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6ae4af0>, <__main__.MainWindow object at 0x7fb8ee6ea160>, <PyQt5.QtWidgets.QLabel object at 0x7fb8e6ae4040>, <PyQt5.QtWidgets.QComboBox object at 0x7fb8ee6ea430>, <PyQt5.QtWidgets.QFrame object at 0x7fb8e6af51f0>, <PyQt5.QtWidgets.QListView object at 0x7fb8e6af5280>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5310>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af53a0>, <__main__.DataArea object at 0x7fb8ee6ea550>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5430>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af54c0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5550>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af55e0>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5670>, <PyQt5.QtWidgets.QMenuBar object at 0x7fb8e6ae4a60>, <PyQt5.QtWidgets.QLabel object at 0x7fb8ee6eadc0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5700>, <PyQt5.QtWidgets.QFrame object at 0x7fb8e6af5790>, <PyQt5.QtWidgets.QListView object at 0x7fb8e6af5820>, <PyQt5.QtWidgets.QComboBox object at 0x7fb8ee6eab80>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af58b0>, <PyQt5.QtWidgets.QListView object at 0x7fb8e6af5940>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af59d0>, <PyQt5.QtWidgets.QLabel object at 0x7fb8ee6eaaf0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5a60>, <PyQt5.QtWidgets.QComboBox object at 0x7fb8ee6ea4c0>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5af0>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5b80>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5c10>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5ca0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5d30>, <PyQt5.QtWidgets.QLabel object at 0x7fb8ee6eaca0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6af5dc0>, <PyQt5.QtWidgets.QLabel object at 0x7fb8ee6ea820>, <PyQt5.QtWidgets.QLabel object at 0x7fb8ee6ea940>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5e50>, <PyQt5.QtWidgets.QScrollBar object at 0x7fb8e6af5ee0>, <PyQt5.QtWidgets.QComboBox object at 0x7fb8ee6ea700>, <PyQt5.QtWidgets.QListView object at 0x7fb8e6af5f70>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6ae41f0>, <PyQt5.QtWidgets.QWidget object at 0x7fb8e6afc040>, <PyQt5.QtWidgets.QMenu object at 0x7fb8e6ae48b0>]

进一步简化MRE:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, os

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class MainWindow(QMainWindow):

     def __init__(self, fileName=None):         
          super().__init__()
          key = QShortcut(QKeySequence("Ctrl+Q"), self)
          key.activated.connect(self.close)
          self.openFile()

    
     def openFile(self):
          fileName, _ = QFileDialog.getOpenFileName(self, 'QFileDialog.getOpenFileName()', '',
                                                    'Images (*.png *.jpeg *.jpg *.bmp *.gif *.yaml)')#)
          #,options=QFileDialog.DontUseNativeDialog)
                        

        
if __name__ == '__main__':
     app = QApplication(sys.argv)
     window = MainWindow()
     window.show()
     sys.exit(app.exec_())

-> 行为没有变化。

看来这个问题可能与QTBUG-59184, which affects native GTK file dialogs opened by Qt5. The bug is still unresolved and has critical priority, so hopefully it will be fixed reasonably quickly. In the meantime, the work-around is to use the built-in Qt file-dialog, which can be specified via the options有关,像这样:

QFileDialog.getOpenFileName(parent, caption, options=QFileDialog.DontUseNativeDialog)

或者像这样:

dialog = QFileDialog(parent, caption)
dialog.setOption(QFileDialog.DontUseNativeDialog, True)