Home > Software design >  Pyside2 trigger messagebox from button click closes whole application
Pyside2 trigger messagebox from button click closes whole application

Time:10-29

i want to trigger a messagebox from a button click without closing the entire application, I managed to do this in my previous project, but this time, the behavior is really unexpected. as soon as I clicked ok or the cross sign on the messagebox, the app also closing too without any error. here's the minimal example

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.set_and_load_ui()

    def set_and_load_ui(self):
        self.ui = QUiLoader().load('main.ui', self)

    def trigger_messagebox(self):
        self.messagebox()

    def messagebox(self,x=""):
        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Information)
        msg.setText("{}".format(x))
        msg.setInformativeText("test")
        msg.setWindowTitle("test")
        msg.exec_()

app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.ui.show()
# mainWindow.trigger_messagebox() #this is fine
mainWindow.ui.mainmenuButton.clicked.connect(lambda: mainWindow.trigger_messagebox()) #this is not fine
exit_code = (app.exec_())

just to check, if the messagebox itself is causing the problem, I tried to call it directly on my code, but turns out it just fine, but not when I tried to trigger it by a button click

CodePudding user response:

The problem is related to the fact that QUiLoader loads the UI using the parent given as argument, and since the UI is already a QMainWindow, you're practically loading a QMainWindow into another, which is unsupported: QMainWindows are very special types of QWidgets, and are intended to be used as top level widgets.

It's unclear to me the technical reason for the silent quit, but it's certainly related to the fact that the loaded window actually has a parent (the MainWindow instance), even if that parent is not shown (since you're showing the ui).

Unfortunately, PySide doesn't provide a way to "install" a UI file to an existing instance unlike PyQt does (through the uic.loadUi function), so if you want to keep using PySide there are only two options:

  1. do not use a main window in Designer, but a plain widget ("Widget" in the "New Form" dialog of Designer), load it using QUiLoader and use setCentralWidget() (which is mandatory, since, as the documentation also notes, "creating a main window without a central widget is not supported"):

     self.ui = QUiLoader().load('main.ui', self)
     self.setCentralWidget(self.ui)
    

    The downside of this approach is that you cannot use the main window features anymore in Designer (so, no menus, status bar, dock widgets or toolbars).

  2. use pyside-uic to generate the python code and use the multiple inheritance method to "install" it; the following assumes that you exported the ui file as mainWindow.py:

     from mainWindow import ui_mainWindow
    
     class MainWindow(QtWidgets.QMainWindow, ui_mainWindow):
         def __init__(self):
             super(MainWindow, self).__init__()
             self.setupUi(self)
             # no "set_and_load_ui"
             self.mainmenuButton.clicked.connect(self.trigger_messagebox)
    
         # ...
    
     app = QtWidgets.QApplication(sys.argv)
     mainWindow = MainWindow()
     mainWindow.show()
     exit_code = (app.exec_())
    

    As you can see, now you can access widgets directly (there's no ui object). The downside of this approach is that you must remember to always generate the python file everytime you modify the related ui.

  • Related