PyQT5

本教程参考自书籍《PyQT5快速开发与实战》

pyqt5学习视频:Python-GUI编程-PyQt5 (少)_哔哩哔哩_bilibili

pyqt5中文文档:【第一节】PyQt5简介-PyQt5中文教程 (py40.com)

学习进度

2022/6/27

现在看的是b站一个up主的视频讲解,整体感觉还可以,没有废话讲得非常有意思,但是肯定只能算是入门,真正需要反复练习才能熟悉使用;

本质上,easyX是一个画图工具,不适合新手使用,而且不容易把图形界面和功能代码分开,最重要的一点是几乎没有人用easyX,网上的教程都是那种非常水的教程;

pyside2对新手非常友好的一点就是可以直接使用工程师手动搭建ui界面然后动态加载进入目标文件中,可能需要着重理解的点就是python的类相关以及常见控件的使用方法,QT是pyside2和pyQT的父类,所以学会了pyside2也就基本完全掌握了QT;

2022/6/27

现在晚上9:43,已经差不多把白月黑羽up主的视频和网站笔记看完了,然后因为实际上up主使用的时pyside2,但是相比于pyqt5资料实在太少了(up主主要讲解的是使用设计师开发,可能还需要找一个基于代码开发的视频理解底层原理,不然绑定事件处理函数的时候真的很懵),而且项目实战的视频也很少(大多数的实战视频要收费,免费的也不全),所以考虑还是需要稍微学一下pyqt5然后找合适的项目来练手(如果不练手的话相当于白学了);

还有就是在跟着做一遍项目之后自己把爬虫那个代码包装一下尝试看能否变成一个简单好用的小工具;

2022/6/30

现在基本上把PyQt5的入门摸清楚了,然后主要就是信号和槽机制不是特别熟悉,个人觉得可能还是需要多看书吧,然后结合给的代码进行练习,直接干看文档也没什么用;

结合代码联系可以让我们在实际开发过程中体验并发现问题,说实话文档没什么看头,所以下一步的话就是再看书,把进阶的东西好好理解一下,最后看文档补充自己还没有学习到的知识点;

前言

2022/6/30 15:35 因为现在网上没有比较统一专业的教导使用PyQt5的教程或者是书籍,所以初学的时候花费了大量时间踩了很多坑,下面是一些根据《PyQt5快速开发与实战》一书进行学习的时候发现的一些问题和心得。现在基本上是简单的将书上的内容都过了一遍,的确受益匪浅,但是有一些存在的问题还是需要注意;

先说一下安装和配置环境(也就是书上的python3+PyQt5+Eric6),这个玩意花了我一个上午的时间没弄好,原因有如下:

  • 首先我创建了名为pyqt_5_2的虚拟环境,接着在该虚拟环境中安装了Eric必须的一些包以及pyqt5的包和pyqt5-tools等,这一步没什么问题都是正常的;
  • 接着需要手动去官网下载Eric6的压缩包和汉化包,这里有两个问题,一个是Eric6在17版本后就没有汉化包了(这个在CSDN上有好心人分享),还有就是官网的下载速度非常慢,并且官网已经不再提供17版本的安装包(最低都是19),好在我在网上白嫖了一个安装包,但是接下来问题就更多了;
  • 按照书上的提示,我们需要将汉化包和软件包放在一起,然后执行install.py文件就可以安装成功并且汉化成功,但是我们实际上完成之后的文件夹树结构和书上几乎不一样,并且打开安装好的软件之后就会出现莫名其妙的报错,大概就是说我没有许可之类的,按照书上的配置进行了设置之后也没有任何作用,按照网上的教程(稀碎)反复安装卸载了很多次也没有成功,所以最后选择直接使用作者提供的方法
  • 下载地址见网盘链接:http://pan.baidu.com/s/1i4Pp5VB 密码:u83w

因为环境的问题,所以资源存放的位置可能会有一点杂乱(但是经过一系列测试应该是没有问题的,可以正常运行)

  • pyqt5依赖的虚拟环境(我们使用pip下载东西的时候预先要激活这个环境,否则下载到base环境中):D:\Download_software\My_miniconda\envs\pyqt_5_2 —— 关于这个虚拟环境有必要说一下,因为实际上我们使用的Eric是作者打包好的,但是在配置的时候我们选择的依赖环境是自己创建的环境,所以在进行某些配置比如设计师的汉化的时候可能需要注意区分是虚拟环境中的设计师还是作者给的文件中的设计师(这一点已经证明直接使用designer打开的设计师是虚拟环境中的设计师,记住一点,我们可以在虚拟环境随便动手但是最好别乱动作者给的文件夹
  • 书籍配套资源代码:D:\My_document\自学指南\PyQt5资源下载——Chapter*目录对应的书上11各章节的案例脚本;tool目录存放的是一些工具如离线帮助手册、UML类图等
  • Eric6软件:D:\WinPyQt5.9-32bit-3.5.3.1\python-3.5.3\Scripts
  • 项目根目录:D:\My_code\Pycharm\pyqt_5

接着是创建项目的问题(不要嫌前期准备麻烦,工欲善其事必先利其器,一定要把各方面都做完善接下来的学习过程才不会半途而废)

  • 我们创建项目的时候一定要在项目根目录下自己再创建一个子文件夹,它不会像其他IDE一样自动创建一个和项目文件名相同的项目文件夹;
  • 我们创建完项目之后再新建文件,注意新建的文件是没有后缀名之类的,第一件事就是先“另存为”在我们的项目文件夹中,保存格式默认为.py文件

关于Eric6编辑器,这个编辑器最大的毛病(这也是为什么最后我放弃它的原因)就是使用Tab缩进会出现问题,经常莫名其妙的报错说空格键和Tab键不兼容(即我们复制的py文件的格式可能是Tab缩进的,但是Eric本身只支持使用空格缩进),解决方法是将整个文章取消缩进再缩进,然后手动调整(实际上这样很麻烦);另一种解决方法就是用pycharm进行编辑(实际上Eric6除了可以直接启动设计师以外,也没有介绍说的那么牛什么专用于QT开发,真要用这玩意估计开发效率降低一倍);Eric6现在唯一给我比较好的体验让我觉得它比Pycharm好一点的地方就在于它拥有直接编译窗体的功能而Pycharm甚至连ui文件都打不开(但是咱们也基本上不会看ui文件)

综上所述,最终我还是选择了回归Pycharm+miniconda+环境内置Designer的开发方式,我们只需要简单的在miniconda中配置好PyQt5所需要的环境和工具pip install pyqt5-tools或者pip install pyside2,接着直接在Pycharm中导入相应的interpreter即可进行开发,无论是在创建方式还是书写代码的速度以及报错和环境问题,Pycharm的使用体验远远高出Eric6,同时因为Eric6我使用的是作者直接给的集成环境所以有一些需要额外安装的包比如书写md的包很可能就根本找不到正确的位置安装

最后声明一点,因为现在是初学阶段,我们做笔记的地方选择的基本都是能够理解但是也比较重要的部分,关于一些不常用的知识点或者是一些较难需要在练习中坜街的知识点我们并没有过分关注,可能会在之后的时间陆续补充进阶知识点;

一、python语法

1.partial函数

函数在定义时如果指定了参数个数,则函数在执行时需要带上所有的必要参数进行调用,然而函数的参数中有一部分依赖于程序的执行,有一部分实际是固定已知的,此时可以使用partial函数

1
2
3
4
5
6
7
8
9
import functools

def add(a,b):
return a+b
#add(3,4)=7
#上述函数我们在调用的时候需要传入a,b两个参数,下面是用partial函数产生一个新的函数
add_2=functools.partial(add,3)
#此时我们调用add_2函数就只需要一个参数了
#add_2(4)=7

2.lambda表达式

lambda是一个表达式而非一个语句,lambda返回一个值,lambda用于编写简单函数,def用于处理更强大的任务

  • 语句是可以单独执行的、能够产生实际效果的代码;
  • 表达式则是包含在语句中,根据某种条件计算出一个值或得出某种结果,然后由语句去判断和处理的代码
1
2
3
fun = lambda x,y:x+y
fun(2,3)
#得到的结果为5,lambda 关键字后跟参数,接着是冒号后面跟表达式

3.类和实例

python是一门面向对象的编程语言(这一点与C++是一致的)

1
2
3
4
5
6
class MyClass:
count = 0
name = 'DefaultName'

def __init__(self,name):
self.name = name

我们主要区分关于类的变量和实例的变量(属性和方法统称为变量)

  • 类级别的变量是该类所有实例共享的,属于类级别的变量在使用时需要带上类名如MyClass.count或MyClass.name
  • 对象级别的变量是实例对象特有的,属于对象级别的变量在使用时一定要带上self表明属于当前对象如self.name
1
2
3
4
5
6
7
8
9
class MyCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量

def __privateCountFun(self):
print('这是私有方法')

def publicCountFun(self):
print('这是公共方法')

类的方法:与一般函数不同,类的方法必须包含参数self且作为第一个参数(无论是否使用该参数)

类的私有方法:只能在类的内部使用,以双下划线开头(只能在类的内部使用也就是说不能使用实例.__privateCountFun()这种方式调用私有方法,只能在类定义的时候使用self.privateCountFun()

类的私有属性:只能在类内部使用,以双下划线开头

  • 实例不能访问私有方法或私有属性

二、Qt Designer

1.概述

1.1 设计师界面

Qt Dedsigner符号MVC(模型——视图——控制器)设计模式,做到了显示和业务逻辑的分离

  • Widget 通用窗口
  • Main Window 主窗口(常用模板):setMenuBar、addToolBar、setCentralWidget、setStatusBar几种行为只有QMainWindow类具有

控件基类使用原则

  • 如果需要嵌入到其他窗体中,则基于QWidget创建;

  • 如果是顶级对话框,则基于QDialog创建;

  • 如果是应用程序主窗体,则基于QMainWindow创建;

1.2 使用ui文件

实际上有多种方法可以使用ui文件,一种就是直接动态加载ui文件,一种就是将ui文件转为py文件

(1)动态加载ui文件

使用如下格式动态加载ui文件(这个方法源自于pyside2教程,第一个代码适用于在逻辑文件中使用代码自定义实现信号/槽机制,第二个适用于我们在ui文件中创建了简单的信号/槽机制直接动态导入就可以使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication
class Stats:
def __init__(self):
# 从文件中加载UI定义,接着从 UI 定义中动态创建一个相应的窗口对象,此处我们将其命名为ui
self.ui = uic.loadUi("main.ui")
# 注意:在创建窗口对象后,里面的控件对象也成为窗口对象的属性了,使用.访问属性,注意名字要与设计师中为控件取的名字一一对应!!
# 比如 self.ui.Button , self.ui.TextEdit
#为Button控件的clicked事件绑定(connect)事件处理函数handleCalc
self.ui.Button.clicked.connect(self.handleCalc)
def handleCalc(self):
#定义handleCalc函数执行的功能
info = self.ui.textEdit.toPlainText()

if __name__=="__main__":
app = QApplication([])
stats = Stats()
stats.ui.show()
app.exec_()
1
2
3
4
5
6
7
8
9
10
11
12
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication
class Stats:
def __init__(self):
# 从文件中加载UI定义并从 UI 定义中动态创建一个相应的窗口对象,此处我们将其命名为ui
self.ui = uic.loadUi("closeWinBtn.ui")

if __name__=="__main__":
app = QApplication([])
stats = Stats()
stats.ui.show()
app.exec_()

(2)静态导入py文件(推荐)

首先介绍两种方法在pyqt5中将ui文件转换为py文件

(1)

  • 借助Eric6“编译窗体”(实际是使用了pyuic5),可以得到编译后的窗体.py文件
  • 使用pyuic5工具进行编译的时候,会报错搜索路径中不存在该进程——在搜索路径tools中可能有一些tools没有安装上,或者是我们指定的路径中没有该工具(比如pyuic5.exe,实际上它是在scripts文件夹下,但我们添加的搜索路径是tools,而且还不能添加多个搜索路径)所以这里最直接暴力的解决方法就是在环境变量中添加pyuic5.exe(没尝试过直接复制exe文件到tools目录下,理论上也是可行的)

(2)通过命令行转换(这一步实际上也是借助pyuic5完成,所以前面我们选择将其路径添加到环境变量中而非直接复制粘贴pyuic5到tools中是正确的2022/6/30 15:49 这句话是针对当时我使用Eric6的时候,直接点击使用窗口编译ui文件报错找不到pyuic5,然后我就把miniconda虚拟环境中的pyuic5添加到用户环境变量中没想到成功了,如果我们使用Pycharm的话根本不需要这么麻烦,直接在Terminal中对ui文件夹下的ui文件执行下面的Shell命令即可

1
pyuic5 -o UI.py stats.ui

转换过后的py文件内容格式大致如下,存放的位置是在项目根目录下

我们将ui文件转换为py文件只是第一步,接下来就需要使用继承的知识,将界面firstMainWin.py文件导入到业务逻辑文件中使用,实现界面与逻辑的分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from PyQt5.QtWidgets import QApplication,QMainWindow
#从firstMainWin.py中导入UI文件的窗体类Ui_MainWindow
from firstMainWin import Ui_MainWindow

#继承界面文件的主窗口类
# 注意:这里选择要继承的父类,要和UI文件窗体类是同一类型(Ui_MainWindow与MainWindow是相同类型) —— 主窗口是QMainWindow,表单是QWidget,对话框是QDialog
#简单来说要继承的父类要么是QMainWindow,要么是QFrame,多利用网络检索判断出错类型
class MyMainWindow(QMainWindow):
def __init__(self):
#简单的说super().__init__(),就是继承父类的init方法;super()函数就是用来继承父类的方法的
super().__init__()
# 动态创建ui对象,从文件中导入定义的界面类Ui_MainWindow
self.ui = Ui_MainWindow()
# 初始化界面
self.ui.setupUi(self)
# 使用界面定义的控件,也是从ui里面访问(这里给出一个示例)
self.ui.webview.load('http://www.baidu.com')

#是当前文件就执行,不是当前文件(比如该py文件被import到其他py文件中)就不执行其中的代码块
if __name__=="__main__":
app = QApplication([])
stats = Stats()
stats.ui.show()
app.exec_()

咱们上面说过通过设计师可以做到界面显示与业务逻辑的分离,实际上是需要我们手动去实现的,本质上就是在项目中额外新建一个py文件,该py文件作为用户入口文件,导入了之前生成好的界面py文件(动态加载ui文件也是相同的道理),这样当我们修改界面文件的时候对业务逻辑是没有影响的(除非在业务逻辑文件中涉及对界面文件的一些修改操作)

  • 假如使用的是动态加载ui文件,可参照pyside2教程中的做法;

  • 假如使用的是import py文件也就是静态加载界面py文件,需要用到继承的知识,尤其需要注意继承的父类;

  • 可能书上的代码更加严谨,添加了sys.exit()等异常终止机制,我们这里为了简洁明了选择简约的写法

(3)总结

实际上我个人更倾向于直接使用动态加载,尽管这样做在最后发布打包程序的时候需要额外手动将ui文件放进去,但是在我们设计的过程中不需要修改一遍ui就编译一遍,可以做到即改即看;

但是,在之后的开发过程中(2022/6/30进行天气预报项目时)使用动态加载会出问题,假如我们给某个内置信号自定义了一个自定义槽函数,在使用uic动态加载的过程中会直接报错说根本没有这个槽函数(因为动态加载在加载的时候就已经开始运行代码了,但是运行到自定义槽函数的时候自定义槽函数还在逻辑文件中没有被执行定义,所以就会报错),这个时候就只能使用静态加载了(也就是说尽管我们的静态可能比较麻烦,但是针对任何一种情况不会报错“万精油”);

综上,我们要学会熟练使用静态导入进行开发的方式

1.3 布局管理

布局管理:即通过布局将各种不同功能的的控件放在程序主窗口中

一般的布局方式有两种,分别是通过布局管理器layout进行布局,另一种是通过容器控件进行布局

  • 容器控件(containers)是指能够容纳其他控件(子控件)的控件,注意与layout(布局管理器)不同,这点从它们的属性也可以看出来(但是它们都被称为对象,因为python面向对象编程);
  • 我们在实际开发过程中并不需要严格记住控件有哪些,根据属性即可判断;
  • 本质上,使用容器控件进行布局最终还是需要依赖于布局管理器进行布局;
  • 当使用布局管理器接管控件的布局后,控件的geometry属性会成为灰色,此时既无法通过手动拖拽改变控件大小,也无法修改geometry的值改变控件的位置和大小;
  • PyQT遵循一个原则:主窗口内的所有窗口控件都有自己的父类

(1)geometry属性

geometry(几何学)属性在pyqt中用于设置控件在窗口中的绝对坐标与自身大小

表示控件左上角距离主窗口左侧为370px,距离主窗口上侧110px,控件宽度为88px,控件高度为23px;

+++++++++++++++++++使用layout接管控件后,使用以下属性来设置其中的控件+++++++++++++++++++

(2)minimumSize & maximumSize

前者用于设置控件在layout中的最小尺寸,后者设置控件在layout中的最大尺寸

每个属性右侧都有一个撤回按钮,可以恢复默认设置属性

(3)sizePolicy属性

大小策略属性用于实现在layout中对控件进行微调

Fixed:窗口控件具有其sizeHint(尺寸提示)期望的尺寸且不会改变;
Minimum:窗口控件的sizeHint期望的尺寸是其最小尺寸;
Maximum:窗口控件的sizeHint期望的尺寸是其最大尺寸;
Preferred:窗口控件可以比sizeHint的尺寸大,可以缩小到minisizeHint的尺寸;
Ignored:无视sizeHint和minisizeHint提示的尺寸;

水平伸展和垂直伸展默认都是0,即在同一个layout中的控件随着layout的伸缩的变化比例相同,假如我们分别设置三个标签的垂直伸展为1:3:1,则垂直拉伸layout后出现如下情况:

1.4 设计顺序

这一个小节在pyside2中也讲过,使用设计师进行界面设计的大致流程如下

  • 先不使用任何Layout,把所有控件按位置摆放在界面上(也就是直接在画布上给出整个程序的雏形);
  • 然后先从最内层开始进行控件的Layout设定(也就是按照从小到大的顺序,按照情况选择垂直布局或水平布局);
  • 逐步拓展到外层进行控件的Layout设定(将小的控件组合为一个Layout后,还可以继续将这些小Layout组合成大的Layout,此时的控件比例肯定会失调,我们将在下一步进行调整);
  • 最后调整layout中控件的大小比例,优先使用Layout的layoutStrentch属性来控制,也可以选择加入space等控件协助调整;

注意:为了方便我们对对象进行管理,我们需要养成设置控件对象名、窗口对象名的习惯(注意不是双击编辑的text内容,而是只能在属性框编辑的objectName)

1.5 信号和槽概述

我们可以类比于前端设计中的事件和事件处理函数,当信号发射时(事件被触发),槽函数会自动执行(与事件绑定的事件处理函数会自动执行),PyQT5中信号和槽通过QObject.signal.connect()绑定,信号和槽构建了一种强大的控件编程机制;

  • 多个信号可以与单个槽绑定;
  • 单个信号可以和多个槽绑定;

PyQT5中常用的三种方式将信号与槽进行绑定:

  • 在Designer窗口的UI设计中添加信号和槽(这种方法适用于添加PyQt默认的信号和槽,实际上我们添加自定义的信号和槽也是可以的,只要在业务逻辑文件中对其进行定义);

    我们可以通过工具栏的编辑信号/槽来手动连线进行编辑

    也可以通过信号/槽编辑器手动输入来进行编辑

  • 通过Eric的“生成对话框代码”产生信号和槽;

这种方法存在缺点,就是生成的py文件无法直接执行,还需要我们进行改写,这需要我们对其代码机制的深刻理解才能掌握(再次证明Eric6不是一个好的编译器)

  • 通过代码连接信号和槽(pyside2就是这种做法,这种方法可以自定义槽函数);

三、PyQt5

1.PyQt5基本控件类

1.1 窗口类型

  • PyQt中将没有嵌入到其他控件中的控件称为窗口(本质上窗口也是控件,即PyQt中一切皆为控件);

QMainWindow QWidget QDialog 三个类用于创建窗口,可以直接使用,也可以继承之后再使用

  • QMainWindow是最常用的窗口形式,可以说是GUI程序的主窗口;
  • QDialog是对话框窗口的基类(对话框主要用于执行短期任务比如登录、输入信息等);
  • 若不确定是作为主窗口还是作为对话框(即可能作为顶层窗口,也可能嵌入到其他窗口中)则使用QWidget类;

(1)QMainWindow类

QMainWindow类创建的窗口是顶层窗口(没有父窗口的窗口),它可以包含菜单、工具、子窗口等;

QMainWindow类继承自QWidget类,拥有它的所有派生方法和属性;

QMainWindow类中常用方法如下:

注意:QMainWindow不能设置layout,因为它有自己的布局;

(2)QWidget类

基础窗口控件QWidget类是所有用户界面对象的基类,也就是所有的窗口和控件都直接或者间接的继承自QWidget类;

  • 窗口是指程序的整体界面,可以包含标题栏、菜单栏、工具栏、按钮等;
  • 控件是指按钮、复选框、进度条等这些组成程序的基本元素;
  • 一个程序可以有多个窗口,一个窗口也可以有多个控件;
  • 没有父类的控件会被当做一个窗口处理,此时setWindowTitle()和setWindowIcon()函数会生效;

1.2 QLable类

QLable类是界面中的标签类,继承自Qframe类;

QLable对象可以作为一个占位符显示不可编辑的文本或对象,也可以放置一个GIF动画、纯文本、富文本、链接等;

QLable类中常用的方法如下

QLable类中的常用信号如下

1.3 QLineEdit类

QLineEdit类的对象是一个单行的文本框控件,可以输入单行的字符串,需要输入多行的字符串则使用QTextEdit类;

QLineEdit类的常用方法如下

QLineEdit类的常用信号如下

1.4 QTextEdit类

QtextEdit是一个多行文本框,可以显示多行文本的内容,当文本内容超过控件显示范围的时候可以显示水平或垂直滚动条

QTextEdit常用方法如下

1.5 QAbstractButton类

PyQt根据不同的使用场景将按钮分为不同的表现形式;

按钮基类是QAbstractButton,提供了按钮的通用功能,QAbstractButton是抽象类,不能实例化,必须由其他类继承QAbstractButton类实现不同的功能和不同的表现形式;

QAbstractButton类提供的状态如下

QAbustractButton提供的信号如下

(1)QPushButton类

QpushButton类继承自QAbstractButton,是最常见的按钮类;

QPushBtton类常用方法如下

(2)QRadioButton类

提供了一组可供选择的按钮和文本标签,用户可以选择其中一个选项(也就是单选按钮)

(3)QCheckBox

提供了一组带文本标签的复选框,用户可以选择多个选项

QCheckBox常用方法如下

(4)QComboBox

就是我们常见的下拉列表框;

QComboBox常用方法如下

QComboBox常用信号如下

1.6 QDialog类

为了实现更好的人机交互,操作系统往往会提供一系列的标准对话框完成特定场景下的功能;

QDialog类的子类的继承结构如下

QDialog类常用方法如下

(1)QMessageBox

QMessageBox是最常见的通用弹出式对话框

QMessageBox的标准按钮类型如下

(2)QInputDialog

(3)QFontDialog

(4)QFileDialog

1.7 窗口绘图类控件

下面介绍的控件类都是关于如何实现在窗口中绘图(这非常类似于使用easyX进行绘图的图形化界面开发)

(1)QPainter

(2)QPen

相比于QPainter直接调用函数进行绘图的限制,使用钢笔绘图更加灵活;

(3)QBrush

(4)QPixmap

2.PyQt5高级控件类

2.1 多线程

一般来说多线程技术涉及三种方法,其中一种是使用计时器模块QTimer;一种是使用多线程模块QThread;还有一种是使用事件处理程序;

(1)QTimer

假如需要在应用程序中周期性的进行某项操作,可以使用QTimer计时器;

QTimer类提供了重复和单次的定时器,要使用定时器需要事先实例化一个QTimer对象,接着将其timeout信号连接到相应的槽,最后调用start()启动计时器,计时器会以恒定间隔发出timeout信号;

(2)QThread类

这是最常用的实现多线程技术的方法,QThread是Qt线程类中最核心的底层类

要使用QThread开始一个线程,可以创建它的一个子类,然后覆盖其QThread.run()函数——简单来说就是建立一个自定义类ChildThread使其继承自QThread,并实现其run()方法即可;

1
2
3
4
5
6
7
8
9
10
class ChildThread(QThread):
def __init__(self):
super(Thread,self).__init__()
def run(self):
pass #此处用于定义线程相关的代码
#完成上述类的创建我们就可以开始使用一个新线程了
#创建新线程
thread=Thread()
#启动新线程,线程启动后会自动调用其实现的run方法,该方法就是该线程的执行函数
thread.start()

(3)事件处理

PyQt为事件处理提供了两种机制:一种是前面提到过的高级的信号与槽机制,另一种是低级的事件处理程序;

低级的事件处理程序也就是QApplication.proccssEvents()函数的使用方法,其作用是处理事件(即刷新页面);

当执行耗时的程序时在界面上的表现就是一直卡在某个页面,直到程序执行完成页面才会刷新得到新页面;我们可以在执行耗时程序的地方插入QApplication.proccssEvents()函数,使其一边执行耗时程序一边不断刷新页面(其实这样看来事件处理程序并不能算严格意义上的多线程,仅仅只是一个掩饰并不能提高程序的执行效率)

2.2 网页交互

PyQt5中可以通过QWebEngineView类来使用网页控件

QWebEngineView类的控件使用load()函数加载一个web页面(实际上就是使用HTTP GET方法加载页面),也可以使用setHtml()函数加载本地的Web代码;

四、信号与槽进阶

注意:书中的代码很大一部分其实显得冗余,实际上白月黑羽教程中的代码(也可以参考前面我写的代码)这种风格是最简约的,我们有选择性的学习就行,千万不要钻牛角尖;

PyQt的窗口控件中有很多内置信号,开发者还可以额外添加自定义信号;

早期的GUI编程中使用的是回调机制(也会是Web编程里面涉及的自动执行的回调函数),PyQt使用了一种新的对象之间的通信机制也就是信号与槽;

信号与槽主要有如下三种分类:

  • 内置信号与槽的使用;
  • 自定义信号与槽的使用(内置只包含一些常用的信号与槽,有些信号的发射找不到对应的内置函数;且内置函数的参数是固定的不能自定义);
  • 装饰器的信号与槽的使用(本质上是第一种方法的衍生)

1.定义信号

PyQt的内置信号是自动定义的,使用pyqtSignal()函数可以将自定义信号在创建类时定义为类的属性;

  • 信号必须在类的创建时定义,不能在类创建后作为类的属性动态添加进来;

2.操作信号

使用connect()函数可以将信号绑定在槽函数上;

使用disconnect()函数可以解除信号和槽函数的绑定;

使用emit()函数可以发射信号(作用类似于主动触发事件;)

3.事件处理机制

这里我们需要先说明一点,在本节之前文章中出现的所有事件以及事件处理函数都指的是Web开发中的处理机制,是与信号/槽机制非常类似的,但是在这之后介绍的事件处理机制就是PyQt的专属概念了

前面我们提到过PyQt为事件处理提供了两种机制:一种是前面提到过的高级的信号与槽机制,另一种是低级的事件处理程序。至于什么是高级什么是低级我们将在这里给出答案:信号与槽机制可以看作是对事件处理机制的高级封装,我们只需要关心信号的发射、处理就行,信号与槽机制为我们屏蔽了底层事务处理逻辑(我们可能仅仅只是click了一下按钮于是产生了click信号,但是实际底层调用了许多事件处理函数来解决如按钮如何接受并处理鼠标点击事件),而这一系列的底层事务逻辑实际上就是低级的事件处理程序;

3.1 常见事件类型

PyQt是对Qt的封装,而Qt程序是由事件驱动的,其每一个动作都由某个事件触发;

4.窗口传递数据

假如程序只有一个窗口则应该关心该窗口内的各个控件之间是如何进行数据传送的;

如果程序有多个窗口,则还需要关心不同的窗口之间是如何传输数据的;

  • 对于具有单一窗口的程序来说,一个控件的变化会影响另一个控件的变化,这种变化利用信号与槽的机制非常容易解决;
  • 对于多窗口情况,一般有两种解决方法
    • 一种是主窗口获取子窗口中控件的属性;
    • 另一种是通过信号与槽机制,一般是子窗口通过发射信号的形式传输数据,主窗口的槽函数获取这些数据;

4.1 单一窗口数据传递

这个操作方法和同一个控件的信号/槽机制非常相似,就是把一个控件的信号绑定在另一个控件的槽函数上

1
2
3
4
5
#先创建两个控件对象
lcd=QLCDNumber(self)
sider=QSlider(Qt.Horizontal,self)
#接着连接sider的信号函数和lcd的槽函数
slider.valueChanged.connect(lcd.display)

4.2 多窗口数据传递:调用属性

注意:我们在下面的介绍过程中可能会默认将子窗口和主窗口分别存储为py文件,这也是我们实际开发中使用的方法,因为我们使用设计师创建窗口的时候一个ui文件只能创建一个窗体,如果一个ui文件中包含多个窗体很容易出现不必要的麻烦;

PyQt编程过程中经常会遇到输入或选择多个参数的问题,但是直接将所有参数写在一个窗口中会导致主窗口非常臃肿,一般我们会选择调用对话框,在对话框中进行参数的选择,关闭对话框会将参数值返回给主窗口;

前面我们已经介绍过PyQt中常见的一些对话框类,用于输入数据、修改数据、更改应用设置等;

使用对话框进行参数的传递也涉及在不同的窗口之间传递数据,这里我们主要介绍通过属性传参;

将对话框作为一个子窗口,在之后会新建一个主窗口来调用这个子窗口的属性(也就是子窗口作为一个py文件,主窗口作为一个py文件):直接在主窗口程序中实例化对话框类,接着调用该对话框对象的函数;

4.3 多窗口数据传递:信号与槽

使用信号与槽机制,一般通过子窗口发射信号,主窗口通过槽函数捕获这个信号,然后获取信号里面的数据;

子窗口发射的信号有两种

  • 一种是PyQt内置的一些信号;
  • 另一种是用户自定义的信号,其参数类型可以自定义;

上面就是简单的展示了在主窗口文件中如何绑定子窗口的信号,我们在主窗口文件中将子窗口实例化为dialog,接着将dialog的内置信号/自定义信号与主窗口的槽函数绑定;

五、PyQt5图形和特效

我们很有必要定制窗口样式,以实现统一的窗口风格和美化窗口界面(假如QQ的窗口还是默认的原生窗口样式一定是非常不好看的)

下面的介绍我们都会结合实际的代码进行演示;

1.设置窗口风格

2.设置窗口样式

3.全屏展开无边框窗口

4.简单绘图

5.QSS

Qt样式表是用来自定义控件外观的一种机制,QSS参考自CSS但是其选择器少且可以使用的QSS属性也少;

QSS使界面美化和代码层分开便于维护;

6.设置窗口背景

窗口背景主要包括背景色和背景图片,主要使用如下三种方式设置窗口背景:

  • QSS设置窗口背景;
  • QPalette设置窗口背景;
  • 使用QPainter绘制背景;

PyQT5
https://gintoki-jpg.github.io/2022/06/28/工具_PyQT5/
作者
杨再俨
发布于
2022年6月28日
许可协议