当前位置:   article > 正文

008.PyQt5_QObject基类_对象名称&属性和父子关系_qt objectname().right(1)

qt objectname().right(1)

QObject


  1. 对象名称,属性(API)
    1. setObjectName('唯一名称') # 给Qt对象设置一个名称,当做对象的ID来使用
    2. objectName() # 获取Qt对象的名称
    3. setProperty('属性名称','值') # 给Qt对象动态的添加一个属性与值(键值对模式)
    4. property("属性名称") # 通过对象的属性名称获取其属性值
    5. dynamicPropertyNames() # 获取一个对象中所有通过 setProperty() 设置的属性名称
  • 应用场景:
    • 用于qss的ID选择器,属性选择器(方便统一设置样式:标签名#ID选择器[属性名='属性值'])
    • 用于装饰器的信号与槽
  1. 父子对象的操作
    1. obj2.setParent(obj1) # 设置父类,把obj2设置为obj1的父对象
    2. obj2.parent() # 获取父对象
    3. obj1.children() # 获取所有直接子对象,返回一个列表
    4. obj1.findChild(参数1, 参数2, 参数3) # 获取第一个指定类型和名称的后代对象,找到第一个就返回结果
    5. 参数1:类型如 QObject;类型元组如 (QPushButton, QLabel)
    6. 参数2:名称 notice(可以省略)
    7. 参数3:查找选项
    8. Qt.FindChildrenRecursively 递归查找,默认选项;
    9. Qt.FindDirectChildrenOnly 只查找直接子对象;
    10. obj1.findChildren(参数1, 参数2, 参数3) # 获取所有指定类型和名称的后代对象,返回一个列表
    11. 参数同上
  • 应用场景:
    • 对Qt对象内存管理机制的影响(父对象被释放,子对象自动被释放)
    • 对Qt控件的影响(父控件被删除,子控件自动删除)

功能作用

  1. 对象名称,属性(API)
    1. setObjectName('唯一名称') # 给Qt对象设置一个名称,当做对象的ID来使用
    2. objectName() # 获取Qt对象的名称
    3. setProperty('属性名称','值') # 给Qt对象动态的添加一个属性与值(键值对模式)
    4. property("属性名称") # 通过对象的属性名称获取其属性值
    5. dynamicPropertyNames() # 获取一个对象中所有通过 setProperty() 设置的属性名称
  • 示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super(Windows, self).__init__()
    6. self.setWindowTitle('对象的名称和属性')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj = QObject()
    13. # 设置对象名称:setObjectName('objname')
    14. obj.setObjectName('QObject对象名称')
    15. # 获取对象名称:objectName()
    16. print('获取对象名称:', obj.objectName())
    17. # 设置对象属性:setProperty('属性名', '值')
    18. obj.setProperty('level1','第一')
    19. obj.setProperty('level2','第二')
    20. obj.setProperty('level3','第三')
    21. # 根据属性获取值:property('属性名')
    22. print('获取对象的属性值:', obj.property('level2'))
    23. # 获取对象所有通过setProperty()方法设置的属性:dynamicPropertyNames()
    24. print('# 获取对象所有通过setProperty()方法设置的属性:', obj.dynamicPropertyNames())
    25. if __name__ == '__main__':
    26. app = QApplication(sys.argv)
    27. w = Windows()
    28. w.show()
    29. sys.exit(app.exec_())
  • 输出结果
  • 应用场景
    • 用于 qss 的 ID 选择器,属性选择器 ——> 方便统一设置样式。
    • 用于装饰器的信号与槽
  • 案例:创建多个用于信息提示的 QLabel
    1. 要求:
    2. 凡是提示的 QLabel 控件,都要求设置:
    3. 字体大小为 20px
    4. 字体颜色为灰色;
    5. 边框圆角为 8px
    6. 信息提示分多个级别:
    7. 正常 (normal)——绿色边框、绿色字体。
    8. 警告 (warning)——黄色边框、黄色字体。
    9. 错误 (error)——红色边框、红色字体。
  • 涉及知识点:
    1. qss 样式表
    2. 文件读取。
    3. 对象 \ 属性名称设置。
  • 先介绍一下 qss:qss 和控件的关系类似于前端 css 和 html 元素的关系。
  • 样式相关:通过setStyleSheet设置对象的样式
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('样式')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. label = QLabel(self)
    13. label.setText('QLabel标签样式')
    14. # 为label标签设置样式
    15. label.setStyleSheet("font-size: 30px; color: purple")
    16. if __name__ == '__main__':
    17. app = QApplication(sys.argv)
    18. w = Windows()
    19. w.show()
    20. sys.exit(app.exec_())
  • 输出
  • 其中 “font-size: 30px; color: purple” 这个字符串就是用来设置标签的样式,为了开发方便,常常把字符串放到某个文件中,需要用的时候读取文件,作用到整个应用程序上,这个文件的后缀就是.qss (qss 我猜是样式表 Qt Style Sheet 的缩写)。
  • qss文件代码
    1. QLabel{
    2. background-color:yellow;
    3. font-size: 30px;
    4. color: black;
    5. }
    6. QPushButton{
    7. background-color:red;
    8. }
  • 因为 qss 里面有很多样式,需要做一个选择器,很多个样式去匹配存在的控件。上面我用的是类选择器,还有很多其他选择器比如:通配符选择器、ID 选择器等等。
  • pyqt5调用qss样式示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. with open('./style.qss',mode='r') as f:
    10. app.setStyleSheet(f.read())
    11. def widget_list(self):
    12. self.add_widget()
    13. def add_widget(self):
    14. label1 = QLabel(self)
    15. label1.setText('标签1样式')
    16. label1.move(50,50)
    17. label2 = QLabel(self)
    18. label2.setText('标签2样式')
    19. label2.move(50, 100)
    20. but = QPushButton(self)
    21. but.setText('按钮样式')
    22. but.move(50, 150)
    23. if __name__ == '__main__':
    24. app = QApplication(sys.argv)
    25. w = Windows()
    26. w.show()
    27. sys.exit(app.exec_())
  • 输出结果
  • 从运行效果可以看到,实现了从 style.qss 样式表中读取字符串,并在整个应用程序上实现,两个标签都变成了一种格式。
  • 然后现在又出现了一个问题:如果只想部分QLabel标签使用样式怎么办,以上方法会一把子把所有的样式都给改了,这时候就要用到 ID 选择器(# ID 名称),这里的 ID 指的是前面说的 ObjectName。
  • qss文件设置如下:
    1. QLabel#notice{
    2. background-color:yellow;
    3. font-size: 30px;
    4. color: black;
    5. }
    6. QPushButton#but{
    7. background-color:red;
    8. }
  • pyqt5调用qss样式示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. with open('./style.qss',mode='r') as f:
    10. app.setStyleSheet(f.read())
    11. def widget_list(self):
    12. self.add_widget()
    13. def add_widget(self):
    14. label1 = QLabel(self)
    15. label1.setText('标签1使用样式')
    16. label1.setObjectName('notice')
    17. label1.move(50,50)
    18. label2 = QLabel(self)
    19. label2.setText('标签2使用样式')
    20. label2.setObjectName('notice')
    21. label2.move(50, 100)
    22. label3 = QLabel(self)
    23. label3.setText('标签3不使用样式')
    24. label3.move(50, 150)
    25. but1 = QPushButton(self)
    26. but1.setText('按钮1不使用样式')
    27. but1.move(350, 50)
    28. but2 = QPushButton(self)
    29. but2.setText('按钮2使用样式')
    30. but2.setObjectName('but')
    31. but2.move(350, 100)
    32. if __name__ == '__main__':
    33. app = QApplication(sys.argv)
    34. w = Windows()
    35. w.show()
    36. sys.exit(app.exec_())
  • 输出结果
  • 可以看到只有对象名称为notice的QLabel对象和对象名称为but的QPushButton对象才会被修改样式,label3和but1 还是默认样式。
  • 解决了这一个问题,还有问题是如何给 label 和 label2 设置不同样式,因为现实生活中同样是 notice,有的提示正确信息,有的提示错误信息,还需要样式区分,这时候需要属性选择器。
  • qss 设置:
  • 其中 border 设定边框为 1 像素宽,实线,颜色使用 gray 来表达。radius 表示半径,也就是圆角。
  • px 表示像素(Pixel),是相对电脑分辨率谈的长度单位,和实际无关
    1. QLabel#notice {
    2. font-size: 20px;
    3. color: gray;
    4. border: 1px solid gray;
    5. border-radius: 8px;
    6. }
    7. QLabel#notice[noticelevel="normal"] {
    8. color: green;
    9. border-color: green;
    10. }
    11. QLabel#notice[noticelevel="warning"] {
    12. color: yellow;
    13. border-color: yellow;
    14. }
    15. QLabel#notice[noticelevel="error"] {
    16. color: red;
    17. border-color: red;
    18. }
    19. QPushButton#but{
    20. background-color:red;
    21. }
  • PyQt5调用qss样式代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. with open('./style.qss',mode='r') as f:
    10. app.setStyleSheet(f.read())
    11. def widget_list(self):
    12. self.add_widget()
    13. def add_widget(self):
    14. label = QLabel(self)
    15. label.setObjectName("notice")
    16. label.setText("没有任何地位的通知")
    17. label.move(50, 50)
    18. label2 = QLabel(self)
    19. label2.setObjectName("notice")
    20. label2.setProperty("noticelevel", "warning")
    21. label2.setText("效率变低啦")
    22. label2.move(50, 100)
    23. label3 = QLabel(self)
    24. label3.setObjectName("notice")
    25. label3.setProperty("noticelevel", "error")
    26. label3.setText("被退学了")
    27. label3.move(50, 150)
    28. label3 = QLabel(self)
    29. label3.setObjectName("notice")
    30. label3.setProperty("noticelevel", "normal")
    31. label3.setText("无事发生")
    32. label3.move(50, 200)
    33. but1 = QPushButton(self)
    34. but1.setText('按钮1不使用样式')
    35. but1.move(350, 50)
    36. but2 = QPushButton(self)
    37. but2.setText('按钮2使用样式')
    38. but2.setObjectName('but')
    39. but2.move(350, 100)
    40. if __name__ == '__main__':
    41. app = QApplication(sys.argv)
    42. w = Windows()
    43. w.show()
    44. sys.exit(app.exec_())
    • noticelevel 是属性名,normal、warning、error是属性的值。
  • qss样式说明
    标签名#ID选择器[属性名='属性值']
  • 对应关系
    • qss样式表
      1. QLabel#notice[noticelevel="error"] {
      2. color: red;
      3. border-color: red;
      4. }
      5. QPushButton#but{
      6. background-color:red;
      7. }
    • pyqt5文件代码
      1. label2 = QLabel(self)
      2. label2.setObjectName("notice")
      3. label2.setProperty("noticelevel", "warning")
      4. label2.setText("效率变低啦")
      5. label2.move(50, 100)
      6. but2 = QPushButton(self)
      7. but2.setText('按钮2使用样式')
      8. but2.setObjectName('but')
      9. but2.move(350, 100)
    • 关系图:

  1. 父子对象的操作
  • 对象父类、子类设置和查找方法
    1. obj2.setParent(obj1) # 设置父类,把obj1设置为obj2的父对象
    2. obj2.parent() # 获取父对象
    3. obj1.children() # 获取所有直接子对象,返回一个列表
    4. obj1.findChild(参数1, 参数2, 参数3) # 获取第一个指定类型和名称的后代对象,找到第一个就返回结果
    5. 参数1:类型如 QObject;类型元组如 (QPushButton, QLabel)
    6. 参数2:名称 notice(可以省略)
    7. 参数3:查找选项
    8. Qt.FindChildrenRecursively 递归查找,默认选项;
    9. Qt.FindDirectChildrenOnly 只查找直接子对象;
    10. obj1.findChildren(参数1, 参数2, 参数3) # 获取所有指定类型和名称的后代对象,返回一个列表
    11. 参数同上
  • 尝试构造如下父子关系图
  • setParent():设置父对象示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj1.setParent(obj0)
    19. obj2.setParent(obj0)
    20. obj3.setParent(obj1)
    21. obj4.setParent(obj2)
    22. obj5.setParent(obj2)
    23. print('obj0:', obj0)
    24. print('obj1:', obj1)
    25. print('obj2:', obj2)
    26. print('obj3:', obj3)
    27. print('obj4:', obj4)
    28. print('obj5:', obj5)
    29. if __name__ == '__main__':
    30. app = QApplication(sys.argv)
    31. w = Windows()
    32. w.show()
    33. sys.exit(app.exec_())
  • 输出结果
  • parent():获取父对象示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj1.setParent(obj0)
    19. obj2.setParent(obj0)
    20. obj3.setParent(obj1)
    21. obj4.setParent(obj2)
    22. obj5.setParent(obj2)
    23. print('obj0:', obj0)
    24. print('obj1:', obj1)
    25. print('obj2:', obj2)
    26. print('obj3:', obj3)
    27. print('obj4:', obj4)
    28. print('obj5:', obj5)
    29. print("obj1's Parebt:", obj1.parent())
    30. print("obj2's Parebt:", obj2.parent())
    31. print("obj3's Parebt:", obj3.parent())
    32. print("obj4's Parebt:", obj4.parent())
    33. print("obj5's Parebt:", obj5.parent())
    34. if __name__ == '__main__':
    35. app = QApplication(sys.argv)
    36. w = Windows()
    37. w.show()
    38. sys.exit(app.exec_())
  • 输出结果
    • 结果可以看到obj1和obj2的父对象是obj0,obj3的父对象是obj1,obj4和obj5的父对象是obj2
  • children():获取所有所有子对象
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj1.setParent(obj0)
    19. obj2.setParent(obj0)
    20. obj3.setParent(obj1)
    21. obj4.setParent(obj2)
    22. obj5.setParent(obj2)
    23. print('obj0:', obj0)
    24. print('obj1:', obj1)
    25. print('obj2:', obj2)
    26. print('obj3:', obj3)
    27. print('obj4:', obj4)
    28. print('obj5:', obj5)
    29. print('')
    30. print("obj0's children:", obj0.children())
    31. if __name__ == '__main__':
    32. app = QApplication(sys.argv)
    33. w = Windows()
    34. w.show()
    35. sys.exit(app.exec_())
  • 输出结果
    • 返回结果是一个列表,obj0的所有子对象是obj1和obj2
  • findChild(参数1, 参数2, 参数3):获取第一个指定类型和名称的后代对象,找到第一个就返回结果
  • 参数说明
    1. 参数1:类型如 QObject;类型元组如 (QPushButton, QLabel)
    2. 参数2:名称 notice(可以省略)
    3. 参数3:查找选项
    4. Qt.FindChildrenRecursively 递归查找,默认选项;
    5. Qt.FindDirectChildrenOnly 只查找直接子对象;
  • 示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj2.setObjectName('obj2')
    19. obj3.setObjectName('obj2')
    20. obj1.setParent(obj0)
    21. obj2.setParent(obj0)
    22. obj3.setParent(obj0)
    23. obj4.setParent(obj2)
    24. obj5.setParent(obj2)
    25. print('obj0:', obj0)
    26. print('obj1:', obj1)
    27. print('obj2:', obj2)
    28. print('obj3:', obj3)
    29. print('obj4:', obj4)
    30. print('obj5:', obj5)
    31. print('')
    32. print("obj0's children:", obj0.findChild(QObject))
    33. print("obj0's children:", obj0.findChild(QObject, 'obj2'))
    34. if __name__ == '__main__':
    35. app = QApplication(sys.argv)
    36. w = Windows()
    37. w.show()
    38. sys.exit(app.exec_())
  • 输出结果
    • 注意:
      • 如果不指定对象名称,则是查找满足类型的所有后代对象,找到第一个就返回结果
      • 如果不指定对象类型和名称,则是查找所有后代对象,找到第一个就返回结果
  • 如果给 obj0 的添加一个 QLabel子对象
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. label = QLabel()
    19. obj2.setObjectName('obj2')
    20. obj3.setObjectName('obj2')
    21. obj1.setParent(obj0)
    22. obj2.setParent(obj0)
    23. obj3.setParent(obj0)
    24. obj4.setParent(obj2)
    25. obj5.setParent(obj2)
    26. label.setParent(obj0)
    27. print('obj0:', obj0)
    28. print('obj1:', obj1)
    29. print('obj2:', obj2)
    30. print('obj3:', obj3)
    31. print('obj4:', obj4)
    32. print('obj5:', obj5)
    33. print('')
    34. print("obj0's children:", obj0.findChild(QLabel))
    35. if __name__ == '__main__':
    36. app = QApplication(sys.argv)
    37. w = Windows()
    38. w.show()
    39. sys.exit(app.exec_())
  • 运行报错:
    • 原因:控件的父类项必须是一个控件(widget)
  • 直接查找后代对象呢?
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj2.setObjectName('obj2')
    19. obj3.setObjectName('obj3')
    20. obj1.setParent(obj0)
    21. obj2.setParent(obj0)
    22. obj3.setParent(obj1)
    23. obj4.setParent(obj2)
    24. obj5.setParent(obj2)
    25. print('obj0:', obj0)
    26. print('obj1:', obj1)
    27. print('obj2:', obj2)
    28. print('obj3:', obj3)
    29. print('obj4:', obj4)
    30. print('obj5:', obj5)
    31. print('')
    32. print("obj0's children:", obj0.findChild(QObject, 'obj3'))
    33. if __name__ == '__main__':
    34. app = QApplication(sys.argv)
    35. w = Windows()
    36. w.show()
    37. sys.exit(app.exec_())
  • 直接通过对象名称查找孙对象,输出结果
    • 结果可以看出,是能够直接找到其孙对象obj3。是因为参数3默认是Qt.FindChildrenRecursively 递归查找。如果我们将参数3修改为Qt.FindDirectChildrenOnly只查找直接子对象呢?
  • 示例
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj2.setObjectName('obj2')
    19. obj3.setObjectName('obj3')
    20. obj1.setParent(obj0)
    21. obj2.setParent(obj0)
    22. obj3.setParent(obj1)
    23. obj4.setParent(obj2)
    24. obj5.setParent(obj2)
    25. print('obj0:', obj0)
    26. print('obj1:', obj1)
    27. print('obj2:', obj2)
    28. print('obj3:', obj3)
    29. print('obj4:', obj4)
    30. print('obj5:', obj5)
    31. print('')
    32. print("obj0's children:", obj0.findChild(QObject, 'obj3', Qt.FindDirectChildrenOnly))
    33. if __name__ == '__main__':
    34. app = QApplication(sys.argv)
    35. w = Windows()
    36. w.show()
    37. sys.exit(app.exec_())
  • 输出结果
    • 结果显示设置参数3为为Qt.FindDirectChildrenOnly只查找直接子对象后,返回结果为None
  • findChildren(参数1, 参数2, 参数3):获取所有指定类型和名称的后代对象,返回一个列表
  • 参数说明
    1. 参数1:类型如 QObject;类型元组如 (QPushButton, QLabel)
    2. 参数2:名称 notice(可以省略)
    3. 参数3:查找选项
    4. Qt.FindChildrenRecursively 递归查找,默认选项;
    5. Qt.FindDirectChildrenOnly 只查找直接子对象;
  • 示例代码
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj0 = QObject()
    13. obj1 = QObject()
    14. obj2 = QObject()
    15. obj3 = QObject()
    16. obj4 = QObject()
    17. obj5 = QObject()
    18. obj6 = QObject()
    19. obj7 = QObject()
    20. obj1.setParent(obj0)
    21. obj2.setParent(obj0)
    22. obj3.setParent(obj1)
    23. obj4.setParent(obj2)
    24. obj5.setParent(obj2)
    25. obj7.setParent(obj1)
    26. obj6.setParent(obj4)
    27. print('obj0:', obj0)
    28. print('obj1:', obj1)
    29. print('obj2:', obj2)
    30. print('obj3:', obj3)
    31. print('obj4:', obj4)
    32. print('obj5:', obj5)
    33. print('obj6:', obj6)
    34. print('obj7:', obj7)
    35. print('')
    36. print("obj0's children:", obj0.findChildren(QObject))
    37. if __name__ == '__main__':
    38. app = QApplication(sys.argv)
    39. w = Windows()
    40. w.show()
    41. sys.exit(app.exec_())
  • 代码中的父子关系图
  • 代码执行输出结果
    • 从输出结果可以看出来,findChildren()的查找顺序并不是先找子对象再找孙对象...
    • 而是一条线路全部获取完,再返回查找第二条线路
    • obj0 ——> obj1 ——> obj3(没有后代对象了,返回上一层)——> obj1 ——> obj7(没有后代对象了,返回上一层) ——> obj1(所有后代对象都找完了,返回上一层) ——> obj0 ——> obj2 ——> obj4 ——> obj6(没有后代对象了,返回上一层) ——> obj2 ——> obj5
  • 应用场景
  1. 对Qt对象内存管理机制的影响
  • QObject 继承树:
  • 所有的对象都是直接或者间接继承自 QObject,也自然继承了 setParent 的方法,便于设置父子对象;
  • QObjects 在一个对象树中组织他们自己,当创建一个 QObject 时,如果使用了其他对象作为其父对象。那么,它就会被添加到父对象的 children() 列表中。当父对象被销毁时,这个 QObject 也会被销毁。(根节点删除,子节点自然会被删除)
  • 当父对象被销毁时,这个QObject也会被销毁
  • 当父控件被删除时,子控件也会自动删除
  • 子对象的生命周期被父对象接管
  • 示例1
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj1 = QObject()
    13. obj2 = QObject()
    14. obj3 = QObject()
    15. obj4 = QObject()
    16. obj2.setParent(obj1) # 设置obj1为obj2的父对象,obj1销毁,obj2将自动销毁
    17. obj3.setParent(obj2) # 设置obj2为obj3的父对象,obj2销毁,obj3将自动销毁
    18. obj4.setParent(obj3) # 设置obj3为obj4的父对象,obj3销毁,obj4将自动销毁
    19. print(obj1)
    20. print(obj2)
    21. print(obj3)
    22. print(obj4)
    23. obj1.destroyed.connect(lambda :print('obj1被释放了'))
    24. obj2.destroyed.connect(lambda :print('obj2被释放了'))
    25. obj3.destroyed.connect(lambda :print('obj3被释放了'))
    26. obj4.destroyed.connect(lambda :print('obj4被释放了'))
    27. if __name__ == '__main__':
    28. app = QApplication(sys.argv)
    29. w = Windows()
    30. w.show()
    31. sys.exit(app.exec_())
    • PS:lambda 定义了一个匿名函数,不会带来程序运行效率的提高,只会使代码更简洁。如果使用lambda,lambda 内不要包含循环,如果有,宁愿定义函数来完成,使代码获得可重用性和更好的可读性。总结:lambda 是为了减少单行函数的定义而存在的。
    • 在这个案例中
      • 设置obj1为obj2的父对象,这个时候就有一个指针指向obj2
      • 设置obj2为obj3的父对象,这个时候就有一个指针指向obj3
      • 设置obj3为obj4的父对象,这个时候就有一个指针指向obj4
    • 有指针指向的对象,不会被自动释放。但是由于没有指针指向obj1,obj1又是局部变量,在案例中的add_widget()方法执行完成的时候,obj1就会自动释放。由于obj1是obj2的父对象,obj2的生命周期被obj1接管,obj1父对象被销毁时,obj2子对象将自动被销毁。obj3和obj4同
      1. 引用计数机制:
      2. PyObject是每个对象必有的内容,当一个对象有新的引用时,它的 ob_refcnt就会增加,
      3. 当引用它的对象被删除,它的ob_refcnt就会减少,当引用计数为0时,该对象生命就结束了。
  • 输出结果
    1. <PyQt5.QtCore.QObject object at 0x000001921800A670>
    2. <PyQt5.QtCore.QObject object at 0x000001921800A700>
    3. <PyQt5.QtCore.QObject object at 0x000001921800A790>
    4. <PyQt5.QtCore.QObject object at 0x000001921800A820>
    5. obj1被释放了
    6. obj2被释放了
    7. obj3被释放了
    8. obj4被释放了
  • 示例2:self.obj1 = obj1相当于把obj1作为了self对象的一个属性,有指针指向obj1这个对象,此时obj1不会被自动释放
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj1 = QObject()
    13. obj2 = QObject()
    14. obj3 = QObject()
    15. obj4 = QObject()
    16. self.obj1 = obj1
    17. obj2.setParent(obj1) # 设置obj1为obj2的父对象,obj1销毁,obj2将自动销毁
    18. obj3.setParent(obj2) # 设置obj2为obj3的父对象,obj2销毁,obj3将自动销毁
    19. obj4.setParent(obj3) # 设置obj3为obj4的父对象,obj3销毁,obj4将自动销毁
    20. print(obj1)
    21. print(obj2)
    22. print(obj3)
    23. print(obj4)
    24. obj1.destroyed.connect(lambda :print('obj1被释放了'))
    25. obj2.destroyed.connect(lambda :print('obj2被释放了'))
    26. obj3.destroyed.connect(lambda :print('obj3被释放了'))
    27. obj4.destroyed.connect(lambda :print('obj4被释放了'))
    28. if __name__ == '__main__':
    29. app = QApplication(sys.argv)
    30. w = Windows()
    31. w.show()
    32. sys.exit(app.exec_())
    • 当我们将obj1赋值给到self这个window对象属性的时候,就有一个指针指向了obj1。只有等window对象退出(关闭窗口)时,指针消失,obj1才会被释放
  • 示例3:手动把父对象obj1释放掉,子对象obj2也会被自动释放
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj1 = QObject()
    13. obj2 = QObject()
    14. obj3 = QObject()
    15. self.obj1 = obj1
    16. obj2.setParent(obj1) # 设置obj1为obj2的父对象,obj1销毁,obj2将自动销毁
    17. obj3.setParent(obj2) # 设置obj2为obj3的父对象,obj2销毁,obj3将自动销毁
    18. print(obj1)
    19. print(obj2)
    20. print(obj3)
    21. obj1.destroyed.connect(lambda: print('obj1被释放了'))
    22. obj2.destroyed.connect(lambda: print('obj2被释放了'))
    23. obj3.destroyed.connect(lambda: print('obj3被释放了'))
    24. del self.obj1
    25. if __name__ == '__main__':
    26. app = QApplication(sys.argv)
    27. w = Windows()
    28. w.show()
    29. sys.exit(app.exec_())
    • 手动把父对象 obj1 释放掉,子对象 obj2 也会被自动释放
  • 示例4:self.obj2 = obj2 相当于把 obj2作为了 self 对象的一个属性
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj1 = QObject()
    13. obj2 = QObject()
    14. obj3 = QObject()
    15. obj4 = QObject()
    16. self.obj2 = obj2
    17. obj2.setParent(obj1) # 设置obj1为obj2的父对象,obj1销毁,obj2将自动销毁
    18. obj3.setParent(obj2) # 设置obj2为obj3的父对象,obj2销毁,obj3将自动销毁
    19. obj4.setParent(obj3) # 设置obj3为obj4的父对象,obj3销毁,obj4将自动销毁
    20. print(obj1)
    21. print(obj2)
    22. print(obj3)
    23. print(obj4)
    24. obj1.destroyed.connect(lambda :print('obj1被释放了'))
    25. obj2.destroyed.connect(lambda :print('obj2被释放了'))
    26. obj3.destroyed.connect(lambda :print('obj3被释放了'))
    27. obj4.destroyed.connect(lambda :print('obj4被释放了'))
    28. if __name__ == '__main__':
    29. app = QApplication(sys.argv)
    30. w = Windows()
    31. w.show()
    32. sys.exit(app.exec_())
    • 相比于示例2,示例3将obj2赋值给了self这个window对象。但是程序一执行,obj1、obj2、obj3、obj4就全部被释放了
    • 原因:obj1是局部变量,当add_widget方法执行完之后,就自动释放了。而obj1又是obj2的父对象,当父对象销毁时,子对象会自动销毁
    • 以上四组实验证明父子关系对内存管理机制是有影响的,一旦父对象被干掉,子对象会被自动干掉。如果用在控件中会有什么影响?
    • QWidget 扩展了父-子关系:当一个控件设置了父控件,会包含在父控件内部(体现在 GUI 控件展示层面上),受父控件区域裁剪(子控件不可能超出父控件的范围),父控件被删除时,子控件会自动删除(窗口关闭后上面的图标也会被关闭,节省内存)
  1. 对Qt控件的影响
  • 如果一个控件,没有任何父控件,那么就会被当成顶层控件(窗口),多个顶层窗口相互独立。如果想要一个控件被包含在另外一个控件内部,就需要设置父子关系。显示位置受父控件约束,生命周期也被父对象接管
  • 案例1:创建两个独立的窗口。
    • 要求:设置不同的标题
      1. from PyQt5.Qt import *
      2. import sys
      3. app = QApplication(sys.argv)
      4. w1 = QWidget()
      5. w1.setWindowTitle('第一个窗口')
      6. w2 = QWidget()
      7. w2.setWindowTitle('第二个窗口')
      8. w1.show()
      9. w2.show()
      10. sys.exit(app.exec_())
    • 两个窗口对象没有父子关系,所以是独立存在的,两个窗口互不干扰,都可以单独关闭
  • 案例2:创建一个窗口,包含另外两个子控件。
    • 要求:两个子控件必须在同一个窗口内部
      1. from PyQt5.Qt import *
      2. import sys
      3. app = QApplication(sys.argv)
      4. w1 = QWidget()
      5. w1.setWindowTitle('第一个窗口')
      6. w2 = QLabel(w1)
      7. w2.setText('这是一个QLabel标签控件')
      8. w2.move(50, 50)
      9. b1 = QPushButton()
      10. b1.setParent(w1)
      11. b1.setText('这是一个QPushButton控件')
      12. b1.move(50, 100)
      13. w1.show()
      14. w2.show()
      15. sys.exit(app.exec_())
    • PS:父控件的设置有两种方法,w2 = QLabel(w1)放在定义时的括号里,本质上是在用QLabel对象的init方法里的setparent()函数,和b1.setParent(w1)是一样的。
    • 把w1.show()放到w2和b1之前,导致界面显示出来时还没有子控件的定义,也自然显示不出来了
      案例3:创建一个窗口,包含多个子控件。
    • 要求:让所有的QLabel类型子控件都设置背景颜色为cyan,即使后续再增加新的子控件。
    • 可以遍历子控件并加以过滤,children不能过滤,findChildren可过滤。
      1. for sub_w in w1.findChildren(QLabel):
      2. sub_w.setStyleSheet("background-color:cyan;")

del和deleteLater()

  • del
    • del语句:删除变量到对象的引用和变量名称本身
    • 注意del语句作用在变量上,而不是数据对象上
  • deleteLater()
    • 延迟删除对象。deleteLater()并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象
  • 示例
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj1 = QObject()
    13. obj2 = QObject()
    14. obj3 = QObject()
    15. obj4 = QObject()
    16. self.obj1 = obj1
    17. obj2.setParent(obj1) # 设置obj1为obj2的父对象,obj1销毁,obj2将自动销毁
    18. obj3.setParent(obj2) # 设置obj2为obj3的父对象,obj2销毁,obj3将自动销毁
    19. obj4.setParent(obj3) # 设置obj3为obj4的父对象,obj3销毁,obj4将自动销毁
    20. print(obj1)
    21. print(obj2)
    22. print(obj3)
    23. print(obj4)
    24. obj1.destroyed.connect(lambda :print('obj1被释放了'))
    25. obj2.destroyed.connect(lambda :print('obj2被释放了'))
    26. obj3.destroyed.connect(lambda :print('obj3被释放了'))
    27. obj4.destroyed.connect(lambda :print('obj4被释放了'))
    28. del obj2 # 这里只是删除了栈中的变量到对象的引用和变量名本身。并没有删除堆中的对象
    29. # print(obj2) # 这里不能再使用obj2这个变量,因为del语句删除变量到对象的引用和变量名称本身
    30. obj3.deleteLater() # 延迟删除堆中的对象
    31. print('del obj2之后')
    32. if __name__ == '__main__':
    33. app = QApplication(sys.argv)
    34. w = Windows()
    35. w.show()
    36. sys.exit(app.exec_())
  • 输出(在窗体关闭之前)
    1. <PyQt5.QtCore.QObject object at 0x00000298ECB9A670>
    2. <PyQt5.QtCore.QObject object at 0x00000298ECB9A700>
    3. <PyQt5.QtCore.QObject object at 0x00000298ECB9A790>
    4. <PyQt5.QtCore.QObject object at 0x00000298ECB9A820>
    5. del obj2之后
    6. obj3被释放了
    7. obj4被释放了
    • 从结果可以看到
    • del obj2执行之后,并没有被释放出来。因为del语句仅仅是删除变量到对象的引用和变量名称本身,并没有删除对象本身
    • obj3.deleteLater()之后,obj3和obj4立马被释放了。是因为deleteLater将obj3这个对象给删除了,obj3又是obj4的父对象,父对象被销毁时子对象跟着被销毁,所有obj4也被释放
  • 关于延迟删除示例
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. obj1 = QObject()
    13. obj2 = QObject()
    14. obj3 = QObject()
    15. obj4 = QObject()
    16. self.obj1 = obj1
    17. obj2.setParent(obj1) # 设置obj1为obj2的父对象,obj1销毁,obj2将自动销毁
    18. obj3.setParent(obj2) # 设置obj2为obj3的父对象,obj2销毁,obj3将自动销毁
    19. obj4.setParent(obj3) # 设置obj3为obj4的父对象,obj3销毁,obj4将自动销毁
    20. print(obj1)
    21. print(obj2)
    22. print(obj3)
    23. print(obj4)
    24. obj1.destroyed.connect(lambda :print('obj1被释放了'))
    25. obj2.destroyed.connect(lambda :print('obj2被释放了'))
    26. obj3.destroyed.connect(lambda :print('obj3被释放了'))
    27. obj4.destroyed.connect(lambda :print('obj4被释放了'))
    28. del obj2
    29. obj3.deleteLater()
    30. print('del obj2之后')
    31. print('obj3.deleteLater之后',obj3)
    32. if __name__ == '__main__':
    33. app = QApplication(sys.argv)
    34. w = Windows()
    35. w.show()
    36. sys.exit(app.exec_())
  • 输出结果(窗体关闭之前)
    1. <PyQt5.QtCore.QObject object at 0x000002635E22A670>
    2. <PyQt5.QtCore.QObject object at 0x000002635E22A700>
    3. <PyQt5.QtCore.QObject object at 0x000002635E22A790>
    4. <PyQt5.QtCore.QObject object at 0x000002635E22A820>
    5. del obj2之后
    6. obj3.deleteLater之后 <PyQt5.QtCore.QObject object at 0x000002635E22A790>
    7. obj3被释放了
    8. obj4被释放了
    • 结果可以看到,obj3.deleteLater()之后,obj3任然能够被打印出来。是因为deleteLater是延迟删除
    • deleteLater()并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象
  • 实例说明
    1. from PyQt5.Qt import *
    2. import sys
    3. class Windows(QWidget):
    4. def __init__(self):
    5. super().__init__()
    6. self.setWindowTitle('deleteLater')
    7. self.resize(980, 500)
    8. self.widget_list()
    9. def widget_list(self):
    10. self.add_widget()
    11. def add_widget(self):
    12. bnt1 = QPushButton(self)
    13. bnt1.setText('按钮1')
    14. bnt1.move(100,100)
    15. bnt2 = QPushButton(self)
    16. bnt2.setText('按钮2')
    17. bnt2.move(200, 100)
    18. bnt3 = QPushButton(self)
    19. bnt3.setText('按钮3')
    20. bnt3.move(300, 100)
    21. qw = QWidget(self)
    22. bnt4 = QPushButton()
    23. bnt4.setParent(qw)
    24. bnt4.setText('按钮4')
    25. bnt4.move(400, 100)
    26. del bnt2 # del 仅仅是删除变量到对象的引用和变量名称本身,并没有删除对象本身
    27. bnt3.deleteLater() # deleteLater() 直接删除对象
    28. qw.deleteLater() # deleteLater() 直接删除父对象之后,子对象自动删除
    29. if __name__ == '__main__':
    30. app = QApplication(sys.argv)
    31. w = Windows()
    32. w.show()
    33. sys.exit(app.exec_())
  • 展示效果


python中的堆、栈和队列

  • 队列:
    • 1、队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
  • 栈(stack):
    • 1、栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底,栈就相当于一个有底的水桶,出栈的过程就像倒出水的过程,是先进后出。
    • 2、栈(Stack)是操作系统在建立某个进程或者线程时(在支持多线程的操作系统中是线程)为这个线程建立的存储区域。
  • 堆(Heap):
    • 1、堆是在程序运行时,而不是在程序编译时,请求操作系统分配给自己某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。
    • 2、堆是指程序运行时申请的动态内存,而栈只是指一种使用堆的方法(即先进后出)。栈是先进后出的,但是于堆而言却没有这个特性,两者都是存放临时数据的地方。 对于堆,我们可以随心所欲的进行增加变量和删除变量,不要遵循什么次序,只要你喜欢。
  • 堆、栈、队列之间的区别是?
    • 1、堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。
    • 2、栈就是一个桶,后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来。(后进先出)
    • 3、队列只能在队头做删除操作,在队尾做插入操作.而栈只能在栈顶做插入和删除操作。(先进先出)
  • Python中的堆栈
    • 1、内存中的堆栈和数据结构中的堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构。
    • 2、内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区。
    • 3、代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。
    • 4、静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收。
    • 5、栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。
    • 6、堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。
  1. 在Python中,变量也称为对象的引用。因为,变量存储的就是对象的地址,变量通过地址引用了对象。
  2. 变量位于:栈内存,对象位于:堆内存
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号