0

I made two forms on pyqt5 - qt-designer. One is the Main Form and the second is a Dialog which I will use for user input. I converted both to py code.

First of all I should inform you that I do not modify the converted Ui.py files. I prefer to have an extra "Main" file where I set each modification. I do this so that I won't have to make the extra changes each time I modify with the Ui files.

So, I added a second class on my Main file and tried to call it from my MainForm Class through a menu item. Here is some example code:

class MainForm(QMainWindow):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        self.ui = Ui_MainForm()
        self.ui.setupUi(self)

        self.ui.actionMenu1.triggered.connect(self.open_my_dialog)

    def open_my_dialog(self):
        my_dialog = QDialog()
        my_dialog.ui = MyDialog()
        my_dialog.ui.setupUi(my_dialog)
        # MainForm.hide(self)
        my_dialog.exec_()
        my_dialog.show()


class MyDialog(QDialog):
    def __init__(self, parent=None):
        super(MyDialog, self).__init__(parent)
        self.ui = Ui_MyDialog()
        self.ui.setupUi(self)
        self.ui.pushButton_cancel.clicked.connect(self.cancel_dialog)

    def cancel_dialog(self):
        print("Closing Dialog Window...")
        sys.exit()

When I run this and click the respective menu button I get the following error:

AttributeError: 'MyDialog' object has no attribute 'setupUi'

the Error is at this line:

self.ui.setupUi(self)  # this is under MyDialog Class

I can get the code working if I reference it to the external (Ui_MyDialog) file directly without using the second class here. But as I stated at the beginning I want to control this from within this file, make the modifications directly here so that I won't have the keep track of the modifications to the Ui files in the future.

shafuq
  • 465
  • 6
  • 20

2 Answers2

1

Given that the error occurs at this line

self.ui.setupUi(self)

and you assigned an Ui_MyDialog instance to self.ui, before

self.ui = Ui_MyDialog()

the error message should be mentioning class Ui_MyDialog and not MyDialog.

So, either you misreported the error message or the error does not occur 'under MyDialog Class'.

I also would point out: QWidget and derived classes have no setupUi method, themselves. Such method belongs to Ui_* classes, generated by uic, and is typically called from the constructor of a widget that inherits from a Ui_* class.

So, if you want to call setupUi on MyDialog instances, MyDialog must inherit from Ui_MyDialog, in the first place.

And you do call it in open_my_dialog:

my_dialog.ui = MyDialog()
my_dialog.ui.setupUi(my_dialog)
p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
  • The external file Ui_MyDialog has: `class Ui_MyDialog(object):` `def setupUi(self, MyDialog):` `# the code from converting the qt-deigner file` Shouldn't it refernce to that from my Class in the main file? I use this way for the MainForm and it works without an issue, it too calls the setupUi in the same manner. – shafuq Dec 10 '17 at 12:26
  • Thank you @p-a-o-l-o I entered the answer after testing everything that I could think of. I wanted to upvote your answer but since I'm new it did not let me do so. I hope the answer I provided is the correct way to go. :) – shafuq Dec 10 '17 at 13:15
1

Finally I got it to work. But before I provide the answer, I would like to thank @p-a-o-l-o and @LoïcG.. the latter helping me out all the way. Thanks!

The code worked when I changed it to the following:

class MainForm(QMainWindow):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        self.ui = Ui_MainForm()
        self.ui.setupUi(self)

        self.ui.actionMenu1.triggered.connect(self.open_my_dialog)

    def open_my_dialog(self):
        my_dialog = MyDialog()
        # my_dialog.show()  <-- seems this line is not necessary either
        my_dialog.exec_()  # this alone was enough for the code to work


class MyDialog(QDialog):
    def __init__(self, parent=None):
        super(MyDialog, self).__init__(parent)
        self.ui = Ui_MyDialog()
        self.ui.setupUi(self)

        self.ui.pushButton_cancel.clicked.connect(self.cancel_dialog)

    def cancel_dialog(self):
        print("Closing Dialog Window...")
        self.close()  # not important but corrected this also.

I hope this helps!

Edit: I corrected some lines and made the answer more simple thanks to
@LoïcG. !

shafuq
  • 465
  • 6
  • 20
  • I don't think this code works. You call `my_dialog.show()` while `my_dialog` is undefined. – Loïc G. Dec 10 '17 at 13:16
  • @LoïcG. my apologies.. correcting it now.. Corrected those two lines by: MyDialog().show() and MyDialog().exec_() – shafuq Dec 10 '17 at 13:19
  • I'm sure it doesn't work. No need to test it. Also you do not need to call `MyDialog().ui.setupUi(QDialog())` since `self.ui.setupUi(self)` is already called into the constructor of `MyDialog` – Loïc G. Dec 10 '17 at 13:21
  • I corrected it. The code worked but I was using the code on the post and copy pasting it.. forgot to edit the 2 lines there. Sorry again. The code does work the way it is. Please undo your downvote if you see fit @LoïcG. – shafuq Dec 10 '17 at 13:23
  • Sorry but `MyDialog().exec_()` should be enough to open your dialog. Currently, you instantiate 3 times `MyDialog` – Loïc G. Dec 10 '17 at 13:27
  • @LoïcG. Yes, I tried removing that line and it did work too. I didn't know it was that easy. Thank you.. I will try to comment that part out now. – shafuq Dec 10 '17 at 13:28
  • @LoïcG. The code doesnt work if i comment out exec_() , but does work when (only) show() is commented out. Is that the proper way to do it? Should I not use show and just exec_? – shafuq Dec 10 '17 at 13:38
  • I never say to comment out `exec_()` but I said `MyDialog().exec_()` should be enough. Please read [the doc](http://pyqt.sourceforge.net/Docs/PyQt4/qdialog.html) to learn what these methods do. Furthermore, as already said too, you currently instanciate two times `MyDialog` : first time with `MyDialog().show()` and second time with `MyDialog().exec_()`. To avoid that, you have to pass a reference to the instance : `d = MyDialog()` – Loïc G. Dec 10 '17 at 13:42
  • @LoïcG. Can you check my answer again? Is this the proper way to do it? I tried to change it according to your feedback. – shafuq Dec 10 '17 at 13:52
  • It's better but calling `my_dialog.show()` before `my_dialog.exec_()` is useless. – Loïc G. Dec 10 '17 at 14:06
  • @LoïcG. I apologies for taking so much of your time. Just to be clear, should I not use show() when calling my dialog? If so I will just delete that line in my answer. Or when you said "before" did you mean that I should write the show() line AFTER the exec_? Thank you for your patience. I'm new as you can understand and want to learn the proper way of coding. – shafuq Dec 10 '17 at 14:09
  • According to [the doc](http://pyqt.sourceforge.net/Docs/PyQt4/qdialog.html#exec), `QDialog.exec_()` shows the dialog as a modal dialog, blocking until the user closes it. You do not need to call `QWidget.show()` for showing you dialog. – Loïc G. Dec 10 '17 at 14:13
  • @LoïcG. Thanks again. Updated the answer now. It seems so easy but took time for me to understand it. – shafuq Dec 10 '17 at 14:25