控件继承结构
QObject
是所有Qt
对象的基类,QWidget
是所有的可视控件的基类
QObject
基类
属性相关操作的API
API | 描述 |
---|---|
setObjectName("唯一名称") | 给一个Qt 对象设置一个名称,一般这个名称是唯一的,当做对象的ID 来使用 |
objectName() | 获取一个Qt 对象的名称 |
setProperty("属性名称",值) | 给一个Qt 对象动态的添加一个属性与值 |
property("属性名称") | 获取一个对象的属性值 |
dynamicPropertyNames() | 获取一个对象中所有通过setProperty() 设置的属性名称 |
案例--qss
的ID
选择器
详情代码见实用案例积累的OObject
案例--qss
的ID
选择器
案例--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()
获取到的子对象是直接相连的子对象,不能间接跳级获取。
QObject
与QLabel
并不能有直接的父子关系,QObject
是不可见的,要在相关的控件下才能测试。
相关的继承详情代码见实用案例积累的QObject
案例--对象的父子关系操作
对象的内存管理机制
QObject
继承树
Qt
中的任何控件类都继承自QObject
类,所有的对象都是直接或者间接继承自QObject
,若给定两个控件类别,设置父子关系,为他们做了一个前提铺垫,QObjects
在一个对象数中组织他们自己,当创建一个QObject
时,如果使用了其他对象作为父对象,那么它就会被添加到父对象的children
列表中,当父对象被销毁时,其相应的子对象也会被销毁。(内存管理机制的)
如果应用在GUI
控件QWidget
中,当一个控件设置了父控件,子控件会包含在父控件的内部,子控件会受父控件区域的裁剪(子控件不可能超过父控件的范围),父控件被删除时,子控件会被自动的删除。
相关案例应用场景:一个对话框,上面有很多操作按钮,按钮和对话框是父子控件,当我们操作的时候,是操作对话框控件本身,而不是操作子控件(按钮),当对话框被删除时,内部的子控件也会自动的删除,防止产生内存泄露。
父子对象关系
如果一个控件,没有任何的父控件,那么就会被当成顶层控件(窗口),多个顶层窗口相互独立;如果想要一个控件被包含在另外一个控件内部,那么就要设置父子关系,设置完后其显示位置受父控件约束(其子控件的大小不可能超出父控件的范围),生命周期被父控件接管(父控件被销毁其子控件也会被销毁)。
# 设置两个独立的顶层窗口,同时展示
win1 = QWidget()
win1.show()
win2 = QWidget()
win2.show()
# 将win1设置为win2的父对象,将窗口2放到窗口1的内部
win2.setParent(win1)
# 将子控件添加到父控件的两种方法,不在类中操作的演示
win1 = QWidget()
# 添加父控件的第一种方式
label1 = QLabel()
label1.setText("label1")
label1.setParent(win1)
# 添加父控件的第二种方式
btn = QPushButton(win1)
btn.setText("btn")
win1.show()
案例--通过父子关系设置所有QLabel
控件设置背景颜色
创建一个窗口包含多个子控件QWidget
和QLabel
,要求让所有的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 为布尔类型的数据,输入True 和False ,输入True 表示临时阻断的后面信号与槽之间的连接,输入False 表示临时恢复的后面信号与槽之间的连接 |
obj.signalsBlocked() | 获取信号是否被阻止,True 表示断开连接;False 表示没有断开连接 |
obj.receivers(信号) | 返回连接到信号接收器(槽)的数量,如:print(self.obj.receivers(self.obj.objectNameChanged)) |
案例--通过信号与槽在修改标题前加前缀“mypyqt”
使用信号与槽连接来为修改的窗口标题添加前缀,同时使用了临时中断连接的函数防止进入死循环。详细代码见:QObject
案例--通过信号与槽在修改标题前加前缀“mypyqt”
类型判定
用于判定一个对象的类型,从而到达类型过滤,比如判定它是不是一个控件,或者判定它继承哪个类,判定相关的API
如下:
API | 描述 |
---|---|
isWidgetType() | 判定是否为一个控件类型,输出True 或者False ,QObject 不是控件,会显示False |
inherits(父类) | 判断某个对象是否继承自某个父类,继承包括直接继承和间接继承,输出True 或者False ,输入的父类格式如下:"QWidget" ,要加双引号 |
# 可以通过将相关控件放在数组中进行遍历查找
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)
方法:程序运行时优先调用子类的方法,子类找不到再去找父类。
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
案例--定时器显示倒计时