【python】第六章:python-GUI编程-PyQt5
文承https://blog.csdn.net/friday1203/article/details/153183219?spm=1001.2014.3001.5502
八、控件学习
前面认识各种类型控件后,我们就可以调包实现了,但是现实总是五花八门的,有人喜欢红色边框有人喜欢蓝色边框,有人喜欢宋体有人喜欢楷书....所以我们还得会个性化设置每个控件的特定属性和功能。而个性化设置控件,就要用到面向对象编程,具体说就是要用到类的继承、多态、重载等底层逻辑。
这部分内容我是默认你非常理解什么是类的继承、多态、重载、重写这些底层概念的,如果这些概念你不理解,后面你就非常难懂我在干什么。这部分内容你可以参考我的C++语法系列中的博文:https://blog.csdn.net/friday1203/article/details/139986081
优秀的编程语言的底层逻辑都是相似的,可谓优秀的作品如出一辙,糟糕的东西千奇百怪,所以python语法中也有相同的概念,可以通过我这篇博文去查找相关知识点:https://blog.csdn.net/friday1203/article/details/138354754
1、PyQt5库种的类的层级继承关系
PyQt5库是一层层抽取共性,被抽取的共性打包成一个子类,这个子类继承它前面的大类:

有没有发现这个思想很优美啊?!看似五花八门、凌乱不堪、毫无规律的事情,就水灵灵的让PyQt5给分门别类的梳理清楚了!这也给我们以后处理自己的事情提供了一个极好的借鉴思路。
基于上面的思想,我们可以看到PyQt5库的类是如下结构的:

其实就是一个树状结构的分类,所有的分支都继承它前面的分支,直到叶子节点。我们还可以用代码获取PyQt5库的类:

可见,QObject是PyQt5库中最高的父类,是实现比如网络、多媒体、多线程、数据库、桌面UI等PyQt5库中的所有对象的基类。
但是我们这里只学桌面UI方面的功能,而桌面UI的最高父类是QWidget,或者说QWidget是所有可视化控件的基类,所以我们可以通过QWidget.subclasses()获取它下面的所有控件类别。比如第一个类别是QAbstractButton,就是按钮类,而且在这个类下面还有4种不同的按钮子类。
所以说你想实现什么功能,你要层层寻找你需要的类,然后你自己再写一个类,让这个类继承PyQt5库中的类,然后在这个类中写你自己的个性化设置。
2、从QObject类来看,控件的属性和方法是如何被继承的
从大的链条上看:

从代码角度看:

(1)用代码测试一下上图的A处:

(2)了解qss,展示如何设置样式,也就是展示B处的操作过程
我们先展示一个简单的样式设置:

如果你的样式不复杂,可以这样简单处理。如果你的样式非常复杂,那你就得使用qss样式表。
qss样式表和控件之间的关系,就类似于css样式表和html元素之间的关系,学过前端的同学就应该秒懂了。就是将样式表和具体控件做一个分离,把qss字符串放到一个单独的文件,代码运行时,会从这个文件中去读取样式,然后渲染显示。具体操作示例如下:

如果你想单独渲染某类控件中的某个控件怎么办?此时就要用到obj对象的名称和属性:

3、父子对象的操作
到这里很多人会有点懵,我们写类的时候不是有继承父类的写法嘛?!不是就有父类和子类了嘛?!不是就是父对象和子对象嘛?!是滴,确实是这样的。子类继承了父类的属性和方法,尤其是继承了父类的构造函数和析构函数。但是仅仅是这样还不够!对于桌面控件来说,除了同一类别的控件可以通过继承来实现更多的属性和方法方面的细节,在现实需求中,我们有时还需要相同类别控件之间的嵌套,或者不同层级控件之间的嵌套,比如下面这个例子:

可见,在这种业务场景中,我们用写类时继承父类的做法肯定是无法办到的,此时就得要用setParent()来手动设置父子对象。
在业务角度上,我需要把窗口2放到窗口1里面,我关闭窗口1的时候窗口2也随之消失。此时我就要设置父子对象,设置窗口1是父对象,窗口2是子对象,这样就能实现业务需求了。而这个过程的底层其实就是如何依次执行各个控件的析构函数,防止内存泄露,这部分内容我在C++系列中有详细解释:https://blog.csdn.net/friday1203/article/details/139986081
4、信号与槽机制

(1)首先你要理解什么是"信号"?
一是,在Qt中,只要是对象,那这个对象就有信号,不同对象会有不同的信号。比如QObject对象的底层代码就给它设置了2种信号,就是上图的A处,一种是QObject对象的名称发生变化时,它就自动发出一个信号;另外一种是QObject对象被销毁时,它也会发出一个信号。
二是,所有继承自QWidget的控件都支持“信号与槽”机制。也就是所有可视控件也都是可以有信号的。就是说有的控件本身就内置有一些信号,比如按钮控件,就内置有点击信号。而有的控件即使没有内置信号,也是支持我们手动添加信号的。
(2)其次是你要理解什么是“槽”?
槽的通俗理解就是一个函数或者一个方法,所以槽也叫槽函数。顾名思义,槽函数就是用来执行某个功能的。
一些控件也是内置有槽函数,而一些没有内置槽函数的控件,也是支持我们自定义槽函数的。
(3)信号和槽是要链接的!
就是当一个控件发出了信号,那一个控件是允许发出多种信号的,那这个控件发出的这个特定信号它是想执行什么呢?也就是它想引发哪个槽函数呢?所以每种信号都是有.connect()方法的,这个方法就是用来链接信号与槽函数的。这样就实现了某个特定信号就触发某个特定槽函数,或者说就是某个特定信号就执行某个特定功能的效果。
下面用代码展示一些小案例我们体会一下:



5、类型判定
这里就在上图A、B处展示两个案例即可:


6、对象删除
对象删除其实底层就是调用析构函数,而何时调用析构函数就会涉及到内存泄露这个概念,所以C++底层语法已经把这个问题考虑得非常严谨了。但是PyQt框架有自己的更多要求,所以它又开发了一个deleteLater这么一个接口,来延缓对象的删除:

当我们要删除一个对象时,PyQt它是不会第一时间就去删除,而是主消息循环在下个循环中才删除。下面看个例子对比理解一下这个api:

7、事件处理-事件机制
事件处理指的就是Qt对象之间的信息通讯。所以事件机制是对象通讯的逻辑。
(1)事件处理的具体逻辑链条
一是,app.exec_()死循环地检测各种消息
二是,一旦app.exec_()检测到消息,开始执行消息相应逻辑。下面图的例子是以按钮控件为例展示的整个逻辑链条:

上面的0-7步骤是按钮控件的点击事件的逻辑链,由于这个事件太常见,所以Qt已经给我们打包好了,我们直接使用信号-槽机制就可以实现。可见信号-槽机制其实就是对事件机制的更高级的二次包装而已。所以如果我们有特殊需求,也就是Qt内置的信号-槽机制满足不了我们的需求时,我们就得更改上面的信息传递链条。
(2)如何更改事件处理?
一是在上图4处的QApplication.notify()方法中拦截消息传递。我们先看看QApplication做了什么:

因为QApplication是个类对象嘛,我们只要重写它的notify()方法,即可更改:

上图B处你就可以自由发挥。
二是,你可以在流程图6处的空间对象上拦截消息事件:

说明,上图只是举了两个最常见的方法,按钮控件的方法还有非常非常多,如右下角所示,所以你可以针对你的需求自由发挥。
8、定时器
定时器顾名思义就是:定一个时间或者时间间隔,自动的去做某件事情。

下面我用代码展示几个小例子即可:



其实上面三个小示例都是相同的思路,换汤不换药,用到的都是类的继承、重写的底层逻辑。
from PyQt5.Qt import *
import sys
class My_label(QLabel): #2、开始自己写label类
def __init__(self,*args, **kwargs): #3、初始化类的时候用*args,**kwargs就不用考虑实例化时具体的参数类型了
super().__init__(*args, **kwargs) #4、Qt内置的label类的初始化东西我原封不动搬来
self.setText("10") #5、然后我个性化设置我自己的初始化
self.setStyleSheet("font-size:50px;")
self.move(70, 70)
#self.timer_id = self.startTimer(1000) #6、目的就是开启label对象的计时器功能
def set_starter(self, starter): #从什么时间开始倒计时
self.setText(str(starter))
def set_frequence(self, frequence):
self.timer_id = self.startTimer(frequence)
def timerEvent(self, *args, **kwargs): #7、重写label的计时器事件方法
print("时间过了1秒")
current_sec = int(self.text()) #获取当前label中的数字
current_sec -= 1
self.setText(str(current_sec))
if current_sec == 0:
print("停止!")
self.killTimer(self.timer_id) #销毁计时器
app = QApplication(sys.argv)
window = QWidget()
window.resize(200, 200)
label = My_label(window) #1、我要自己写一个label类,让它可以倒计时 这里的window是实例化类时的参数
label.set_starter(5)
label.set_frequence(500)
window.show()
sys.exit(app.exec())
from PyQt5.Qt import *
import sys
class My_window(QWidget):
def __init__(self, time_jiange, *args, **kwargs):
super().__init__(*args, **kwargs)
self.resize(200, 200)
self.timer_id = self.startTimer(time_jiange)
def timerEvent(self, *args, **kwargs):
print("我变大...我再变大...")
current_w = self.width()
current_h = self.height()
self.resize(current_w+10, current_h+10)
if current_w==500:
print("已经够大了,不能再大了....")
self.killTimer(self.timer_id)
app = QApplication(sys.argv)
window = My_window(time_jiange=500)
window.show()
sys.exit(app.exec())
更多推荐


所有评论(0)