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.