Skip to content

控件继承结构

QObject是所有Qt对象的基类,QWidget是所有的可视控件的基类

QObject基类

属性相关操作的API

API描述
setObjectName("唯一名称")给一个Qt对象设置一个名称,一般这个名称是唯一的,当做对象的ID来使用
objectName()获取一个Qt对象的名称
setProperty("属性名称",值)给一个Qt对象动态的添加一个属性与值
property("属性名称")获取一个对象的属性值
dynamicPropertyNames()获取一个对象中所有通过setProperty()设置的属性名称
案例--qssID选择器

详情代码见实用案例积累的OObject案例--qssID选择器

案例--qss的属性选择器

详情代码见实用案例积累的QObject案例--qss的属性选择器

QObject父子对象操作的API

使两个QObject对象产生一个父子关系

API描述
setParent(parent)将一个对象设置成为另外一个对象的父对象,父对象只能设置一个,括号里的parent是父对象
parent()获取某一个对象父对象
children()获取某一个对象的所有子对象
findChild(参数1,参数2,参数3)通过一些条件查询某一个子对象,参数1是类型,可以是类型如:QObject,也可以是类型元组如:(QPushButton,QLabel) 参数2是名称,通过参数名称进行查找,但是要先对其进行setObjectName()设置名称,参数2可以省略 参数3是查找选项:Qt.FindChildrenRecursively 递归查找(默认选项)先在一级子对象中查找,如果没有会去二阶子对象中继续查找,继续向下查找;Qt.FindDirectChildrenOnly 只查找直接子对象
findChildren(参数1,参数2,参数3)通过一些条件查询多个子对象,参数1是类型,可以是类型如:QObject,也可以是类型元组如:(QPushButton,QLabel) 参数2是名称,通过参数名称进行查找,但是要先对其进行setObjectName()设置名称,参数2可以省略 参数3是查找选项:Qt.FindChildrenRecursively 递归查找(默认选项);Qt.FindDirectChildrenOnly 只查找直接子对象

一个对象的父对象只能有一个,而且是以程序后设置的为主,将之前设置的进行覆盖。

通过children()获取到的子对象是直接相连的子对象,不能间接跳级获取。

QObjectQLabel并不能有直接的父子关系,QObject是不可见的,要在相关的控件下才能测试。

相关的继承详情代码见实用案例积累的QObject案例--对象的父子关系操作

对象的内存管理机制

QObject继承树

Qt中的任何控件类都继承自QObject类,所有的对象都是直接或者间接继承自QObject,若给定两个控件类别,设置父子关系,为他们做了一个前提铺垫,QObjects在一个对象数中组织他们自己,当创建一个QObject时,如果使用了其他对象作为父对象,那么它就会被添加到父对象的children列表中,当父对象被销毁时,其相应的子对象也会被销毁。(内存管理机制的)

如果应用在GUI控件QWidget中,当一个控件设置了父控件,子控件会包含在父控件的内部,子控件会受父控件区域的裁剪(子控件不可能超过父控件的范围),父控件被删除时,子控件会被自动的删除。

相关案例应用场景:一个对话框,上面有很多操作按钮,按钮和对话框是父子控件,当我们操作的时候,是操作对话框控件本身,而不是操作子控件(按钮),当对话框被删除时,内部的子控件也会自动的删除,防止产生内存泄露。

父子对象关系

如果一个控件,没有任何的父控件,那么就会被当成顶层控件(窗口),多个顶层窗口相互独立;如果想要一个控件被包含在另外一个控件内部,那么就要设置父子关系,设置完后其显示位置受父控件约束(其子控件的大小不可能超出父控件的范围),生命周期被父控件接管(父控件被销毁其子控件也会被销毁)。

python
# 设置两个独立的顶层窗口,同时展示
win1 = QWidget()
win1.show()

win2 = QWidget()
win2.show()
python
# 将win1设置为win2的父对象,将窗口2放到窗口1的内部
win2.setParent(win1)
python
# 将子控件添加到父控件的两种方法,不在类中操作的演示
win1 = QWidget()
# 添加父控件的第一种方式
label1 = QLabel()
label1.setText("label1")
label1.setParent(win1)
# 添加父控件的第二种方式
btn = QPushButton(win1)
btn.setText("btn")
win1.show()
案例--通过父子关系设置所有QLabel控件设置背景颜色

创建一个窗口包含多个子控件QWidgetQLabel,要求让所有的QLabel类型控件都设置背景颜色为cyan,即使后续加入QLabel也是这个背景颜色,详细代码见:QObject案例--对象的父子操作设置QLabel背景颜色

信号的处理

信号与槽机制:用于对象之间进行通讯。

信号:当一个控件的状态发生改变时,向外界发出的信息。

槽:一个执行某些操作的函数/方法。

所有继承自QWidget的控件都支持信号与槽机制。

一个信号可以连接多个槽函数,一个槽可以监听多个信号,一个信号也可以连接另一个信号(信号关联)

信号的参数可以是任何一个python类型

QObejct相关的信号:

  • obj.destroyed 当一个对象被销毁掉时,就会触发信号的发射

  • obj.objectNameChanged 当对象的名称发生改变时,就会触发信号的发射

QObejct提供操作信号与槽的API

API描述
obj.connect建立连接 如obj.objectNameChanged.connect(槽函数) 建立信号obj.objectNameChanged 与槽函数的连接
obj.disconnect取消连接 如obj.objectNameChanged.disconnect(槽函数) 取消信号obj.objectNameChanged 与槽函数的连接
obj.blockSignals(bool)临时(取消)阻止指定控件所有的信号与槽的连接,bool为布尔类型的数据,输入TrueFalse,输入True表示临时阻断的后面信号与槽之间的连接,输入False表示临时恢复的后面信号与槽之间的连接
obj.signalsBlocked()获取信号是否被阻止,True表示断开连接;False表示没有断开连接
obj.receivers(信号)返回连接到信号接收器(槽)的数量,如:print(self.obj.receivers(self.obj.objectNameChanged))
案例--通过信号与槽在修改标题前加前缀“mypyqt”

使用信号与槽连接来为修改的窗口标题添加前缀,同时使用了临时中断连接的函数防止进入死循环。详细代码见:QObject案例--通过信号与槽在修改标题前加前缀“mypyqt”

类型判定

用于判定一个对象的类型,从而到达类型过滤,比如判定它是不是一个控件,或者判定它继承哪个类,判定相关的API如下:

API描述
isWidgetType()判定是否为一个控件类型,输出True或者FalseQObject不是控件,会显示False
inherits(父类)判断某个对象是否继承自某个父类,继承包括直接继承和间接继承,输出True或者False,输入的父类格式如下:"QWidget",要加双引号
python
# 可以通过将相关控件放在数组中进行遍历查找
obj = QObject()
w = QWidget()
btn = QPushButton()
label = QLabel()
objs = [obj, w, btn, label]
for o in objs:
    print(o.isWidgetType())
    print(o.inherits("QWidget"))
    print(o.inherits("QPushButton"))

对象删除

移出某一个对象的时候需要用到对象删除的API,其API如下所示:

API描述
obj.deleteLater()稍后删除(下一个循环才删除):deleteLater()并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁该对象,这样的好处是可以在这些延迟删除的时间内完成一些操作,坏处是内存释放不及时。删除一个对象时,也会解除与它父对象之间的关系。

obj.deleteLater()删除时先解除父子关系在删除

删除某个控件直接输入:label.deleteLater()即可删除label控件,运行程序后不会在显示窗口上显示。

事件处理机制

事件处理机制算是信号与槽机制的一个补充,信号与槽机制是对事件处理机制的高级封装。信号与槽机制更加贴近于开发人员,事件处理机制更加偏向底层(远离用户)。

一个按钮点击后在pycharm中显示“按钮被点击了”该信号与槽机制,在计算机中是如何进行执行的:

在操作系统中有以上的应用程序窗口在运行,当用户点击按钮会产生一个事件消息,操作系统接收到事件消息,并发现其产生自哪一个应用程序中,到时候操作系统会把消息分发给该应用程序的消息队列,应用程序的消息循环在程序运行的时候开启的(app.exec_())不断的扫描队列中有没有新的消息,当扫到事件消息,就会把其包装成QEvent对象进行分发处理,首先分发给QApplication对象中notify(receiver,evt)方法,最后还会发射给按钮对象,默认发生给按钮对象的event方法,再根据事件的类型(用户点击,双击,滑动等等)进行一个具体的分发,分发给特定的函数,当这样的事件函数被调用时,会自动的在事件函数内部再次的向外界发射一个信号,对应的信号所连接的槽才会被执行,通过继承,在子类中重写notify(receiver,evt)方法:程序运行时优先调用子类的方法,子类找不到再去找父类。

python
def notify(self, QObject, QEvent):
# QObject表示事件的接收者receiver,QEvent表示被包装的事件对象evt
	return super.notify(receiver,evt)  # 将方法传给父类,让父类去处理

一般情况下,能通过信号与槽机制解决需求,就用信号与槽,如果信号与槽解决不了,再考虑事件处理机制,一层层去做。

详细案例代码见:QObject案例--事件处理机制

定时器

功能:定时或者每隔一个固定的间隔去做某件事情

QObject类中的定时器相关的API

API描述
startTimer(ms, Qt.TimerType) -> timer_id开启一个定时器,ms表示毫秒;Qt.TimerType中有三个参数:Qt.PreciseTimer:精确定时器(尽可能保持毫秒准确);Qt.CoarseTimer:粗定时器(5%的误差间隔);Qt.VeryCoarseTimer:很粗定时器(只能到秒级);timer_id:定时器唯一标识符
killTimer(timer_id)根据定时器ID,杀死定时器
timerEvent()定时器执行事件
案例--通过定时器显示倒计时

创建一个窗口,设置一个子控件QLabel,显示十秒倒计时,倒计时结束停止。详细见实用案例积累:QObject案例--定时器显示倒计时

Released under the MIT License.