1

I have just started using and discovering PyQt5 and Qt. I installed it with pip install pyqt5. Then I opened the designer.exe that comes with pyqt-tools and made my design.

After that I converted it from .ui to .py using pyuic design.ui > dialog.py.

When I run app.py it runs fine and the ui is working fine. But now my problem is: How do I add functions that get called when a button is pressed?

Here is my app.py code:

import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form

class AppWindow(QDialog)
        def __init__(self):
           super().__init__()
           self.ui = Ui_Form()
           self.ui.setupUi(self)
           self.show()  
                                              
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
musicamante
  • 41,230
  • 6
  • 33
  • 58
karimv2
  • 56
  • 2
  • 10
  • Just to clarify: what you're showing as your `app.py` is clearly *not* the output of the `pyuic` command. So, maybe the file created with pyuic is actually `dialog.py`, right? – musicamante Nov 02 '20 at 10:15
  • @musicamante Yes you're right i showed the file that i typed not the generted one since i though that's not really important – karimv2 Nov 02 '20 at 10:21
  • It's important as using wrong names or confusing references makes your question unclear and requires unnecessary questions and confirmation like those we're having and that could be avoided :-) – musicamante Nov 02 '20 at 10:37

2 Answers2

1

Your approach is actually correct (at least, one of the correct ones): the pyuic generated files should never be modified unless you really (REALLY) know what you're doing and why. Also, their behavior should never be mimicked, as there's no reason to do that.

As a (I'd say, mandatory) reference, you can read more about this topic on the official PyQt guidelines about using Designer.

Let me do an important premise about this topic.

What you're doing is known as the single inheritance approach, meaning that you're creating a subclass that only inherits from the QWidget subclass you're using (QDialog, in this case) and you're building the ui on top of it using what's referred to as the form class. The form class is a standard Python object type that is responsible of creating all child widgets on top of the main subclass instance when its setupUi function is called.
The result is that the subclassed instance will be always referred to self within its scope, while all its children are available through the self.ui object.

A similar (and more common) approach is the multiple inheritance approach. In this way you inherit from both the QWidget subclass (QDialog) and the ui object. The UI result will be the same, but the widgets will be more directly available using self.objectName instead of self.ui.objectName.

Whether you use one or the other is a matter of choice, just remember that whenever you use the multiple inheritance approach the setupUi will potentially overwrite any previously set instance attribute, and if you create a new attribute for an already existing object name, that object will not be (directly) accessible anymore.

Finally, let me give you another suggestion: while not completely wrong, the link posted in the other answer not only gives a very misleading suggestion, but doesn't even dig more in why the previous post it refers to was wrong about. The bottom line is that pyuic created files should never be modified (nor you should try to mimic their behavior) for lots of reasons: not only if you need to change your ui you'll need to merge the already modified code with the new one (hoping that you've not overwritten it using pyuic), but it also leads to misconceptions about the whole object structure.


So, in your case (with the single inheritance approach), all objects created within Designer are actually accessible using self.ui.objectName, and that objectName is what is shown in the object inspector of Designer.

Let's assume that you created a UI with a single push button and you want to print something when that button is pressed. If you didn't do too many tests and modifications to your ui, that button's object name is probably "pushButton".

Then, your code will probably look like this:

import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form

class AppWindow(QDialog)
    def __init__(self):
        super().__init__()
        self.ui = Ui_Form()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.buttonClicked)

    def buttonClicked(self):
        print('button clicked!')

                                              
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())

Note that Qt object names are not unique. You could theoretically set the same object name for more than one QObject (or QWidget), but there's no use in that. Consider that from the python perspective: each variable should have its own name, if you overwrite an already existing variable name, the previous one will become unaccessible (if not even garbage collected).
Designer is smart enough (not that it requires so much...) to prevent creating objects sharing the same object name, and that's why if you add another button, it will be named pushButton_2, pushButton_3, etc. PyQt takes benefit from that by creating unique instance attributes for all object names whether you're using the pyuic file or uic.loadUi().

For the sake of completeness, here's how a multiple inheritance approach would look, while behaving as the example above:


import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form

class AppWindow(QDialog, Ui_Form)
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.pushButton.clicked.connect(self.buttonClicked)

    def buttonClicked(self):
        print('button clicked!')

                                              
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())

In light of what explained above, consider the attribute name issue: if you create an attribute named self.pushButton before calling self.setupUi, after that call self.pushButton will be a reference to the push button and the previously set value will be lost; if you create a new attribute named self.pushButton after calling self.setupUi you won't be able to access the QPushButton instance anymore.


Now, while for normal usage they usually behave in the same way, there are small but still important differences between using the pyuic generated files, or uic.loadUiType(), and the more direct uic.loadUi, and unfortunately those differences are not officially documented (and, frankly, I don't remember all of them).

An example has been previously reported by eyllanesc in the question Size of verticalLayout is different in Qt Designer and PyQt program.

Consider that those are very specific exceptions (which might even be addressed in future releases of PyQt, especially now that Qt6 is on the way) and they normally don't create major issues for standard programming.

Finally (at last!), while Designer is usually smart, is not quite perfect. For example, "technically unimportant" objects as QAction separators are considered as "anonymous" and designer doesn't even apply their object names: if you add a separator to a menu, there's no way to directly access it, even if you actively name it.

musicamante
  • 41,230
  • 6
  • 33
  • 58
0

I would recommend that you don't programm in that converted pythonscript. Just because if you want to change your layout and convert it again you overwrite everything programmed there.

Import this converted ui into a new pythonscript. There are tons of introductions like this (google them) https://nitratine.net/blog/post/how-to-import-a-pyqt5-ui-file-in-a-python-gui/

Then you should read about slots and signals (you can also add them in the qt designer) or add the functions in pythonscript: https://www.tutorialspoint.com/pyqt/pyqt_signals_and_slots.htm

its the easiest way to learn about them when following the tutorials ...

user123
  • 13
  • 6
  • Yess Thank youu i looked at the link you provided and followed the guide and it worked thank you. – karimv2 Nov 02 '20 at 10:10
  • @user123 actually, what the OP shows in the code example is *not* the file converted from pyuic, and, while using `uic.loadUi` is perfectly fine (I normally prefer that method, actually), it doesn't correctly answer the question as it was asked, which is how to implement functions using the pyuic generated files. – musicamante Nov 02 '20 at 10:17
  • @musicamante I actually tested with both imported .ui and generated pyuic and they both work the main part that i was missing was `self.findChild(QtWidgets.QPushButton, 'printButton').clicked.connect(self.function)` and it works with witch ever method you import your ui, just put that line i just mentioned before `self.show()` and it'll work in both cases. – karimv2 Nov 02 '20 at 10:20
  • Both methods get *almost* the same results, but there are some slight (while sometimes important) differences I'll address in another answer. Besides that, that guide gives a very **bad** suggestion, which is to use `findChild` to get the object. Not only that's misleading, but also completely unnecessary, as both pyuic built files and uic module already provide direct access to objects created in Designer. In the case of your last comment, your "printButton" is already accessible as `self.printButton` (or `self.ui.printButton` if you used single inheritance). – musicamante Nov 02 '20 at 10:35
  • @musicamante How would i go about implementing single inheritence? And what is the alternative of findChild since you said it was misleading? – karimv2 Nov 02 '20 at 19:18
  • While the single inheritance (`self.ui.widgetName`) approach is conceptually more "object oriented" wise, it doesn't provide so many benefits than the multiple inheritance (`self.widgetName`) approach. I suggest you to read the official guidelines about [using Designer](https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html) to understand more about this topic. I'm going to add another answer that addresses all those topics. – musicamante Nov 02 '20 at 19:37