1. QObject模块详解
1.1 描述
所有Qt对象的父类
1.2 功能和作用
1.2.1 对象名称和属性
1.2.1.1 API
API | 功能 | 备注 | ||||||
---|---|---|---|---|---|---|---|---|
|
|
| ||||||
|
|
| ||||||
|
|
| ||||||
|
|
| ||||||
|
|
|
1.2.1.2 应用场景
- 用于qss的ID选择器,属性选择器,方便统一设置样式
- 用于装饰器的信号与槽
1.2.1.3 使用案例1
from PyQt5.Qt import *
import sys
class Window(Qwidget):
def __init__(self):
"""初始化方法"""
super().__init__()
# 设置窗口大小
self.resize(500, 500)
# 设置窗口标题栏名称
self.setWindowTitle("Object")
self.initUI()
def initUI(self):
"""调用方法"""
# self.QObject_text()
self.QObject_caozuo()
def QObject_text(self):
"""QObject模块学习"""
# 查看QObject继承父类
mros = QObject.mro()
for mro in mros:
print(mro)
def QObject_caozuo(self):
"""测试API方法"""
obj = QObject()
# 给QT对象设置一个名称
obj.setObjectName('notice')
print(obj.objectName())
# 给QT对象动态添加一个属性与值
obj.setProperty('notice_level', 'error')
obj.setProperty('notice_level1', 'Info')
print(obj.property('notice_level1'))
# 获取一个对象中所有通过setProperty()设置的属性名称
print(obj.dynamicPropertyNames())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.1.4 使用案例2
要求: 创建多个用于信息提示的QLabel
- 凡是提示的QLabel控件,都要求设置①字体大小为25px;②字体颜色为灰色;③边框圆角为8px;
- 信息提示分为多个级别①正常(normal):绿色字体及边框;②警告(warning):黄色字体及边框;③错误(error):红色字体及边框;
QObject.qss文件
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: yellow;
border-color: yellow;
}
QLabel#notice[notice_level="error"] {
color: red;
border-color: red;
}
qss_test.py文件
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""Qt初始化"""
super().__init__()
self.setWindowTitle('QSS案例')
self.resize(500, 500)
self.initUI()
def initUI(self):
"""Qt API方法调用函数"""
# 调用QSS测试方法
self.qss_style()
# 调用加载QSS样式表方法
self.QSS_test()
def QSS_test(self):
"""QSS初体验"""
# 设置label1样式
label1 = QLabel(self)
label1.setText('normal')
label1.setObjectName('notice')
label1.setProperty('notice_level', 'normal')
# 设置label2样式
label2 = QLabel(self)
label2.setText('warring')
label2.setObjectName('notice')
label2.setProperty('notice_level', 'warring')
label2.move(100, 0)
# 设置label3样式
label3 = QLabel(self)
label3.setText('error')
label3.setObjectName('notice')
label3.setProperty('notice_level', 'error')
label3.move(200, 0)
def qss_style(self):
"""加载qss样式函数"""
with open('./QObject.qss', 'r') as f:
qApp.setStyleSheet(f.read())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.2 父子对象操作
1.2.2.1 API
API | 功能 | 备注 | ||||||
---|---|---|---|---|---|---|---|---|
|
|
| ||||||
|
|
| ||||||
|
|
| ||||||
|
|
| ||||||
|
|
|
1.2.2.2 应用场景
1.2.2.2.1 Qt对象内存管理机制
1.2.2.2.1.1 QObject继承树
- 所有对象都是直接或间接的继承QObject
- QObjects在一个对象树中组织他们自己
- 当创建一个QObject时,如果使用了其他对象作为其父对象,那么他就会被添加到父对象的children()列表中
- 当父对象被销毁时,这个QObject也会被销毁
1.2.2.2.1.2 QWidget
- 扩展了父子关系
- 当一个控件设置了父控件
- 会包含在父控件内部
- 收父控件区域裁剪
- 父控件被删除时,子控件会自动删除
1.2.2.2.2 多个顶层窗口互相独立
如果一个控件,没有任何父控件,那么就会被当成顶层控件
1.2.2.2.3 如果一个控件被包含在另外一个控件内部,就需要设置父子关系
- 显示位置收父控件约束
- 生命周期也被父对象接管
1.2.2.3 示例
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""初始化方法"""
super().__init__()
self.resize(500, 500)
self.setWindowTitle('对象的父子关系操作')
self.initUI()
def initUI(self):
"""调用函数"""
self.parent_children()
def parent_children(self):
"""QObject父子关系测试方法"""
obj1 = QObject()
obj2 = QObject()
obj3 = QObject()
print('obj1:', obj1)
print('obj2:', obj2)
print('obj3:', obj3)
print("+++++"*5)
# 设置obj1, 3父对象为obj2
obj1.setParent(obj2)
obj3.setParent(obj2)
# 检测obj1的父对象
print(obj1.parent())
print("+++++"*5)
# 打印obj2所有子对象
print(obj2.children())
print("+++++"*5)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.3 信号与槽 (重要)
1.2.3.1 基本概念
- Signal和Slot是Qt中的核心机制,主要作用于对象之间进行通信
- 信号: 当一个控件的状态发生改变时,向外界发出信息
- 槽: 一个执行某些操作的函数/方法
- 所有继承自QWidget的控件都支持“信号与槽”机制
1.2.3.2 机制描述
- 手动操作:信号 connect 槽
- 自动操作:当信号发出时,连接的槽函数会自动执行
1.2.3.3 基本使用
- 信号:控件内置的一些,也可以自定义
- QPushButton().presssed
- QPushButton().clicked
- 槽:不同控件内置槽函数,自定义函数和方法
- 连接方式:object.信号.connect(槽函数)
- 特性:
- 一个信号可以连接多个槽函数
- 一个信号也可以连接另外一个信号
- 信号的参数可以是python任何类型
- 一个槽可以监听多个信号
1.2.3.4 其他操作
- 自定义信号:带参数
- 信号的操作:连接、断开、发射
- 自定义槽函数lambda表达式
- 装饰器信号
1.2.4 QObject信号处理
1.2.4.1 API
API | 功能 | 备注 | ||||||
---|---|---|---|---|---|---|---|---|
|
|
| ||||||
|
|
| ||||||
|
|
| ||||||
|
|
| ||||||
|
|
|
1.2.4.2 应用场景
- 监听信号,影响用户行为
- 信号与槽机制(参见1.2.3)
1.2.4.3 案例
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""初始化方法"""
super().__init__()
self.setWindowTitle('信号与槽函数')
self.resize(500, 500)
self.initUI()
def initUI(self):
"""调用函数"""
self.def_cao()
def def_cao(self):
"""槽函数测试方法"""
self.obj = QObject()
def destory_cao(obj):
print('对象被释放', obj)
def objectName_cao(name):
print('对象名称发生改变', name)
# 对象被释放
self.obj.destroyed.connect(destory_cao)
# 对象名称发生改变
self.obj.objectNameChanged.connect(objectName_cao)
# 修改对象名称
self.obj.setObjectName('xxx')
# 断开槽与信号的连接
# self.obj.objectNameChanged.disconnect()
# 临时取消槽连接
self.obj.blockSignals(True)
# 槽已经断开,触发后无效果
self.obj.setObjectName('ooo')
# 恢复槽连接
self.obj.blockSignals(False)
self.obj.setObjectName('xxoo')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.4.4 案例2
当用户点击按钮的时候, 打印"点我嘎哈?"
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""初始化方法"""
super().__init__()
self.resize(500, 500)
self.setWindowTitle('信号处理')
self.initUI()
def initUI(self):
"""调用函数"""
self.single_and_slot()
def single_and_slot(self):
"""信号与槽测试方法"""
btn = QPushButton(self)
btn.setText('点我!')
btn.clicked.connect(self.cao)
def cao(self):
print('点我干啥!!!')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.4.5 案例3
在所有修改的窗口标题前, 添加前缀"xujie-"
要求: 创建多个用于信息提示的QLabel
- 比如, 后续我们修改标题为"Hello World!!“; 最终会自动变为"xujie-Hello World!!”
- 支持多次修改
from PyQt5.Qt import *
import sys
if __name__ == '__main__':
app = QApplication(sys.argv)
window = QWidget()
# 连接窗口标题变化的信号与槽函数
def cao(tittle):
print('PASS!!', tittle)
# 断开槽连接
window.windowTitleChanged.disconnect()
# 或者使用临时终止window.blockSignals(True)
window.setWindowTitle('xujie-' + tittle)
window.windowTitleChanged.connect(cao)
# 或者使用解除临时终止window.blockSignals(False)
window.windowTitleChanged.connect(cao)
window.setWindowTitle('Hello world!')
window.setWindowTitle('Hello world2!')
window.show()
sys.exit(app.exec_())
运行结果:
1.2.5 类型判定
1.2.5.1 API
API | 功能 | 备注 | ||||||
---|---|---|---|---|---|---|---|---|
|
|
| ||||||
|
|
|
1.2.5.2 应用场景
过滤筛选条件
1.2.5.3 案例1
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""初始化方法"""
super().__init__()
self.setWindowTitle('类型判定')
self.resize(500, 500)
self.initUI()
def initUI(self):
"""调用函数"""
self.type_decide()
def type_decide(self):
"""类型判断测试方法"""
obj = QObject()
wig = QWidget()
btn = QPushButton()
label = QLabel()
a = [obj, wig, btn, label]
for aa in a:
if aa.isWidgetType():
print(aa, '是控件')
else:
print(aa, '不是控件')
if aa.inherits('QWidget'):
print(aa, '继承自QWidget')
else:
print(aa, '不是继承自QWidget')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.5.4 案例2
创建一个窗口,包含多个QLabel或其他控件
要求: 将包含在窗口内所有的QLabel控件,设置背景色cyan
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""初始化方法"""
super().__init__()
self.setWindowTitle('类型判定')
self.resize(500, 500)
self.initUI()
def initUI(self):
"""调用函数"""
self.type_decide()
def type_decide(self):
"""类型判定方法"""
label1 = QLabel(self)
label1.setText('label a')
label2 = QLabel(self)
label2.setText('label b')
label2.move(100, 0)
btn = QPushButton('btn a', self)
btn.move(200, 0)
a = [label1, label2, btn]
for widget in a:
if widget.inherits('QLabel'):
widget.setStyleSheet('background-color: cyan;')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.6 对象删除
1.2.6.1 API
API | 功能 | 备注 | ||||||
---|---|---|---|---|---|---|---|---|
|
|
|
1.2.6.2 应用场景
想要移除某一个对象时使用
1.2.6.3 案例
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
"""初始化方法"""
super().__init__()
self.resize(500, 500)
self.setWindowTitle('对象删除')
self.initUI()
def initUI(self):
"""调用方法"""
self.delete_object()
def delete_object(self):
"""对象删除方法"""
obj1 = QObject()
self.obj1 = obj1
obj2 = QObject()
obj3 = QObject()
obj3.setParent(obj2)
obj2.setParent(obj1)
# 观察obj是否被释放
obj1.destroyed.connect(lambda x:print('obj1被释放'))
obj2.destroyed.connect(lambda x:print('obj2被释放'))
obj3.destroyed.connect(lambda x:print('obj3被释放'))
# 观察obj1是否有子控件
print(obj1.children())
# del方法不可使用
# del obj2
# deleteLater方法
obj2.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
运行结果:
1.2.7 事件机制
1.2.7.1 相当于信号与槽机制
- 信号与槽机制是对事件机制的高级封装
- 事件机制更偏底层(远离用户)
1.2.7.2 图解
1.2.7.3 应用
- 一般情况下,我们直接通过内置的信号与槽就可以解决一般通讯问题
- 例:QPushButton的clicked信号
- 有些控件并没有提供我们想要的信号,我们就需要自己重写具体的事件函数,来捕获产生的事件,做相应的处理
- 例:Qlabel并没有clicked信号
- 某些场景并不会吧我们想要捕获的事件传递给特定函数,而是做了其他的额外处理,此时我们开头重写事件的分发函数
- 列:想捕获用户的tab点击
- 当用户点击了tab键,默认是切换焦点
- 并不会把这个事件分发给keyPressEvent函数
- 此时,需要我们重写event,来做分发处理
- 列:想捕获用户的tab点击
- 如果想要同时多多个不同的控件进行捕获tab点击,那么每个都重写event()函数,也是非常麻烦,那么可以考虑事件过滤器
- QApplication对象的事件过滤器,可以拦截所有的QObject事件,一般不怎么使用
- QApplication对象的notify()更不怎么用,会大大降低程序性能
1.2.7.4 事件的传递
- 如果一个控件没有处理该事件,则会自动传递给父控件进行处理
- 事件对象具备两个特殊的方法
- accept():自己处理了这个事件,并告诉系统不要向上层传递
- ignore():自己忽略了该事件,告诉系统,继续往后传递下去
1.2.7.5 QObject事件处理
1.2.7.5.1 API(后续介绍)
childEvent()、customEvent()、eventFilter()、installEventFilter()、removeEventFilter()、event()
1.2.7.5.2 应用场景
- 事件机制(参见1.2.7)
- 拦截事件,监听特定行为
1.2.7.6 案例
from PyQt5.Qt import *
import sys
class App(QApplication):
def notify(self, a0, a1):
if a0.inherits("QPushButton") and a1.type() == QEvent.MouseButtonPress:
print(a0, a1)
return super().notify(a0, a1)
class Btn(QPushButton):
def event(self, e):
if e.type() == QEvent.MouseButtonPress:
print(e)
return super().event(e)
def mousePressEvent(self, *args, **kwargs):
print('按钮被按下了。。。。。。')
return super().mousePressEvent(*args, **kwargs)
if __name__ == '__main__':
app = App(sys.argv)
windows = QWidget()
windows.resize(500, 500)
btn = Btn(windows)
btn.setText('点击按钮')
btn.move(200, 220)
def cao():
print('不要点我!!')
btn.pressed.connect(cao)
windows.show()
sys.exit(app.exec_())
运行结果:
1.2.8 定时器
1.2.8.1 API
API | 功能 | 备注 | ||||||
---|---|---|---|---|---|---|---|---|
|
| Qt.TimerType:①Qt.PreciseTimer:精确定时器,尽可能保持毫秒准确;②Qt.CoarseTimer:粗定时器,5%的误差间隔;③Qt.VeryCoarseTimer:很粗定时器,只能到秒级; timer_id:定时器唯一标识 | ||||||
|
|
| ||||||
|
|
|
1.2.8.2 应用场景
- 轮询
- 倒计时
1.2.8.3 案例1
创建一个窗口,并设计一个子控件label
要求:
- 展示10秒倒计时
- 倒计时结束,就停止计时
from PyQt5.Qt import *
import sys
class Mylabel(QLabel):
def __init__(self, *args, **kwargs):
"""初始化方法"""
super().__init__(*args, **kwargs)
self.setText('10')
self.move(200, 220)
self.setStyleSheet('font-size: 22px;')
self.timer_id = self.startTimer(1000)
def setSec(self, sec):
"""设置倒计时时间方法"""
self.setText(str(sec))
def startMyTimer(self, ms):
"""设置时间间隔方法"""
self.timer_id = self.startTimer(ms)
def timerEvent(self, a0):
"""重写定时器事件"""
# 获取当前标签内容
current_sec = int(self.text())
current_sec -= 1
self.setText(str(current_sec))
if current_sec == 0:
self.killTimer(self.timer_id)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle('定时器')
window.resize(500, 500)
label = Mylabel(window)
label.setSec(50)
label.startMyTimer(1000)
window.show()
sys.exit(app.exec_())
1.2.8.3 案例2
创建一个窗口,通过定时器不断增加该窗口的尺寸大小
要求:
- 每100ms宽高均增加1px
from PyQt5.Qt import *
import sys
class MyWidget(QWidget):
def timerEvent(self, *args, **kwargs):
current_w = self.width()
current_h = self.height()
self.resize(current_w + 10, current_h + 10)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWidget()
window.resize(200, 200)
window.setWindowTitle('定时器')
window.startTimer(1000)
window.show()
sys.exit(app.exec_())
1.2.9 语言翻译
1.2.9.1 API
- tr()
1.2.9.2 应用场景
- 多语言国际化支持