简体   繁体   English

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

[英]how to close a modal dialog in showEvent?

I can't believe I have to ask this, but I can't get a PyQt5 modal dialog box to close from the showEvent .我不敢相信我不得不问这个问题,但我无法从showEvent关闭 PyQt5 模态对话框。

Here is a miniature example program demonstrating the problem.这是一个演示该问题的微型示例程序。 The intended behavior of this test program is since somethingWentTerriblyWrong is set, as soon as btnShowDialog is pressed, the modal dialog should perhaps appear for an instant, if even that long, and then close automatically, but as you can see from the screenshot below this does not happen.该测试程序的预期行为是因为设置somethingWentTerriblyWrong ,一旦按下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()

In MyDialog , I would have expected either the self.reject() or self.close() lines to get the dialog to close, however on Ubuntu 18.04 at least the modal dialog turns dark and hangs:MyDialog中,我希望 self.reject() 或 self.close() 行可以关闭对话框,但是在 Ubuntu 18.04 上,至少模态对话框会变暗并挂起:

在此处输入图像描述

I'm certain execution is getting in the if statement in MyDialog 's show event because the terminal output is:我确定在MyDialog的 show 事件中执行if语句,因为终端 output 是:

$ python3 test.py 
in if self.somethingWentTerriblyWrong:

What am I doing wrong?我究竟做错了什么?

You are prematurely closing the widget, in the showEvent() method the widget is visible but the painting on the device has not yet been done so when calling the close() method you stop the painting, and it is not hidden because the internal flag was not updated generating undefined behavior .您过早地关闭了小部件,在showEvent()方法中小部件是可见的,但设备上的绘画尚未完成,因此在调用close()方法时停止绘画,并且它没有隐藏,因为内部标志未更新生成未定义的行为

The solution is to invoke the close() method ( or reject() method) an instant after showEvent() after an instant (leaving the time for the internal flags to update correctly) using QTimer or QMetaObject :解决方案是使用QTimerQMetaObjectshowEvent()之后的某个瞬间(留出正确更新内部标志的时间)之后立即调用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)

Complete working example based on eyllanesc's answer:根据 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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM