PyQt5 QFileDialog停止应用程序关闭。

时间:2021-12-04 23:03:03

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

我正在尝试编写一个PyQt5应用程序,它执行以下操作:

  • Creates and opens a Main Window. The MainWindow has a function that opens a QFileDialog window.
  • 创建并打开主窗口。主窗口有一个打开QFileDialog窗口的函数。
  • Function to open QFileDialog can be triggered in two ways (1) from a file menu option called 'Open' (2) automatically, after the Main window is shown.
  • 打开QFileDialog的函数可以通过两种方式触发(1),在显示主窗口后,从名为“open”(2)的文件菜单选项中自动触发。

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:

我的问题是,我还没有找到一个方法来让QfileDialog自动打开(2),当主窗口关闭时,它不会导致应用程序挂起。代码的基本示例可以在下面找到:

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.

代码已经在KDE氖和Arch Linux上测试过,两者都有相同的问题。

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

我可以通过手动处理主窗口的关闭事件来绕过这个问题,即把这个函数添加到MainWindow:

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

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

但我不确定为什么这是必要的b)如果它是最佳实践。

1 个解决方案

#1


1  

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.

我在macOS Sierra上试用了你的代码,它的工作原理是这样的。不过,我会提出一种不同的方法来解决你的问题。

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):

您可以做的是在主窗口类中实现showEvent()函数,该函数在显示小部件时执行(使用.show()或. show最大化())并触发您的自定义插槽打开QFileDialog。这样做时,你可以利用一枪定时器触发一些最小的延迟槽:这背后的原因是,如果你只是从内打开对话框showEvent(),您将看到它下面的对话框窗口而不是主窗口(因为QFileDialog阻塞UI,直到用户执行一些动作)。单发计时器增加了对QFileDialog打开的延迟,并允许在模式对话框后面显示主窗口。这里有一个可能的解决方案(不是我对您的代码做了一些细微的更改,您应该能够轻松地将其拾取):

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

#1


1  

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.

我在macOS Sierra上试用了你的代码,它的工作原理是这样的。不过,我会提出一种不同的方法来解决你的问题。

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):

您可以做的是在主窗口类中实现showEvent()函数,该函数在显示小部件时执行(使用.show()或. show最大化())并触发您的自定义插槽打开QFileDialog。这样做时,你可以利用一枪定时器触发一些最小的延迟槽:这背后的原因是,如果你只是从内打开对话框showEvent(),您将看到它下面的对话框窗口而不是主窗口(因为QFileDialog阻塞UI,直到用户执行一些动作)。单发计时器增加了对QFileDialog打开的延迟,并允许在模式对话框后面显示主窗口。这里有一个可能的解决方案(不是我对您的代码做了一些细微的更改,您应该能够轻松地将其拾取):

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