【PyQT5 最全学习笔记】:第一节 QObject 所有API 讲解及用法实例演示

前言:

本人之前使用过一段时间的PyQt5,发现用它来写GUI真的很方便,而且界面也很简洁、清晰,对初学者来说真的非常友好。但因时间关系,一直未完整的学习过,所以遇到一些问题、想要实现一些高级功能的时候。也只能在网上找别人的博客,依葫芦画瓢,东学一点西学一点,不成体系。

最近稍微闲下来一点,在网上找了视频来系统的学习下,视频好像是撩课顺子哥的(我是淘宝9块9买的…,所以原视频到底出自哪,也不是特别清楚哈)。发现顺子哥的PyQt5视频内容十分全面,把整个PyQt5的所有控件、类的API都讲了一遍,不愧是"社会我顺哥,人狠话还多"…,真大佬。但其笔记真的是有点乱…,所以本着学习及分享的精神把其上课内容和笔记梳理了一遍。

以下是笔记正文:

PyQt 程序基本结构分析

要使一个PyQt程序成功运行的固定结构:

# 1、创建一个应用程序对象
app = QApplication(sys.argv)
# 2、控件操作(创建控件、控件设置)
window = QWidget()  # 创建控件
window.resize(300,400)  # 控件设置(尺寸设置)
# 3、展示控件
window.show()
# 4、应用程序的执行, 进入到消息循环
# 让整个程序开始执行,并且进入到消息循环(无限循环)
# 检测整个程序所接收到的用户的交互信息
sys.exit(app.exec_())

1、QObject 对象名称及属性相关API及实例

QObject 是所有PyQT对象的基类,要真正系统的学好PyQt5, QObject 类是必须先掌握好的。

1.1 API 一览:

项目 说明
setObjectName 给一个Qt对象设置一个名称
objectName 获取一个Qt对象的名称
setProperty 给一个Qt对象动态的添加一个属性与值
property 获取一个对象的属性值
dynamicPropertyNames 获取一个对象中所有通过setProperty设置的属性名称

1.2 应用场景及实例

1.2.1 应用场景

(一):用于qss的ID选择器,属性选择器,方便统一设置样式;
(二):用于装饰器的信号与槽;
(三):其他。

1.2.2 实例:创建多个用于信息提示的QLabel

(一):要求
1、凡是提示的QLabel控件, 都要求设置:字体大小为25px,字体颜色为灰色,边框圆角为8px;
2、信息提示分多个级别:
(1):正常(normal): 绿色边框,绿色字体;
(2):警告(warning): 橙色边框,橙色字体;
(3):错误(error): 红色边框,红色字体。

注:这里使用到了QSS样式表,由于现在还没学习到,部分读者现在看起来可能不是特别清楚,但简单的使用应该还是可以看得懂的(和CSS一个意思)。

QSS文件内容:

扫描二维码关注公众号,回复: 11299189 查看本文章
QLabel#notice {
    font-size: 20px;
    color: gray;
    border: 1px solid gray;
    border-radius: 8px;
}

QLabel#notice[notice_level="normal"] {
    color: green;
    border-color: green;
}

QLabel#notice[notice_level="warning"] {
    color: orange;
    border-color: orange;
}

QLabel#notice[notice_level="error"] {
    color: red;
    border-color: red;
}

(二):代码部分
读取QSS样式表:

# 读取QSS 样式表
with open("QObject.qss", "r") as f:
    qApp.setStyleSheet(f.read())

创建基本控件:

# 创建应用程序
app = QApplication(sys.argv)

# QObject 是PyQt所有对象的基类,但是其本身不是一个控件
# 这里为了实例演示,借其QWidget子类来进行操作
window_1 = QWidget()
# 设置窗口尺寸
window_1.resize(300,300)

创建label普通标签:

# 创建label标签,普通标签
label0 = QLabel(window_1)
# 设置标签对象名称,对象名称为:notice
label0.setObjectName("notice")
# 设置标签内文字内容
label0.setText("普通标签")

创建要求里的正常信息提示标签:

# 创建label标签,正常标签
label1 = QLabel(window_1)
# 设置标签对象名称,对象名称为:notice
label1.setObjectName("notice")
# 移动标签位置
label1.move(0,50)
# 设置标签对象属性与值,对象属性为:notice_level,值为:warning
label1.setProperty("notice_level", "normal")
# 设置标签内文字内容
label1.setText("正常信息提示")

警告标签,错误标签、未定义名称属性的标签:(这里我们还创建了一个按钮控件。)

# 警告标签
label2 = QLabel(window_1)
label2.move(0, 100)
label2.setObjectName("notice")
label2.setProperty("notice_level", "warning")
label2.setText("警告信息提示")

# 错误标签
label3 = QLabel(window_1)
label3.move(0, 150)
label3.setObjectName("notice")
label3.setProperty("notice_level", "error")
label3.setText("错误标签提示")

# 未使用QSS样式表的标签
# 即未设置label标签的对象名称,属性
label3 = QLabel(window_1)
label3.setText("未设置名称属性的标签")
label3.move(0, 200)

# 创建按钮控件,也对其设置名称,看其是否会匹配到QSS样式表里的样式
btn = QPushButton(window_1)
btn.setObjectName("notice")
btn.setText("btn")
btn.move(0, 250)

窗口展示及# 执行应用程序,进入消息循环

# 显示窗口
window_1.show()
# 执行应用程序,进入消息循环
sys.exit(app.exec_())

将以上代码放在一个py文件中,QSS样式表文件Object.qss也创建成功并且路径也正确的话,我们得到的窗口应该是下面这样的:
信息提示标签窗口
(三):获取标签对象的名称,属性名称,属性值:

# 获取label1标签对象的名称,属性名称,属性值
print(label1.objectName())
print(label1.dynamicPropertyNames())
print(label1.property("notice_level"))
# ------运行结果------
# notice
# [PyQt5.QtCore.QByteArray(b'notice_level')]
# normal
# ------运行结果------

(四):小结

1、QObject类里的setObjectName,setProperty,经常被用在QSS样式表的ID选择器,属性选择器上,方便统一设置样式。
2、可通过objectName,dynamicPropertyNames,property方法来获取对象的名称,属性名称及属性值。
3、Qobject类为PyQt里所有对象的基类,就是说所有PyQT5的对象都具有上述方法。

2、QObject 父子对象操作API及实例

2.1 API 一览:

项目 说明
setParent 设置父对象
parent 获取父对象
children 获取所有直接子对象
findChild 获取某一个指定名称和类型的子对象
findChildren 获取某多个指定名称和类型的子对象

2.2 应用场景及实例

2.2.1 应用场景

(一):涉及到Qt对象内存管理机制;
(二):如果一个控件,没有任何父控件,那么就会被当成顶层控件(窗口),多个顶层窗口相互独立;
(三):如果想要一个控件被包含在另外一个控件内部,就需要设置父子关系;
(四):子控件显示位置受父控件约束,生命周期也被父对象接管(父控件被关闭后,子控件也会被关闭)。

2.2.2 实例一:父子结构关系练习

创建如下父子结构的关系:
父子结构关系图
创建应用及QObject对象:

app = QApplication(sys.argv)

# 创建QObject对象
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)

查询对象的父对象:parent() 方法

# 查询对象的父对象
print("obj0:",obj0)
# 查询obj1对象的父对象
print(obj1.parent())
# ------运行结果------
# obj0: <PyQt5.QtCore.QObject object at 0x000001774A308438>
# <PyQt5.QtCore.QObject object at 0x000001774A308438>
# ------运行结果------

查询对象的直接子对象: children() 方法

注:直接子对象的意思就是,不包含子对象的子对象,仅仅是子对象…(应该解释清楚了吧…)。

# 查询对象的直接子对象
print(obj0.children())
# obj0 有两个直接子对象,obj1和obj2
# ------运行结果------
# [<PyQt5.QtCore.QObject object at 0x000001B3E22A84C8>,
# <PyQt5.QtCore.QObject object at 0x000001B3E22A8558>]
# ------运行结果------

查询对象的某一个子对象 findChild() 方法

# 查询对象的某一个子对象
obj3.setObjectName('3')
print("obj3:",obj3)
print(obj0.findChild(QObject, "3", Qt.FindChildrenRecursively))
# ------运行结果------
# obj3: <PyQt5.QtCore.QObject object at 0x000002AE467595E8>
# <PyQt5.QtCore.QObject object at 0x00000184BD7585E8>
# ------运行结果------

findChild 方法参数解析:

findChild(type, name, options]
type:查找类型,name:名称(SetObjectName 可省略)
options:
Qt.FindChildrenRecursively(递归查找 默认选项)
Qt.FindDirectChildrenOnly (只查找直接子对象)

如果是:

print("obj1:",obj1)
print(obj0.findChild(QObject)) # 查找obj0对象为QObject类型,默认:递归查找
# ------运行结果------
# obj1: <PyQt5.QtCore.QObject object at 0x0000022582C684C8>
# <PyQt5.QtCore.QObject object at 0x0000022582C684C8>
# ------运行结果------

结果分析:按查询条件应该可以查询到多个,但是findChild方法只是查找一个,即将第一个查询到的结果返回。

按条件查询对象的所有子对象 findChildren() 方法

# 按条件查询对象的所有子对象
# obj0的所有子对象有obj1,obj2,obj3,obj4。obj5
print(obj0.findChildren(QObject))
# ------运行结果------
# [<PyQt5.QtCore.QObject object at 0x000001C029D484C8>,
# <PyQt5.QtCore.QObject object at 0x000001C029D485E8>,
# <PyQt5.QtCore.QObject object at 0x000001C029D48558>,
# <PyQt5.QtCore.QObject object at 0x000001C029D48678>,
# <PyQt5.QtCore.QObject object at 0x000001C029D48708>]
# ------运行结果------

findChildren 方法参数解析:
和findChildren 方法中的参数一样,只是返回值不一样,一个是返回一个,一个是返回多个。

findChild(type, name, options]
type:查找类型,name:名称(SetObjectName 可省略)
options:
Qt.FindChildrenRecursively(递归查找 默认选项)
Qt.FindDirectChildrenOnly (只查找直接子对象)

小结:
1、setParent:用来设置对象的父对象,一个对象只能有一个父对象,子控件显示位置受父控件约束,生命周期也被父对象接管。
2、parent:获取该对象的父对象。
3、children:获取该对象的直接子对象。
4、findChild:按条件查询对象的子对象,只返回一个结果。如有多个结果返回查找到的第一个。
5、findChildren:按条件查询对象的子对象,返回多个结果。

2.2.3 实例二:为页面某一类控件快捷设置统一样式

要求:
创建一个窗口, 包含多个子控件QWidget和QLabel,
要求让所有的QLabel类型子控件都设置背景颜色为青色(cyan)。

分析:
为满足上面实例要求,我们可以通过QSS样式表来设置,但是也可以通过父子对象关系来实现对页面类某一类控件进行统一样式设置。

方法如下:

app = QApplication(sys.argv)
window = QWidget()
window.resize(300,300)
# 创建标签控件
label1 = QLabel()
# 设置父控件
label1.setParent(window)
label1.setText('标签1')
# 创建标签控件,同时设置父控件
label2 = QLabel(window)
label2.setText('标签2')
label2.move(0,50)
# 创建按钮控件
btn = QPushButton(window)
btn.setText("按钮")
btn.move(0, 100)

# 用父控件查找子控件的方式来设置样式
# 先查找到window控件所有未QLabel类型的子控件,再将其背景颜色设置为青色
for widget in window.findChildren(QLabel):
    widget.setStyleSheet("background-color: cyan;")

# 显示窗口
window.show()
# 执行应用程序,进入消息循环
sys.exit(app.exec_())

3、QObject 信号处理API及实例

3.1 API 一览:

项目 说明
信号.connect(槽) 连接信号与槽
obj.disconnect() 取消连接信号与槽
widget.blockSignals(bool) 临时(取消)阻止指定控件所有的信号与槽的连接
widget.signalsBlocked() 查看信号是否被阻止
widget.receivers(信号) 返回连接到信号的接收器数量

3.2 应用场景及实例

3.2.1 应用场景

(一):监听信号, 响应用户行为。
(二):信号与槽机制。

3.2.1 实例一:信号的连接与阻止

信号与槽是PyQt里控件和方法函数交互的重要方法。
了解控件具有什么样的信号,该信号在什么时候会被触发, 触发之后要做什么事情(连接哪个槽函数)。

信号连接:

app = QApplication(sys.argv)
window = QWidget()
window.resize(300,200)
label1 = QLabel(window)
label1.setText('信息提示标签')

def change_label_text():
    label1.setText('按钮被按下')

button1 = QPushButton(window)
button1.setText('请按按钮')
button1.move(0,50)
#  按钮点击信号绑定change_label_text函数
button1.clicked.connect(change_label_text)

解析:
当按被点击后(clicked信号)触发change_label_text函数,执行函数内的语句,即label标签文字变为:按钮被按下。

取消某控件所有信号的连接 disconnect()方法

button1.disconnect()  
# 加上此句后button按钮的所有信号被取消,按钮点击无效
# 注意,这里是取消button1对象的所有信号连接

取消控件特定信号的连接

button1.clicked.disconnect() # 取消button1对象的按钮点击信号连接的槽函数
# 无论是取消对象的所有信号连接还是对象的单个信息连接后,
# 都要再次将信号与槽函数绑定,才能恢复业务逻辑

重写连接

button1.clicked.connect(change_label_text)

临时取消(阻止)信号的连接,查看信号连接状态:

# 临时阻止信号的连接
button1.blockSignals(True)  # True 阻止
# 查看信号连接状态
print(button1.signalsBlocked())
# ------运行结果------
# True
# ------运行结果------
# 查看连接信号的接收器数量
print(button1.receivers(QPushButton.clicked))
# ------运行结果------
# 1
# ------运行结果------

button1.blockSignals(False)  # False 不阻止

注:信号被临时取消时,可以理解为信号与槽函数之间的连接被临时阻塞。
其信号与槽函数还是处于连接状态中,像上一段代码,即使临时取消了,查看连接信号的接收器数量还是为1。

3.2.2 实例二:给窗口标题增加前缀

要求:
在设置窗口标题时, 自动添加前缀"ZZ-",支持多次修改。

代码:

# 创建窗口
window2 = QWidget()
# 自定义槽函数
def change_title(title):
    # 阻止信号
    window2.blockSignals(True)
    window2.setWindowTitle('ZZ-'+ title)
    # 不阻止
    window2.blockSignals(False)
    
# 窗口标题改变信号连接槽函数
window2.windowTitleChanged.connect(change_title)
# 设置窗口标题
window2.setWindowTitle('窗口一')
window2.setWindowTitle('窗口二')

window2.show()
sys.exit(app.exec_())

代码解析:
注意change_title的逻辑,先阻止信号再取消阻止。因为:如果没有这两步,将会进入死循环
即:改变窗口标题信号连接设置窗口名称,窗口名称重新设置了后又引起信号的传递…

所以正确的思路是: 在设置窗口名称,引发窗口标题信号改变信号,调用change_title方法后。先阻止信号,再将设置的窗口名称加上前缀,这个时候窗口名称虽然改变了,windowTitleChanged信号也被激发了,但是此时信号是被阻止的。
窗口名称改变后,再将信号取消阻止,让下次设置窗口名称时可以进入change_title函数。
大家可以再理一理思路。

4、QObject 类型判定API及实例

4.1 API 一览:

项目 说明
isWidgetType 是否是控件类型
inherits 一个对象是否继承(直接或者间接)自某个类

4.2 应用场景及实例

4.2.1 应用场景

过滤筛选控件

4.2.2 实例1:类型判断

创建控件:

import sys
from PyQt5.Qt import *

app = QApplication(sys.argv)
# 创建QObject对象
obj = QObject()
# 创建QWidget对象
window = QWidget()
# 创建label对象
label0 = QLabel()

判断是否为控件类型:

# 判断是否为控件
print(obj.isWidgetType())
print(window.isWidgetType())
print(label0.isWidgetType())
# ------运行结果------
# False
# True
# True
# ------运行结果------

解析: 继承自QWidget类的对象才是控件,才会获得True返回值。

判断一个对象是否继承(直接或者间接)自某个类:

# 判断一个对象是否继承(直接或者间接)自某个类
print(window.inherits('QObject'))
print(label0.inherits('QObject'))
print(label0.inherits('QWidget'))
# ------运行结果------
# True
# True
# True
# ------运行结果------

注意其与父子关系(父子对象关系)的区别,这里讨论的是类的继承关系,而不是窗口之间的父窗口,子控件。

4.2.3 实例二:对父子对象关系的实例二进行改进

要求:创建一个窗口, 包含多个子控件QWidget和QLabel,让所有的QLabel类型子控件都设置背景颜色为青色(cyan)。

新增要求: 所有的 QPushButton 类型的子控件背景颜色为蓝色。

window = QWidget()
window.resize(300,300)
# 创建标签控件
label1 = QLabel()
# 设置父控件
label1.setParent(window)
label1.setText('标签1')
# 创建标签控件,同时设置父控件
label2 = QLabel(window)
label2.setText('标签2')
label2.move(0,50)
# 创建按钮控件
btn = QPushButton(window)
btn.setText("按钮")
btn.move(0, 100)

# 用父控件查找子控件的方式来设置样式
# 先查找到window控件所有未QLabel类型的子控件,再将其背景颜色设置为青色
for widget in window.findChildren(QLabel):
    widget.setStyleSheet("background-color: cyan;")
    
# ----------------新增代码------------------
# 另外一种方法,让控件去判断是否为 QPushButton 类型
for widget2 in window.children():
    if widget2.inherits('QPushButton'):
        widget2.setStyleSheet("background-color: blue;")

# 显示窗口
window.show()
# 执行应用程序,进入消息循环
sys.exit(app.exec_())

5、QObject 事件处理API

5.1 API 一览:

QObject.childEvent()
QObject.customEvent()
QObject.eventFilter()
QObject.installEventFilter()
QObject.removeEventFilter()
QObject.event()

作用: 监听、过滤一些底层事件,后续细讲,此处暂作了解。

6、QObject 对象删除、定时器API及实例

6.1 API 一览:

项目 说明
obj.deleteLater() 删除一个对象时, 也会解除它与父对象之间的关系
startTimer 开启一个定时器
killTimer 根据定时器ID,杀死定时器
timerEvent 定时器执行事件

6.2 应用场景及实例

6.2.1 应用场景

对象删除: 想要移除某一个对象的时候使用
定时器: 轮询、倒计时等。

6.2.2 实例一:对象删除
import sys
from PyQt5.Qt import *

app = QApplication(sys.argv)

# 创建QObject对象
obj = QObject()
# 对象销毁信号连接槽函数
obj.destroyed.connect(lambda : print('obj被销毁了'))

# 创建 QWidget 对象
window = QWidget()
obj.setParent(window)
# 删除对象
obj.deleteLater()
# 结果:obj被销毁了
6.2.3 实例二:倒计时、定时器案例

要求:
1、创建一个窗口, 并设置两个Label标签;
2、label1控件展示10s倒计时,倒计时结束, 就停止计时。
3、label2控件背景设置为青色,原始大小为100,100。每过100ms长宽各增加2像素。

要执行timeEvent事件,即要重写对象,如第一个倒计时操作:

class MyLabel1(QLabel):
    # 自定义MyLabel1类,继承自QLabel类
    # 定时器启动方法
    def MyStart(self,ms):
        # 开启一个定时器方法,返回值为一个定时器ID
        self.timer_id = self.startTimer(ms)

    # 重写timeEvent方法
    def timerEvent(self, *args, **kwargs):
        current_sec = int(self.text())
        current_sec -= 1
        self.setText(str(current_sec))
        if current_sec == 0:
            # 根据定时器ID,停止定时器
            self.killTimer(self.timer_id)

创建其他控件:

# 创建 QWidget 对象
window = QWidget()
# 设置窗口大小
window.resize(300,300)
# 创建label1,自定义标签类
label1 = MyLabel1(window)
# 设置定时器初始值为10秒
label1.setText('10')
# 自定义的开启定时器方法
label1.MyStart(1000)

对于label2 我们也重写一个MyLabel2类继承自QLabel类,重写timerEvent方法。

class MyLabel2(QLabel):
    def timerEvent(self, *args, **kwargs):
    	# 获取标签初始的宽和高
        current_w = self.width()
        current_h = self.height()
        self.resize(current_w + 2, current_h + 2)

创建label2标签,并启动计时器:

label2 = MyLabel2(window)
label2.setStyleSheet("background-color: cyan;")
label2.resize(100,100)
label2.move(50,50)
label2.startTimer(100)

最后效果图:

倒计时实例效果
小结:

1、deleteLater 用来删除某一对象
2、定时器的一般使用方法:
(1)重写 timerEvent 方法,在方法内部写好需完成的逻辑
(2)开启定时器 startTimer
(3)关闭定时器 killTimer

最后:

以上即为PyQt5 QObject类所有的API及相应的实例演示,除了事件处理的API没讲之外,真的讲的比较详细。相信有了这一份笔记,对QObject类的所有接口和大致的应用场景都会更有把握吧。
整理不易,希望大家点个赞,收藏一个再走吧。

想要笔记原文的,可留意或私信。
原文如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zhouz92/article/details/106597130