poetofzwan
poetofzwan

Reputation: 11

PyQt5 QFileDialog stops application from closing

I am trying to write a PyQt5 application that does the following:

My problem is that I haven't found a way to get the QfileDialog to automatically open (2) that doesn't cause the application to hang when the main window is closed. Basic example of code can be found below:

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QWidget,
        QHBoxLayout, QCalendarWidget, QScrollArea, QFileDialog, QAction, QFrame)
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt

class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.openAction = QAction(QIcon('/usr/share/icons/breeze/places/64/folder-open.svg'), 'Open', self)
        self.openAction.triggered.connect(self.openDialog)

        self.menubar = QMenuBar(self)
        fileMenu = self.menubar.addMenu('&File')
        fileMenu.addAction(self.openAction)

        self.event_widgets = EventWidgets(self)
        self.setMenuBar(self.menubar)
        self.setCentralWidget(self.event_widgets)

    def openDialog(self):

        ics_path = QFileDialog.getOpenFileName(self, 'Open file', '/home/michael/')

class EventWidgets(QWidget):

    def __init__(self, parent):
        super(EventWidgets, self).__init__(parent)

        self.initUI()

    def initUI(self):
        self.calendar = QCalendarWidget(self)

        self.frame = QFrame()

        self.scrollArea = QScrollArea()
        self.scrollArea.setWidget(self.frame)

        horizontal_box = QHBoxLayout()
        horizontal_box.addWidget(self.calendar)
        horizontal_box.addWidget(self.scrollArea)

        self.setLayout(horizontal_box)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app_window = MainWindow()
    app_window.showMaximized()
    app_window.openDialog()
    sys.exit(app.exec_())

Code has been tested on KDE Neon and Arch Linux, both have same issue.

I can get round this issue by handling the close event of the Main Window manually - i.e. adding this function to MainWindow:

def closeEvent(self, event):
        sys.exit()

But I am not sure a) why this is necessary b) if it is best practice.

Upvotes: 0

Views: 1283

Answers (1)

Daniele Pantaleone
Daniele Pantaleone

Reputation: 2733

I tried your code on macOS Sierra and it works as it's supposed to. However I would propose a different approach to solve your problem.

What you could do is to implement the showEvent() function in your MainWindow class, which is executed whenever a widget is displayed (either using .show() or .showMaximized()) and trigger your custom slot to open the QFileDialog. When doing this you could make use of a single shot timer to trigger the slot with some minimal delay: the reason behind this is that if you simply open the dialog from within the showEvent(), you will see the dialog window but not the MainWindow below it (because the QFileDialog is blocking the UI until the user perform some action). The single shot timer adds some delay to the opening of the QFileDialog, and allows the MainWindow to be rendered behind the modal dialog. Here is a possible solution (not that I made some minor changes to your code, which you should be able to easily pick up):

import sys

from PyQt5 import QtCore
from PyQt5 import QtWidgets


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.openAction = QtWidgets.QAction('Open', self)
        self.openAction.triggered.connect(self.openDialog)
        menuBar = self.menuBar()
        fileMenu = menuBar.addMenu('&File')
        fileMenu.addAction(self.openAction)
        self.event_widgets = EventWidgets(self)
        self.setCentralWidget(self.event_widgets)

    def showEvent(self, showEvent):
        QtCore.QTimer.singleShot(50, self.openDialog)

    @QtCore.pyqtSlot()
    def openDialog(self):
        ics_path = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', '/Users/daniele/')


class EventWidgets(QtWidgets.QWidget):

    def __init__(self, parent):
        super(EventWidgets, self).__init__(parent)
        self.calendar = QtWidgets.QCalendarWidget(self)
        self.frame = QtWidgets.QFrame()
        self.scrollArea = QtWidgets.QScrollArea()
        self.scrollArea.setWidget(self.frame)
        horizontal_box = QtWidgets.QHBoxLayout()
        horizontal_box.addWidget(self.calendar)
        horizontal_box.addWidget(self.scrollArea)
        self.setLayout(horizontal_box)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    app_window = MainWindow()
    app_window.showMaximized()
    sys.exit(app.exec_())

Upvotes: 1

Related Questions