Unsolved PySide2: Best practice for using modal dialog loaded from a .ui file
-
Hi All,
I was having a difficult time getting a simple modal dialog to work in PySide2 when loading the dialog from a .ui file and so I am writing this post for some advice/feedback on my solution.
The context is that I have a main window, which is itself a single dialog widget and I wanted to open the modal dialog after the user clicks a button in the main dialog. Simple, right? I originally put the code for launching the sub dialog in the slot connected to the pressed signal of the button in the main dialog, but lots of things went wrong, like multiple copies of the sub dialog appearing on screen -- it seemed like the slot was getting called twice somehow, but if I commented out just the code to open the subdialog, it was not getting called twice. Weird.
I eventually figured out that one has to instantiate the sub dialog object OUTSIDE the slot that tries to use it. Do not create the dialog object from the .ui file inside a slot and then try to use it immediately in that slot. I don't recall if this is an issue with Qt in C++. Perhaps it is and I forgot about this "rule."
So now I am creating the instance of the sub dialog class as a member variable in the init of of the main dialog class, then using the member variable in the slot called then the user presses a button in the main dialog and things are working OK now. If this is what one needs to do, then I think It would be very good to note this clearly in the online docs about QDialog and QUiLoader.
As for best practices, I find it awkward (see code below) to have to manage the object tree that comes out of QUiLoader as a separate member variable (self.window or self.ui) inside an object that is already a QDialog. Is there anyway to get QUiLoader to load the UI directly into an object which is a subclass of QDialog, or to create a QDialog from a .ui file?
Here is a snippet of my code for creating the sub dialog class:
class USlideIDsListDialog(QDialog): def __init__(self, parent=None): super(USlideIDsListDialog, self).__init__(parent) loader = QUiLoader() uifile = QFile(os.path.abspath("slide_list_dialog.ui")) if uifile.open(QFile.ReadOnly): self.window = loader.load(uifile, parent) uifile.close() self.window.doneButton.pressed.connect(self.do_done) self.window.cancelButton.pressed.connect(self.do_cancel) # more window initialization here... def show(self): self.window.show() def exec_(self): self.window.exec_() def do_cancel(self): # do nothing, no changes to settings self.window.close() self.reject() def do_done(self): # code to save some stuff to the settings goes here... self.window.close() self.accept()
Then, in the script for the main dialog window I have this slot which is called when the user presses a button in the main dialog (to open the sub dialog window):
@Slot() def do_slide_list_dialog(self): self.slide_list_dialog.show() self.slide_list_dialog.exec_() print(self.slide_list_dialog.result())
Note: the .ui file has a QDialog object at its root and the QDialog is set in creator as Application Modal.
Is there a better way to do this in PySide2?
Thank you for your feedback,
Doug -
@Doug-Wood
I'm struggling to understand the situation you are in.You are not supposed to have create a "holding"
QDialog
which then has aself.window
member which is the actual dialog, if that is what you are saying.but lots of things went wrong, like multiple copies of the sub dialog appearing on screen -- it seemed like the slot was getting called twice somehow, but if I commented out just the code to open the subdialog, it was not getting called twice. Weird.
I eventually figured out that one has to instantiate the sub dialog object OUTSIDE the slot that tries to use it. Do not create the dialog object from the .ui file inside a slot and then try to use it immediately in that slot. I don't recall if this is an issue with Qt in C++. Perhaps it is and I forgot about this "rule."
Not sure, you would have to explain. But in any case I'm not sure I'd want to be creating a dialog out of
QUiLoader.load(ui_file)
each time I'm about to show it, I'd probably create it once and then re-use it.I don't do things via
QUiLoader
, I prefer havingpyside2-uic
generate a Python class. I don't know if you have looked at that, or if you wish to useQUiLoader
.