本文主要是介绍008.PyQt5_QObject基类_对象名称属性和父子关系,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
QObject
- 对象名称,属性(API)
setObjectName('唯一名称') # 给Qt对象设置一个名称,当做对象的ID来使用 objectName() # 获取Qt对象的名称 setProperty('属性名称','值') # 给Qt对象动态的添加一个属性与值(键值对模式) property("属性名称") # 通过对象的属性名称获取其属性值 dynamicPropertyNames() # 获取一个对象中所有通过 setProperty() 设置的属性名称
- 应用场景:
- 用于qss的ID选择器,属性选择器(方便统一设置样式:标签名#ID选择器[属性名='属性值'])
- 用于装饰器的信号与槽
- 父子对象的操作
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控件的影响(父控件被删除,子控件自动删除)
功能作用
- 对象名称,属性(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)——红色边框、红色字体。
- 涉及知识点:
- qss 样式表
- 文件读取。
- 对象 \ 属性名称设置。
- 先介绍一下 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)
- 关系图:
- qss样式表
- 父子对象的操作
- 对象父类、子类设置和查找方法
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
- 应用场景
- 对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 控件展示层面上),受父控件区域裁剪(子控件不可能超出父控件的范围),父控件被删除时,子控件会自动删除(窗口关闭后上面的图标也会被关闭,节省内存)
- 对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一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。
- 在Python中,变量也称为对象的引用。因为,变量存储的就是对象的地址,变量通过地址引用了对象。
- 变量位于:栈内存,对象位于:堆内存
这篇关于008.PyQt5_QObject基类_对象名称属性和父子关系的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!