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 应用场景
- 多语言国际化支持



















