PySide6 类之源QObject(1)

一、QObject介绍

Pyside中所有的类均继承自QObject,如我们前面所说的QWidgets、QtCore及QtGui。它是这些类的父类,它所有的功能在其继承类上都可以使用,这就是我们为什么要学QObject的原因。

如何查看一个类拥有哪些子类或其继承自哪个父类可以使用

__subclasses__()函数和mro()函数。

from PySide6.QtCore import QObject

for parent in QObject.mro():
    print(parent)

运行结果:

<class 'PySide6.QtCore.QObject'>
<class 'Shiboken.Object'>
<class 'object'>

从输出结果可以看出,QObject的父类python中的object,至于Shiboken这个库,如果只在Python中编写Qt应用程序,则不必担心Shiboken生成器的安装,但如果您想使用自己的绑定或使用Python扩展Qt/C++应用程序,您需要它封装C++文件。

二、QObject功能及作用

1.对象的名称及属性设置

API函数

参数说明

返回值

功能作用

setObjectName(self, name)

name:str

None

为obj设置一个名称,一般这个名称是唯一的,当做对象的ID来使用

objectName(self)

None

str

获取Obj对象的名称

setProperty(self,p_str, Any)

p_str:str

Any:任意类型

False

为obj对象设置属性及值,可以设置多组

property(self, p_str)

p_str:str

Any

通过属性名称获取obj对象的值

dynamicPropertyNames(self)

None

List[QByteArray]

获取obj对象通setProperty()设置的所有属性

setParent(self, obj)

Obj:QObject

None

设置obj的父对象

parent(self)

None

QObject

获取obj的父对象

setObjectName( )方法是为对象设置一个对象名,有的时候我们用for...in...或while的方法批量产生同一个对象的后,在接下来使用过程中,无法通过对象名获取并操作对象。此时就可以为每个对象设置一个对象名更方便的找到这个对象来操作。

setProperty( )原理类似,我们可以通过这个方法为对象添加一些标识,方便后面的操作。

setParent( )方法就是设置对象的父类,在Pyside中,设置父对象有两重含义:

一种是归属性质,即当我们将对象a设置为对象b的父对象,那么b就归属于a。对于实例控件来说,b就是a的子控件,b会显示在a的窗体范围内,字体、窗口样式等属性在没有单独设置的情况下与父对象a保持一致。

另一种是指向性质:简单来说,Pyside每个对象内部都有一个保存子对象链表,这个链表是否是dict实现的我们不去深究,但意思是一样的,所以为了说明问题,这里用字典进行举例。当我们将对象a设置为对象b的父对象,那么a的dict中就会添加一个元素,这个元素包含了子对象的对象名、地址等信息,如{"objName":0x000001A48BC3AB80}。这样我们不仅能够通过父对象找到子对象,也可以通过子对象找到父对象。这么做的目的同时还涉及到Pyside的内存管理机制,我们将在后面进行学习。

example:
QObject是抽象类,它无法直接显示,我们通过在其子类QWidget的初始化方法中进行设置来观察它的属性。

# -*- codeing:uft-8 -*-
from PySide6.QtWidgets import QWidget,QApplication
from PySide6.QtCore import QObject
import sys
class window(QWidget):
    def __init__(self):
        super(window, self).__init__()
        self.resize(600,600)
        obj1 = QObject()
        obj1.setObjectName("obj1")
        obj1.setProperty("level1",100)
        obj1.setProperty("level2",60)
        obj2 = QObject()
        obj2.setParent(obj1)
        print(obj1.property("level2"))
        print(obj1.dynamicPropertyNames())
        print(obj2.parent().objectName())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec())

运行结果:

60
[PySide6.QtCore.QByteArray(b'level1'), PySide6.QtCore.QByteArray(b'level2')]
obj1

2.对象的父子关系

API函数

参数说明

返回值

功能作用

children(self)

None

List[QObject]

获取obj的所有直接子对象列表

findChild(self,type,name , options)

type: type, name: str, options: PySide6.QtCore.Qt.FindChildOption = Instance(Qt.FindChildrenRecursively)

QObject

获取与参数条件及查找规则对应的子对象(只找一个)。

findChildren(self,type,name , options)

参数同findChild

List[QObject]

获取与参数条件对应的所有直接子对象

上述方法中type参数是对象的所属的类,具体应用中可能是按钮QPushButton、标签QLabel等。name参数就是对象的objectName,option是一个枚举类,具体枚举常量及含义如下:

枚举类

枚举常量

枚举值

功能描述

Qt.FindChildOption

FindDirectChildrenOnly

仅查看对象的直接子级。

FindChildrenRecursively

查看对象的所有子项(递归搜索)默认选项

example:
我们创建几个QObject对象,构建他们之间的父子关系,便于查看上述几个方法的用法。关系图如下:

# -*- codeing:uft-8 -*-
from PySide6.QtWidgets import QWidget,QApplication,QLabel
from PySide6.QtCore import QObject
import sys

class MyWindow(QWidget):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.resize(500,500)
        self.setWindowTitle("父子关系测试")
        #将测试内容放进实例方法中
        self.test_parent_or_child()
    def test_parent_or_child(self):
        obj0 = QObject()
        obj1 = QObject()
        obj1.setParent(obj0)
        obj1.setObjectName("obj1")
        obj2 = QObject()
        obj2.setParent(obj0)
        obj2.setObjectName("obj2")
        obj3 = QObject()
        obj3.setParent(obj1)
        obj3.setObjectName("obj3")
        obj4 = QObject()
        obj4.setParent(obj2)
        obj4.setObjectName("obj4")
        obj5 = QObject()
        obj5.setParent(obj2)
        obj5.setObjectName("obj4")
        lab1 = QLabel()
        #lab.setParent(obj0)

        print(obj0,obj1,obj2,obj3,obj4,obj5,sep="\n")
        print("obj1的父对象是{}".format(obj1.parent()))
        print("obj0的所有直接子对象是{}".format(obj0.children()))
        print("obj0使用findchild设置规则FindDirectChildrenOnly寻找子对象结果:{}".format(obj0.findChild(QObject,"obj4",Qt.FindChildOption.FindDirectChildrenOnly)))
        print("obj0使用findchild使用默认的规则寻找子对象结果:{}".format(obj0.findChild(QObject,"obj4")))
        print("obj0使用findchild不设置参数和规则寻找子对象结果:{}".format(obj0.findChild(QObject)))
        print("obj0使用findchildren设置规则FindDirectChildrenOnly寻找子对象结果:{}".format(obj0.findChildren(QObject,"obj4",Qt.FindChildOption.FindDirectChildrenOnly)))
        print("obj0使用findchildren不设置参数和规则寻找子对象结果:{}".format(obj0.findChildren(QObject)))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec())

运行结果:

<PySide6.QtCore.QObject(0x21790a00560) at 0x00000217911BDE00>
<PySide6.QtCore.QObject(0x21790a00580, name = "obj1") at 0x00000217911BDE40>
<PySide6.QtCore.QObject(0x21790a00620, name = "obj2") at 0x00000217911BDEC0>
<PySide6.QtCore.QObject(0x21790a00640, name = "obj3") at 0x00000217911BDF40>
<PySide6.QtCore.QObject(0x21790a00660, name = "obj4") at 0x00000217911BDFC0>
<PySide6.QtCore.QObject(0x21790a00680, name = "obj4") at 0x00000217911C2080>
obj1的父对象是<PySide6.QtCore.QObject(0x21790a00560) at 0x00000217911BDE00>
obj0的所有直接子对象是[<PySide6.QtCore.QObject(0x21790a00580, name = "obj1") at 0x00000217911BDE40>, <PySide6.QtCore.QObject(0x21790a00620, name = "obj2") at 0x00000217911BDEC0>]
obj0使用findchild设置规则FindDirectChildrenOnly寻找子对象结果:None
obj0使用findchild使用默认的规则寻找子对象结果:<PySide6.QtCore.QObject(0x21790a00660, name = "obj4") at 0x00000217911BDFC0>
obj0使用findchild不设置参数和规则寻找子对象结果:<PySide6.QtCore.QObject(0x21790a00580, name = "obj1") at 0x00000217911BDE40>
obj0使用findchildren设置规则FindDirectChildrenOnly寻找子对象结果:[]
obj0使用findchildren不设置参数和规则寻找子对象结果:[<PySide6.QtCore.QObject(0x21790a00580, name = "obj1") at 0x00000217911BDE40>, <PySide6.QtCore.QObject(0x21790a00640, name = "obj3") at 0x00000217911BDF40>, <PySide6.QtCore.QObject(0x21790a00620, name = "obj2") at 0x00000217911BDEC0>, <PySide6.QtCore.QObject(0x21790a00660, name = "obj4") at 0x00000217911BDFC0>, <PySide6.QtCore.QObject(0x21790a00680, name = "obj4") at 0x00000217911C2080>]

上面的例子中,需要说明的是,将lab.setParent(obj0)这行代码注释掉了,如果打开运行,程序将报错。这是因为,对QLabel这样的实体控件,其父对象Pyside在定义的时候要求其父对象必须也是实体控件也就是QWidget及其子类,而不能是QObject。 

另外,findchlidren()方法还有一种重载方法,

findChildren(self, type: type, pattern: Union[PySide6.QtCore.QRegularExpression, str], options: PySide6.QtCore.Qt.FindChildOption = Instance(Qt.FindChildrenRecursively)) 

其第二个参数pattern是一个QRegularExpression,即正则表达式。

3.父子关系的内存管理机制

前面我们已经明确了PySide的所有对象都是直接或者间接继承自QObject,那么父子关系的确立,就将控件之间建立了联系,层层父子关系的设立,就形成对象树(看前面的例子)。而这种父子关系在内存管理让上的表现就是父对象被销毁时,子对象也跟着被销毁。

example:

# -*- coding:utf-8 -*-
from PySide6.QtWidgets import QWidget,QApplication 
from PySide6.QtCore import QObject
import sys
class window(QWidget):
    def __init__(self):
        super(window, self).__init__()
        self.resize(600,600)
        self.setWindowTitle("父子关系的内存管理机制")
      
        self.My_Ui()

    def My_Ui(self):
        obj = QObject()
        self.obj = obj
        obj2 = QObject()    
        obj2.setParent(obj)     
        obj2.destroyed.connect(lambda :print("obj2被销毁"))
          
        del self.obj

if __name__ == '__main__':
    app = QApplication(sys.argv)   
    win = window() 
    win.show()
    sys.exit(app.exec_())

运行结果:

obj2被销毁

上面的代码中,因为obj没有设置父对象,程序运行后此行代码执行完obj就会被自动销毁,所以我们要用self.obj = obj这行代码,让obj作为win的属性,如此就有指针指向obj对象,运行时就不会被销毁。而obj2.destroyed.connect(lambda :print("obj2被销毁"))这行代码的作用是为了监听obj2的销毁事件,即当obj2被销毁时就会打印“obj2被销毁”这段话。因为obj不会被自动销毁,那么我们使用了del self.obj这行代码来手动销毁。

所以从上面看出,当我们销毁obj后,obj2也被销毁,继而触发信号,打印了“obj2被销毁”。

综上,对于pyside的父子对象具体到实体控件QWidget及其子类中,一个控件如果设置另一个控件为其父控件,那么作用有以下几点:

  • 子控件会包含在父控件内部
  • 子控件大小会受到父控件大小的裁剪,即不会超出父控件边界
  • 当父控件被销毁时,子控件也会被销毁。比如,我们关闭窗口,其内部的按钮、文本框等等都会被销毁。

4.信号与槽机制基本介绍

信号(Signal)与槽(slot)机制是PySide6的核心机制,主要作用是用于对象之间的通信。所谓的信号就是当一个控件发生改变时,向外界发出的信息,而槽则是执行某些操作的函数或方法。

我们举一个例生活中的例子并将其模拟为信号与槽的编程,以便于大家容易理解:

比如你很爱你老婆,对她百依百顺。于是,他跟你约定了一向服务规定:“手指敲桌子三下,你就需要马上去煮咖啡。”这里,手指就相当于控件,他的状态发生了改变(敲桌子三下)并向你发出了信号(赶紧去煮咖啡),你收到信号就去执行槽函数(煮咖啡这个函数),而煮咖啡这个函数也是你提前编辑好的,包含:“磨咖啡豆,煮咖啡,倒咖啡”等等内容。

回到程序中,比如我们想通过一个按钮来改变窗口的标题,这就需要用到信号与槽机制。具体为,当按钮被点击(clicked)就会向窗口发出信号,窗口接收到信号后就执行setWindowTitle()这个槽函数。具体代码如下:

# -*- coding:utf-8 -*-
from PySide6.QtWidgets import QApplication,QWidget,QPushButton
import sys
class window(QWidget):
    def __init__(self):
        super(window, self).__init__()
        #设置窗口大小
        self.resize(600,600)
        #设置窗口标题
        self.setWindowTitle("通过按钮改变窗口标题")

        self.My_Ui()

    def My_Ui(self):
        #创建一个按钮对象
        btn = QPushButton("按钮",self)
        #按钮点击信号链接到槽函数
        btn.clicked.connect(self.changeTitle)

    def changeTitle(self):
        #setWindowTitle()方法的作用是设置窗口的标题
        self.setWindowTitle("标题被改变了")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = window()
    win.show()
    sys.exit(app.exec())

运行结果:

20240904-030751

5.QObject的信号

信号

参数说明

返回值

功能作用

objectNameChanged(str)

Str-传递新对象名给槽函数

对象名称改变发射信号

destroyed(obj

Obj-被销毁的对象传递给槽函数

对象被销毁发射信号

# -*- coding:utf-8 -*-

from PySide6.QtWidgets import QWidget,QApplication
from PySide6.QtCore import QObject
import sys

class window(QWidget):
    def __init__(self):
        super(window, self).__init__()
        self.resize(600,600)
        self.move(300,300)
        self.setWindowTitle("QObject信号测试1")

        obj = QObject()
        obj.setParent(self)
        obj.setObjectName("111")
        #链接到改名槽函数
        obj.objectNameChanged.connect(lambda name:self.name_change(name))
        #链接到销毁对象槽函数
        obj.destroyed.connect(destroy_obj)
        # 改变对象名
        obj.setObjectName("222")
        
    def name_change(name):
        print("对象的名称被改变为:",name)

    def destroy_obj(obj):
        print("{}被销毁了".format(obj))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = window()
    win.show()
    sys.exit(app.exec_())

运行结果:

对象的名称被改变为 222
<PySide6.QtCore.QObject object at 0x000001C84110E040>被销毁了

当程序运行至obj.setObjectName("222")这行代码时,就触发了objNameChanged这个信号,信号连接到槽函数后打印了“对象的名称被改变为 222”。而此时,程序还在运行,那么窗口win还一直存在,那么其子对象obj就不会被销毁,故不会触发destroyed这个信号,但当我们关闭窗口,win被销毁,obj也就不存在了,那么就会触发destroyed这个信号,就会被销毁了”。


API函数

参数说明

返回值

功能作用

QObject.disconnect()

None

None

断开对象所有信号与槽函数的链接

Signal.disconnect()

None

None

断开对象信号链接的所有槽函数

Signal.disconnect(menthod)

Menthod-槽函数名称

None

断开对象信号链接的特定槽函数

# -*- coding:utf-8 -*-

from PySide6.QtWidgets import QWidget,QApplication
from PySide6.QtCore import QObject
import sys

class window(QWidget):
   def __init__(self):
      super(window, self).__init__()


      obj = QObject(self)
      obj.objectNameChanged.connect(self.name_change)
      obj.objectNameChanged.connect(self.change_name)
      obj.destroyed.connect(lambda :print("obj被销毁了"))
      #obj.disconnect()
      #obj.objectNameChanged.disconnect()
      #obj.objectNameChanged.disconnect(self.name_change)
      obj.setObjectName("对象1")

   def name_change(self,name):
      print("obj的名称改变为{}".format(name))

   def change_name(self):
      print("obj改变了名字触发了信号")

if __name__ == '__main__':
   app = QApplication(sys.argv)
   win = window()
   win.show()
   sys.exit(app.exec())

上面的例子中,需要将注释分别打开观察结果。


API函数

参数说明

返回值

功能作用

blockSignals(self, b)

b:bool

bool

临时断开/恢复信号与槽的链接,成功断开/恢复,返回True,否则返回Flase

signalsBlocked(self)

None

bool

信号是否被断开链接

sender()NoneQObject

如果信号被激活,返回信号发送对象。否则返回None

sender()方法非常有用,在多个信号链接同一个槽函数时,我们就可以通过sender()信号来获取信号发送对象,条件判断后进行操作。

6.QObject的类型判定

API函数

参数说明

返回值

功能作用

inherits(self, classname)

calssname:Str

bool

判断一个对象是否直接或间接继承自一个类

isWidgetType(self)

None

bool

判断一个对象是否是QWidget类型

等同于inherits(QWidget),只是速度更快

isWindowType(self)

None

bool

判断一个对象是否是Qwindow类型

等同于inherits(QWindow),只是速度更快

这里需要说明的是,isWindowType(self)通常用不上,因为大部分控件都不会用到QWindow类型,查看文档的时候,我们发现在使用OpenGL时,才可能选用。所以我们不用去关注它,只需掌握前面两个类型判定的方法就可以了。

关于类的判定,当然我们也可以用python的instance()方法。

# -*- coding:utf-8 -*-
from PySide6.QtCore import QObject
from PySide6.QtWidgets import QWidget,QApplication,QPushButton
from PySide6.QtGui import QWindow
import sys

class window(QWidget):
   def __init__(self):
      super(window, self).__init__()
      self.resize(600,600)

      obj = QObject()
      obj.setParent(self)
      btn = QPushButton(self)
      mywin = QWindow()
      list1 =[obj,btn,mywin]
      for i in list1:
         print("{}是否继承自QWidget".format(i),i.inherits("QWidget"))
         print("{}是否是Qwidget类型".format(i),i.isWidgetType())
         print("{}是否是Qwindow类型".format(i), i.isWindowType())
   def name_change(self,str):
      print("obj的名称改变为{}".format(str))
   def change_name(self):
      print("obj改变了名字触发了信号")
if __name__ == '__main__':
   app = QApplication(sys.argv)
   win = window()
   win.show()
   sys.exit(app.exec())

运行结果:

<PySide6.QtCore.QObject(0x25dda3034e0) at 0x0000025DDC221980>是否继承自QWidget False
<PySide6.QtCore.QObject(0x25dda3034e0) at 0x0000025DDC221980>是否是Qwidget类型 False
<PySide6.QtCore.QObject(0x25dda3034e0) at 0x0000025DDC221980>是否是Qwindow类型 False
<PySide6.QtWidgets.QPushButton(0x25ddc3e12f0) at 0x0000025DDC221A00>是否继承自QWidget True
<PySide6.QtWidgets.QPushButton(0x25ddc3e12f0) at 0x0000025DDC221A00>是否是Qwidget类型 True
<PySide6.QtWidgets.QPushButton(0x25ddc3e12f0) at 0x0000025DDC221A00>是否是Qwindow类型 False
<PySide6.QtGui.QWindow(0x25dda2ed1f0) at 0x0000025DDC221A80>是否继承自QWidget False
<PySide6.QtGui.QWindow(0x25dda2ed1f0) at 0x0000025DDC221A80>是否是Qwidget类型 False
<PySide6.QtGui.QWindow(0x25dda2ed1f0) at 0x0000025DDC221A80>是否是Qwindow类型 True

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值