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:
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 usesetCentralWidget()
(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).
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.