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

Time:05-11

I wrote a QMainWindow, two QDialog, and one QMessageBox. The code is as follows:

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

The process is as follows:

  1. Start the app, then MainWindow shows itself.
  2. Click on the menu item to show DocumentDialog.
  3. Click on the button on DocumentDialog to show DetailDialog.
  4. Click on the button on DetailDialog to show a QMessageBox.
  5. Click on the button on QMessageBox to close it along with DetailDialog.

The QMessageBox is closed, as expected. the weird thing is, DocumentDialog is over MainWindow when it shows up. But after QMessageBox is closed, DocumentDialog hides behind MainWindow, why? How not to let DocumentDialog change it position?

I record a GIF to illustrate the process:

enter image description here

CodePudding user response:

You should set Qt.Popup Flag for your Dialog :

This makes your Dialog always on top of others.

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

Result:

enter image description here

Edited:

From 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.

Then as @musicamante said you should change show to 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()
  • Related