008.PyQt5

编程入门 行业动态 更新时间:2024-10-09 00:40:10

008.PyQt5

008.PyQt5

QObject


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

功能作用

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

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

del和deleteLater()

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

更多推荐

008.PyQt5

本文发布于:2024-02-13 06:15:47,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1757071.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!