在 PyQt 中关闭 QMessageBox 后对话框的位置发生了变化

A dialog's position changed after closing a QMessageBox in PyQt

我写了一个QMainWindow,两个QDialog,一个QMessageBox。代码如下:

from PyQt5.QtWidgets import QDialog, QVBoxLayout, QPushButton, QMessageBox, QApplication, QAction, QMainWindow


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        action = QAction("Open DocumentDialog", self)
        action.triggered.connect(self.show_main_dialog)

        menu_bar = self.menuBar()
        file = menu_bar.addMenu("File")

        file.addAction(action)
        self.setWindowTitle("MainWindow")

    def show_main_dialog(self):
        main_dialog = DocumentDialog(self)
        main_dialog.show()


class DocumentDialog(QDialog):
    def __init__(self, parent):
        super().__init__(parent)

        vbox = QVBoxLayout()
        btn = QPushButton("Open DocumentDetailDialog")
        btn.clicked.connect(self.btn_clicked)
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.setWindowTitle("DocumentDialog")

    def btn_clicked(self):
        detail_dialog = DetailDialog(self)
        detail_dialog.show()


class DetailDialog(QDialog):
    def __init__(self, parent: QDialog):
        super().__init__(parent)

        vbox = QVBoxLayout()
        btn = QPushButton("Open delete message box")
        btn.clicked.connect(self.btn_clicked)
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.setWindowTitle("DetailDialog")

    def btn_clicked(self):
        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setText("Are you sure?")
        msg_box.setWindowTitle("Delete")
        msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msg_box.buttonClicked.connect(self.delete_handler)
        msg_box.show()

    def delete_handler(self, btn):
        self.close()


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)

    mw = MainWindow()
    mw.show()
    sys.exit(app.exec())

过程如下:

  1. 启动应用程序,然后 MainWindow 出现。
  2. 点击菜单项显示DocumentDialog
  3. 单击 DocumentDialog 上的按钮显示 DetailDialog
  4. 单击 DetailDialog 上的按钮显示 QMessageBox
  5. 单击 QMessageBox 上的按钮将其与 DetailDialog 一起关闭。

QMessageBox 已按预期关闭。奇怪的是,DocumentDialog 出现时 超过 MainWindow。但是QMessageBox关闭后,DocumentDialog躲在后面MainWindow,为什么?如何不让 DocumentDialog 改变它的位置?

我录制一个GIF来说明这个过程:

您应该为您的对话框设置 Qt.Popup 标志:

这使您的对话框始终位于其他对话框之上。

from PyQt5.QtWidgets import QDialog, QVBoxLayout, QPushButton, QMessageBox, QApplication, QAction, QMainWindow
from PyQt5.QtCore import *

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        action = QAction("Open DocumentDialog", self)
        action.triggered.connect(self.show_main_dialog)

        menu_bar = self.menuBar()
        file = menu_bar.addMenu("File")

        file.addAction(action)
        self.setWindowTitle("MainWindow")


    def show_main_dialog(self):
        main_dialog = DocumentDialog(self)
        main_dialog.show()


class DocumentDialog(QDialog):
    def __init__(self, parent):
        super().__init__(parent)

        vbox = QVBoxLayout()
        btn = QPushButton("Open DocumentDetailDialog")
        btn.clicked.connect(self.btn_clicked)
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.setWindowTitle("DocumentDialog")

    def btn_clicked(self):
        detail_dialog = DetailDialog(self)
        detail_dialog.show()


class DetailDialog(QDialog):
    def __init__(self, parent: QDialog):
        super().__init__(parent)

        vbox = QVBoxLayout()
        btn = QPushButton("Open delete message box")
        btn.clicked.connect(self.btn_clicked)
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.setWindowTitle("DetailDialog")
        self.setWindowFlags(self.windowFlags() |Qt.Popup)


    def btn_clicked(self):
        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setText("Are you sure?")
        msg_box.setWindowTitle("Delete")
        msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msg_box.buttonClicked.connect(self.delete_handler)
        msg_box.show()

    def delete_handler(self, btn):
        self.close()


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)

    mw = MainWindow()
    mw.show()
    sys.exit(app.exec())

结果:

已编辑:

来自Dialog modal

This property holds whether show() should pop up the dialog as modal or modeless

By default, this property is false and show() pops up the dialog as modeless. Setting this property to true is equivalent to setting QWidget::windowModality to Qt::ApplicationModal.

exec() ignores the value of this property and always pops up the dialog as modal.

然后正如@musicamante 所说,您应该将 show 更改为 exec :

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        action = QAction("Open DocumentDialog", self)
        action.triggered.connect(self.show_main_dialog)

        menu_bar = self.menuBar()
        file = menu_bar.addMenu("File")

        file.addAction(action)
        self.setWindowTitle("MainWindow")


    def show_main_dialog(self):
        main_dialog = DocumentDialog(self)
        main_dialog.exec()


class DocumentDialog(QDialog):
    def __init__(self, parent):
        super().__init__(parent)

        vbox = QVBoxLayout()
        btn = QPushButton("Open DocumentDetailDialog")
        btn.clicked.connect(self.btn_clicked)
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.setWindowTitle("DocumentDialog")

    def btn_clicked(self):
        detail_dialog = DetailDialog(self)
        detail_dialog.exec()


class DetailDialog(QDialog):
    def __init__(self, parent: QDialog):
        super().__init__(parent)

        vbox = QVBoxLayout()
        btn = QPushButton("Open delete message box")
        btn.clicked.connect(self.btn_clicked)
        vbox.addWidget(btn)
        self.setLayout(vbox)
        self.setWindowTitle("DetailDialog")


    def btn_clicked(self):
        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setText("Are you sure?")
        msg_box.setWindowTitle("Delete")
        msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msg_box.buttonClicked.connect(self.delete_handler)
        msg_box.exec()

    def delete_handler(self, btn):
        self.close()