1

I'm a little bit confused about the use of the "self" parameter with some widgets like (QLineEdit), indeed when learning to use the QLabel widget, I used to call the class without the self paramater, or when using the QLineEdit widget, the widget wouldn't work without the "self" parameter, here's the code I'm working on :

# Import necessary modules
import sys

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton
from PyQt5.QtCore import Qt

class EntryWindow(QWidget): # Inherits QWidget

def __init__(self): # Constructor
    super().__init__() # Initializer which calls constructor for QWidget

    self.initializeUI() # Call function used to set up window

def initializeUI(self):
    """
    Initialize the window and display its contents to the screen
    """
    self.setGeometry(400, 300, 400, 200)
    self.setWindowTitle('QLineEdit Widget')
    self.displayWidgets()

    self.show() # Show everything

def displayWidgets(self):
    '''
    Setup the QLineEdit and other widgets.
    '''
    # Create name label and line edit widgets
    QLabel("Please enter your name below.", self).move(100, 20)
    name_label = QLabel("Name:", self)
    name_label.move(55, 70)

    self.name_entry = QLineEdit(self)
    self.name_entry.move(120, 68)
    self.name_entry.resize(200, 25) # Change size of entry field

    self.name_entry.setAlignment(Qt.AlignLeft) # The default alignment

    
    text_font = self.name_entry.font() # Get font option from the Qlineedit
    text_font.setPointSize(12)         # Modify font size
    #text_font.setBold(True)           # Bold
    self.name_entry.setFont(text_font) # Apply font
    

    self.clear_button = QPushButton('Clear text', self)
    self.clear_button.clicked.connect(self.clearEntries)
    self.clear_button.move(120, 130)

    self.exit_button = QPushButton("Exit", self)
    self.exit_button.clicked.connect(self.exitApplication)
    self.exit_button.move(240, 130)

def clearEntries(self):
    
    sender = self.sender()
    if sender.text() == 'Clear text':
        self.name_entry.clear()

def exitApplication(self):

    sender = self.sender()
    if sender.text() == "Exit":
        self.close() # Close the window

   # Run program
   if __name__ == '__main__':
       app = QApplication(sys.argv)
       window = EntryWindow()
       sys.exit(app.exec_())

So here is where I'm confused, when using QLabel, I didn't have to put the "self" parameter before, or when using QLineEdit, I had to put "self" otherwise my code wouldn't work :

QLabel("Please enter your name below.", self).move(100, 20)
self.name_entry = QLineEdit(self)
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
DarKing
  • 25
  • 6

3 Answers3

3

First of all, the problem or difference has nothing to do with the "self", but what it is for, pre-established rules in Qt design.

In Qt there is a hierarchy tree between the QObjects where it is established that the parent QObject manages the memory (the life cycle of its children) so if the parent deleted the children, they will also be deleted. This allows avoiding memory leaks since many QObjects are generally used in many applications.

On the other hand, that concept of kinship is also passed to QWidgets since they are also QObjects, but there is also another characteristic: a QWidget in general will be drawn on top of its parent. So if you want the QLineEdit and QLabel to be part of the window then they must be children of the window so it is necessary that you pass the window object which is "self" as parent.

So when you go to the window (in this case "self") you avoid 2 problems:

  • That the object has a longer life cycle (the same as the window).
  • And you make the widget (either QLabel or QLineEdit) be placed on top of the window.
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I understand the garbage collection strategy and the display characteristic, but what I'm still confused about is why I have to call QLineEdit like this : `self.name_entry = QLineEdit(self)` and not like this : `name_entry = QLineEdit(self)` – DarKing Dec 05 '20 at 12:28
  • @DarKing The self.X creates an attribute of the class that can be accessed in all the methods of the class, instead the second code can only be accessed in the method where you use it – eyllanesc Dec 05 '20 at 13:51
  • I see, I got it now, that's why when I call a Widget without the "self.X" my code runes well, but the method that should be executed when clicked or other signal doesn't work, Thank you so much ! – DarKing Dec 05 '20 at 19:25
  • @DarKing btw, `SomeClass()` is not "calling", that is creating an instance. Unless you refer to the "calling" verb as calling the class instance constructor, which returns an instance, but using that word in this context could be confusing. Btw, signals work anyway even if the object that emits them is not set as an instance attribute, so your last comment is not correct: if the object is not created as an instance attribute but is not deleted (if it's created with/added to a parent), its signals will work anyway. – musicamante Dec 06 '20 at 01:06
  • @musicamante Thank you for the clarifications, I'm begining in OOP and I'm still confused about some things. – DarKing Dec 06 '20 at 20:38
1

The self name is a Python convention to indicate the instance of an object. When set as argument in an object constructor, it's normally used to give the new object a reference to the one that created it, and that's normally called a "parent". Note that while self is usually used as parent argument, you can set any QWidget instance as parent.

In Qt terms, not only it indicates the roles in a "parentship" of the object structure, but also results in two important aspects:

  • In the world of QObjects (from which QWidget inherits) this helps for memory management: when a parent is deleted, all its children objects are deleted as well. It's also useful to find child objects (using findChild() or findChildren()) which are direct children or [great, ...] grandchildren of another.
  • When creating a widget with a QWidget parent, that widget is also only "drawn inside" it: it can only be visible within the boundaries of the parent, not outside it; note that this is usually optional, as most times you'll add a widget to a layout manager (but you didn't), which automatically reparents the widget to the widget that is managed by that layout: if it was a child of another widget, it's removed from that and added to the new one. Also consider that if a widget has no parent and is not added to a layout, it's considered a "top level widget" (a standalone window), and if show() or setVisible(True) is called it will have its own window.
    This means that if the widget has a parent and the parent becomes visible, the child will be automatically visible too (unless hide() or setVisible() was explicitly called).

Note that while a QWidget that has no parent is always a top level window, a widger that has a parent could be a top level window, and that happens if it has the QtCore.Qt.Window flag set (using the f keyword in the constructor, or with setWindowFlags()). This is useful when the widget has to be a top level window, but the parentship must still be preserved, not only for memory management (when the parent window is closed and deleted, any child should be deleted as well) but for interaction purposes: for example, with QDialogs (which are standalone windows) it's important to set the parent, so that they can be modal to that parent, meaning that no keyboard/mouse interaction can happen on the parent until the dialog is closed.

Finally, it's important to remember that creating widgets "on the fly" in python has a (sometimes perceived incoherent) result: if no python reference is given to the widget but the widget has a parent, it won't garbage collected.

Consider the following:

def test1(self):
    someDialog = QtWidgets.QDialog()
    someDialog.show()

This will probably show a window just for an instant, and then it will disappear instantly.

def test2(self):
    someDialog = QtWidgets.QDialog(self)
    someDialog.show()

In this case, the dialog will not disappear, that's because it has a parent, PyQt knows it, and it will not try to delete it.

Even this is actually valid (but, no, don't do it):

def test2b(self):
    QtWidgets.QDialog(self).show()

Similar result, but only apparently:

def test3(self):
    self.someDialog = QtWidgets.QDialog()
    self.someDialog.show()

In the case above, the visual result will be probably the same as test2, but if test3 is called again while the dialog is still visible, it will delete the current one and create a new one: that's because the dialog has no parent, and overwriting self.someDialog with a new instance will cause python to release the previous one, which will tell Qt that it can safely erase it, since it has no parent.

Finally, it's not necessary to set the parent to widgets, as long as they are added to a layout (which is the preferred approach, as using fixed geometries is highly discouraged), since the layout will automatically set the parent anyway as soon as it's [going to be] set on the actual parent.

Note that QDialog is normally shown with exec_() in order to correctly display it as modal of the parent and use it as it should.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Also, you could read [this answer](https://stackoverflow.com/a/30357216), but consider that it's mostly C++ oriented. – musicamante Dec 05 '20 at 01:15
  • Thank you so much for this answer, it's really helpful, I'll need to read this again when I'll advance in my book, I understood what you explained and it seems logical. – DarKing Dec 06 '20 at 20:45
0

Note the import statements at the top

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton

QLabel and QLineEdit as can be seen in the documentations https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLabel.html https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLineEdit.html

both take a QWidget as a parameter. The self passed as the second parameter indicates the current QWidget type. For other methods inside the class, self is used to invoke the instance methods.

Anulot
  • 55
  • 8
  • 1
    No, it does not "indicate the current `QWidget` type". It is the (optional) parent that the QWidget superclass (and then QObject) uses to represent the parentship for the new object and the existing instance. It could be *any* QWidget subclass instance, it could be another widget or even a child of the current, and it is normally used to instantly set the ownership if the widget is not going to be a top level window (or it is, but still requires a parent for other reasons, such as QDialog). – musicamante Dec 05 '20 at 01:21