【问题标题】:In PyQT why somes widgets needs the "self" parameter before calling them, while others don't在 PyQT 中,为什么某些小部件在调用它们之前需要“self”参数,而其他小部件则不需要
【发布时间】:2020-12-05 00:47:01
【问题描述】:

我对将“self”参数与某些小部件(如(QLineEdit))一起使用有点困惑,事实上,在学习使用 QLabel 小部件时,我曾经在没有 self 参数的情况下调用类,或者在使用QLineEdit 小部件,如果没有“self”参数,小部件将无法工作,这是我正在处理的代码:

# 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_())

所以这就是我感到困惑的地方,在使用 QLabel 时,我之前不必输入“self”参数,或者在使用 QLineEdit 时,我必须输入“self”,否则我的代码将无法工作:

QLabel("Please enter your name below.", self).move(100, 20)
self.name_entry = QLineEdit(self)

【问题讨论】:

    标签: python class oop pyqt self


    【解决方案1】:

    首先,问题或差异与“自我”无关,而是为了什么,Qt设计中预先设定的规则。

    在 Qt 中,QObject 之间有一个层次结构树,其中建立了父 QObject 管理内存(其子对象的生命周期),因此如果父对象删除了子对象,它们也将被删除。这可以避免内存泄漏,因为许多 QObject 通常用于许多应用程序。

    另一方面,亲属关系的概念也传递给 QWidgets,因为它们也是 QObjects,但还有另一个特点:QWidget 通常会绘制在其父级之上。因此,如果您希望 QLineEdit 和 QLabel 成为窗口的一部分,那么它们必须是窗口的子级,因此您必须将“self”的窗口对象作为父级传递。

    因此,当您转到窗口时(在本例中为“self”),您可以避免 2 个问题:

    • 表示对象的生命周期较长(与窗口相同)。
    • 然后您将小部件(QLabel 或 QLineEdit)放置在窗口顶部。

    【讨论】:

    • 我了解垃圾收集策略和显示特性,但我仍然感到困惑的是为什么我必须这样调用 QLineEdit:self.name_entry = QLineEdit(self) 而不是这样:name_entry = QLineEdit(self)跨度>
    • @DarKing self.X创建了类的一个属性,该属性可以在该类的所有方法中访问,而第二个代码只能在您使用它的方法中访问
    • 我明白了,我现在明白了,这就是为什么当我调用没有“self.X”的小部件时,我的代码运行良好,但单击或其他信号时应执行的方法却没有辛苦了,非常感谢!
    • @DarKing btw,SomeClass() 不是“调用”,而是创建一个实例。除非您将“调用”动词称为调用类实例构造函数,该构造函数返回一个实例,但在此上下文中使用该词可能会造成混淆。顺便说一句,即使发出信号的对象未设置为实例属性,信号仍然有效,因此您的最后一条评论不正确:如果对象不是作为实例属性创建但没有被删除(如果它是使用/添加到父母),它的信号无论如何都会起作用。
    • @musicamante 感谢您的澄清,我开始使用 OOP,但我仍然对某些事情感到困惑。
    【解决方案2】:

    注意顶部的导入语句

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

    QLabel 和 QLineEdit 可以在文档中看到 https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLabel.html https://doc.qt.io/qtforpython/PySide2/QtWidgets/QLineEdit.html

    两者都将QWidget 作为参数。作为第二个参数传递的self 表示当前的QWidget 类型。对于类中的其他方法,self 用于调用实例方法。

    【讨论】:

    • 不,它没有“表示当前的QWidget 类型”。它是 QWidget 超类(然后是 QObject)用来表示新对象和现有实例的父级关系的(可选)父级。它可以是 any QWidget 子类实例,它可以是另一个小部件甚至是当前的子类,如果小部件不是顶级窗口,它通常用于立即设置所有权(或者是,但由于其他原因仍然需要父级,例如 QDialog)。
    【解决方案3】:

    self 名称是 Python 约定,用于指示对象的实例。当在对象构造函数中设置为参数时,它通常用于为新对象提供对创建它的对象的引用,这通常称为“父对象”。请注意,虽然self 通常用作父参数,但您可以将任何 QWidget 实例设置为父参数。

    在 Qt 术语中,它不仅表示对象结构的“父级”中的角色,而且还导致两个重要方面:

    • 在 QObjects(QWidget 从中继承)的世界中,这有助于内存管理:当父对象被删除时,它的所有子对象也会被删除。查找子对象(使用 findChild()findChildren())也很有用,它们是另一个的直接子对象或 [great, ...] 孙子对象。
    • 创建带有 QWidget 父级的小部件时,该小部件也仅“绘制在”它内部:它只能在父级边界内可见,而不能在其外部可见;请注意,这通常是可选的,因为大多数时候您会将小部件添加到布局管理器(但您没有),它会自动将小部件重新设置为由该布局管理的小部件:如果它是另一个小部件的孩子小部件,它已从中删除并添加到新的小部件中。还要考虑如果一个小部件没有父级并且没有添加到布局中,它被认为是一个“顶级小部件”(一个独立的窗口),如果show()setVisible(True) 被调用,它将有自己的窗口。 这意味着如果小部件具有父级并且父级变为可见,则子级也将自动可见(除非显式调用 hide()setVisible())。

    请注意,虽然 没有父级 的 QWidget 始终是顶级窗口,但 父级 的窗口小部件可能是顶级窗口水平窗口,如果它设置了QtCore.Qt.Window 标志(在构造函数中使用f 关键字,或者使用setWindowFlags()),就会发生这种情况。当小部件必须是顶级窗口时,这很有用,但仍必须保留父级关系,不仅用于内存管理(当父窗口关闭并删除时,任何子窗口也应被删除),而且用于交互目的:例如,对于 QDialogs(独立窗口),设置父级很重要,以便它们可以模态到该父级,这意味着没有键盘/鼠标交互可以发生在父级上,直到对话框关闭。

    最后,重要的是要记住,在 python 中“动态”创建小部件会产生(有时被认为是不连贯的)结果:如果没有为小部件提供 python 引用小部件 有一个父母,它不会被垃圾收集。

    考虑以下几点:

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

    这可能会只显示一个窗口,然后它会立即消失。

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

    在这种情况下,对话框不会消失,那是因为它有一个父级,PyQt 知道它,它不会尝试删除它。

    即使这实际上是有效的(但是,不,不要这样做):

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

    类似的结果,但只是表面上:

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

    在上述情况下,视觉结果可能与test2 相同,但是如果在对话框仍然可见时再次调用test3,它将删除当前对话框并创建一个新对话框:这是因为对话框没有父对话框,并且用新实例覆盖self.someDialog 将导致python 释放前一个对话框,这将告诉Qt它可以安全地擦除它,因为它没有父节点。

    最后,没有必要将父级设置为小部件,只要将它们添加到布局(这是首选方法,因为高度不鼓励使用固定几何图形),因为布局无论如何,只要它[将要]设置在实际的父级上,就会自动设置父级。

    请注意,QDialog 通常与exec_() 一起显示,以便正确地将其显示为父级的模态并按应有的方式使用它。

    【讨论】:

    • 另外,您可以阅读this answer,但考虑到它主要面向 C++。
    • 非常感谢你的回答,它真的很有帮助,当我在我的书中前进时,我需要再读一遍,我理解你的解释,这似乎合乎逻辑。
    猜你喜欢
    • 2014-05-30
    • 1970-01-01
    • 1970-01-01
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多