2
votes

My goal is simply to switch the dialog in application to another by clicking "Next" button and and close the previous one. So, I have a starting script:

from PyQt5.QtWidgets import QApplication, QDialog
from gui.Ui_base import Ui_Base

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = QDialog()
    ui = Ui_Base()
    ui.setupUi(window)

    window.show()
    sys.exit(app.exec_())

Then it enters this dialog, it's GUI was created by pyuic5:

from PyQt5 import QtCore, QtGui, QtWidgets
from gui.Ui_scripts import Ui_Scripts

class Ui_Base(QtWidgets.QDialog):
    def setupUi(self, Base):
        """Here goes a lot of code for creating gui like size, place and etc."""
        self.nextButton = QtWidgets.QPushButton(Base)
        self.nextButton.clicked.connect(self.openScripts)
        QtCore.QMetaObject.connectSlotsByName(Base)

    def openScripts(self, Base):
        scriptsWindow = QDialog()
        scriptsUi = Ui_Scripts()
        scriptsUi.setupUi(scriptsWindow)
        Base.close()
        scriptsWindow.show()
        scriptsWindow.exec_()

And I get an error:

   File "/Users/max/Project/gui/Ui_base.py", line 125, in openScripts Base.close()
AttributeError: 'bool' object has no attribute 'close'

So, actually it does open next dialog, but doesn't hide the current one. I tried other methods of QDialog like hide() and other, but nothing works. And I don't really understang with is Base (which is a window actually) bool?? Thanks.

1
WHy not self.close() ? Also, should be scriptsWindow.exec_()mdurant
Yeah, not exec but exec_, but it doesn't affect it. And with self.close() dialog (Base) simply stays behind the new one, but doesn't disappear.mbk

1 Answers

2
votes

First, you don't (and should NOT) need to call both show() and exec_() on a dialog. exec_() will call show() as part of its operation, and blocks until the dialog is Accepted or Rejected

Which brings me to: QDialog has a special semantic for its closing, the concept of being Accepted or Rejected. If you want a button to close a QDialog, you simply need to connect it to the accept slot or the reject slot. When that signal is emitted, the Dialog will close and its return code will be set to either accepted or rejected:

self.nextButton.clicked.connect(self.accept)

Another problem with your code is that QPushbutton.clicked emits a signal to a slot expecting that slot to accept a bool as the parameter (which represents the checkState of a radio/check button). It doesn't send a class reference. This is why you get the exception you do.

Generally speaking, you don't chain dialogs from within themselves. For one thing, once the first dialog is closed, it will also close its children, which is not what you want because that will close your new dialog. Dialogs can pop up other dialogs, but that should only be used when the new dialog is a 'child' of the old one, since by default it will be modal and block the parent dialog (this is the behavior you are seeing). The correct way to implement your behavior would be to check the return code of calling window.exec_() like so:

accepted = window.exec_()
if accepted == QDialog.Accepted:
   # open the next dialog, user accepted
   accepted = scriptsWindow.exec_()
else:
   # Do what you should if the user rejects the dialog, if that's possible.

Basically, you should create a separate controlling state machine that cycles through the dialog set based on if each dialog is accepted or rejected. In general, I recommend you give the QDialog Documentation another once-over, it describes in pretty solid detail how to treat dialogs and what the common use/implementation patterns are for them.