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

Question:

I have a not very sophisticated but also not trivial Qt5 python application (the full thing is here: https://gitlab.com/rulrich/pydatagrab but see below for a minimal example)

My question is the following:

I have a QMainWindow where in the constructor I do many things, but also:

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

furthermore, I call a file dialog in the constructor

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

When I either press Ctrl+Q or click on File->Exit the same thing happens: the GUI disappears, there are no errors. But the process is kept alive and does not terminate. On Linux, when I start this in the shell, killing the process with Ctrl+C (SIGINT) even does not work (very strange), I have to terminate it with kill (SIGTERM).

I don’t have any special "close" method or functionality in my application. This is directly QMainWindow.close().

Initially I was 100% unclear where to start looking for the problem. However, with the help of this question here and the responses, I figured out it is the QFileDialog itself that is causing the behavior. If I take this out, the application behaves normally.

The minimally reproducing code is here:

#!/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_())

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

Output of print(app.allWidgets()) (after fixing the problem):

[<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>]

Further simplified 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_())

-> no change in behaviour.

Asked By: Ralf Ulrich

||

Answers:

It seems this issue may be related to 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, like this:

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

or like this:

dialog = QFileDialog(parent, caption)
dialog.setOption(QFileDialog.DontUseNativeDialog, True)
Answered By: ekhumoro
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.