Python 如何在showEvent中关闭模式对话框?

Python 如何在showEvent中关闭模式对话框?,python,pyqt,pyqt5,Python,Pyqt,Pyqt5,我不敢相信我必须问这个问题,但我无法从showEvent关闭PyQt5模式对话框 下面是一个演示该问题的微型示例程序。此测试程序的预期行为是,由于设置了严重错误的somethingWenterryblyError,因此只要按下btnShowDialog,模态对话框可能会出现一瞬间(如果有那么长的话),然后自动关闭,但从下面的屏幕截图中可以看出,这不会发生 # test.py from PyQt5.QtWidgets import QApplication, QDialog, QWidget,

我不敢相信我必须问这个问题,但我无法从
showEvent
关闭PyQt5模式对话框

下面是一个演示该问题的微型示例程序。此测试程序的预期行为是,由于设置了严重错误的
somethingWenterryblyError
,因此只要按下
btnShowDialog
,模态对话框可能会出现一瞬间(如果有那么长的话),然后自动关闭,但从下面的屏幕截图中可以看出,这不会发生

# test.py

from PyQt5.QtWidgets import QApplication, QDialog, QWidget, QLabel, QGridLayout, QPushButton
from PyQt5.Qt import Qt

def main():
    app = QApplication([])
    mainForm = MainForm()
    mainForm.show()
    app.exec()
# end main

class MainForm(QWidget):

    def __init__(self):
        super().__init__()

        self.initUi()

        self.myDialog = MyDialog(self)
        # set that something went wrong in the dialog so it should close immediately in the showEvent
        self.myDialog.somethingWentTerriblyWrong = True
    # end function

    def initUi(self):
        # set default form size and location
        self.setGeometry(400, 400, 400, 275)

        # declare a button
        self.btnShowDialog = QPushButton('show dialog')
        self.btnShowDialog.clicked.connect(self.btnShowDialogClicked)

        # increase the font size
        setFontSize(self.btnShowDialog, 16)

        # declare a layout and add the label to the layout
        self.gridLayout = QGridLayout()
        self.gridLayout.addWidget(self.btnShowDialog)

        # add the layout to the form
        self.setLayout(self.gridLayout)
    # end function

    def btnShowDialogClicked(self):
        retVal = self.myDialog.exec()
        print('retVal = ' + str(retVal))
    # end function

# end class

class MyDialog(QDialog):

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

        self.initUi()

        self.somethingWentTerriblyWrong = False
    # end function

    def initUi(self):
        self.setGeometry(250, 250, 250, 175)

        self.lblDialog = QLabel('label on Dialog')

        # center the label and increase the font size
        self.lblDialog.setAlignment(Qt.AlignCenter)
        setFontSize(self.lblDialog, 15)

        self.gridLayout = QGridLayout()
        self.gridLayout.addWidget(self.lblDialog)

        self.setLayout(self.gridLayout)
    # end function

    def showEvent(self, event):
        super(MyDialog, self).showEvent(event)

        # if something went terribly wrong, close this dialog form
        if self.somethingWentTerriblyWrong:
            print('in if self.somethingWentTerriblyWrong:')
            # self.reject()
            self.close()
        # end if

    # end function

# end class

def setFontSize(widget, fontSize):
    font = widget.font()
    font.setPointSize(fontSize)
    widget.setFont(font)
# end function

if __name__ == '__main__':
    main()
MyDialog
中,我希望self.reject()或self.close()行使对话框关闭,但在Ubuntu 18.04上,至少模式对话框变暗并挂起:

我确信执行在
MyDialog
的show事件中的
if
语句中,因为终端输出是:

$ python3 test.py 
in if self.somethingWentTerriblyWrong:

我做错了什么?

您正在过早地关闭小部件,在
showEvent()
方法中,小部件是可见的,但是在调用
close()
方法停止绘制时,设备上的绘制尚未完成,并且它没有隐藏,因为内部标志没有更新,生成未定义的行为

解决方案是使用
QTimer
QMetaObject
showEvent()
之后的一瞬间(保留内部标志正确更新的时间)调用
close()
方法(或
reject()
方法):

from PyQt5.QtCore import Qt, QTimer, QMetaObject

# ...


class MyDialog(QDialog):
    # ...
    def showEvent(self, event):
        super(MyDialog, self).showEvent(event)

        # if something went terribly wrong, close this dialog form
        if self.somethingWentTerriblyWrong:
            print("in if self.somethingWentTerriblyWrong:")
            QTimer.singleShot(0, self.close)
            # or
            # - QMetaObject.invokeMethod(self, "close", Qt.QueuedConnection)
            # - QTimer.singleShot(0, self.reject)
            # - QMetaObject.invokeMethod(self, "reject", Qt.QueuedConnection)

根据eyllanesc的回答完成工作示例:

# CloseDialogInShowEvent.py

from PyQt5.QtWidgets import QApplication, QDialog, QWidget, QLabel, QGridLayout, QPushButton
from PyQt5.Qt import Qt
from PyQt5.QtCore import QTimer

def main():
    app = QApplication([])
    mainForm = MainForm()
    mainForm.show()
    app.exec()
# end main

class MainForm(QWidget):

    def __init__(self):
        super().__init__()

        self.initUi()

        self.myDialog = MyDialog(self)
        # set that something went wrong in the dialog so it should close immediately in the showEvent
        self.myDialog.somethingWentTerriblyWrong = True
    # end function

    def initUi(self):
        # set default form size and location
        self.setGeometry(400, 400, 400, 275)

        # declare a button
        self.btnShowDialog = QPushButton('show dialog')
        self.btnShowDialog.clicked.connect(self.btnShowDialogClicked)

        # increase the font size
        setFontSize(self.btnShowDialog, 16)

        # declare a layout and add the label to the layout
        self.gridLayout = QGridLayout()
        self.gridLayout.addWidget(self.btnShowDialog)

        # add the layout to the form
        self.setLayout(self.gridLayout)
    # end function

    def btnShowDialogClicked(self):
        retVal = self.myDialog.exec()
        print('retVal = ' + str(retVal))
    # end function

# end class

class MyDialog(QDialog):

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

        self.initUi()

        self.somethingWentTerriblyWrong = False

        self.tmrClose = QTimer(self)
        self.tmrClose.setInterval(200)
        self.tmrClose.timeout.connect(self.tmrCloseTimeout)
    # end function

    def initUi(self):
        self.setGeometry(250, 250, 250, 175)

        self.lblDialog = QLabel('label on Dialog')

        # center the label and increase the font size
        self.lblDialog.setAlignment(Qt.AlignCenter)
        setFontSize(self.lblDialog, 15)

        self.gridLayout = QGridLayout()
        self.gridLayout.addWidget(self.lblDialog)

        self.setLayout(self.gridLayout)
    # end function

    def showEvent(self, event):
        super(MyDialog, self).showEvent(event)

        # if something went terribly wrong, close this dialog form
        if self.somethingWentTerriblyWrong:
            print('in if self.somethingWentTerriblyWrong:')
            self.tmrClose.start()
        # end if
    # end function

    def tmrCloseTimeout(self):
        self.reject()
    # end function

# end class

def setFontSize(widget, fontSize):
    font = widget.font()
    font.setPointSize(fontSize)
    widget.setFont(font)
# end function

if __name__ == '__main__':
    main()