1. 介绍
制作程序UI界面,一般可以通过UI制作工具(例如:Qt Designer)和纯代码编写两种方式。在PyQt中,也可以采用这两种方式,以下主要是介绍使用Qt Designer来制作UI界面。
2. QtDesigner 快速入门
- QtDesigner是专门用来制作PyQt程序UI界面的工具,其生成的UI界面是一个后缀为.ui的文件。
- 可以通过命令将.ui文件转换为.py格式的文件,并被其他Python文件引用
- QtDesigner符合MVC(模型-视图-控制器)设计模式,做到了显示和业务逻辑的分离,其具有以下的优点:
- 使用简单,通过拖拽和点击就可以完成界面设计,还可以随时预览查看效果图
- 转换Python文件很方便,QtDesigner保存的设计好的用户界面.ui文件,实际上是XML格式的文本文件。如果想要在PyQt中使用该文件,可以通过pyuic5命令将.ui文件转换为.py文件,然后再进行引用。
- 启动文件我的电脑上目录位于:
D:\anaconda\envs\ppocr\Scripts
。这个位置与以前版本有很大变化。 - 我电脑上的版本:
- python3.7
- PyQt5 5.15.1
- PyQt5-tools 5.15.1.2
2.1 新建主窗口
打开QtDesigner,会自动弹出新建窗口
对话框,在模板选项中,最常见的就是Widget(通用窗口)和Main Window(主窗口)。
PS:在PyQt5中,Widget被分离出来,用来代替Dialog,并将Widget放入了QtWidget模块库中。
模板选择Main Window
,就会创建一个主窗口,可以看到主窗口默认添加了菜单栏
、工具栏
和状态栏
。
2.2 QtDesigner界面窗口区域介绍
- 左边是工具箱(Widget Box),里面的控件支持拖动(拖到主窗口中),包括布局layouts,Buttons按钮等。
- 在菜单栏中选择“窗体(Form)”→“预览(View/View in)”,或者使用Ctrl+R快捷键。
- 右边分别是
- 对象查看器(Object Inspector),可以查看主窗口中放置的对象列表
- Property Editor(属性编辑器),其中提供了对窗口、控件、布局的属性编辑功能
- ObjectName 控件对象名称(用于代码编写)
- geometry 相对坐标系
- sizePolicy 控件大小策略
- minimumSize/maximumSize 最小/大宽度 高度
- font 字体
- cursor 光标
- windowTitle 窗口标题(控制显示)
- iconSize 图标大小
- toolTip 提示信息
- statusTip 任务栏提示信息
- text 控件文本
- shortcut 快捷键
- 信号/槽编辑器(Signal/Slot Editor)可以为控件添加/编辑自定义的信号和槽函数
- 动作编辑器( Action Editor )
- 资源浏览器( Resource Browser)可以为控件添加图片,比如Label、Button的背景图片
2.3 查看UI文件
如果电脑上不幸安装了UltralSO,那就会看到你的.ui文件默认是这个软件打开的。。将.ui文件拖入文本编辑器,就可以看到xml格式的文本内容了。
直接修改.ui文件中的内容与在界面上进行修改效果是一样的,即使用Qt Designer就能够更快的开发出程序界面,避免了使用纯代码编写带来的繁琐
2.4 .ui文件转为.py文件
方式1
如果使用的是Eirc6 IDE工具,则只需要切换到“窗体”选项卡,选中.ui文件,右击选择“编译窗体”即可。操作完成后,切换回“源代码”选项卡,就可以看到生成的.py文件。然后双击.py文件,点击“开始”菜单→“运行脚本”,或者按F2键,运行结果与.ui呈现结果一致,说明转换成功了。
方式2
使用命令行进行转换
pyuic5 -o XXX.py XXX.ui
pyuic5这个程序默认安装在D:\anaconda\envs\ppocr\Scripts
注意:在命令行使用的时候要激活特定的conda环境,不然会报错(如果不修改-o的地址,默认就产生在当前命令行目录下)
转换后类似下图(真是厉害!):
2.5 界面与逻辑分离
我们将由.ui文件编译来的.py文件称为界面文件
。
由于界面文件每次编译时都会初始化,所以需要新建一个.py文件调用界面文件,这个新建的.py文件被称为逻辑文件
或者业务文件
。
界面文件和逻辑文件是两个相对独立的文件,这样就实现了界面和逻辑的分离。
实现界面和逻辑分离的方法很简单,新建一个xx.py文件,并继承界面文件的主窗口类即可。
例如:(在上面文件里,窗口类是 Ui_MainWindow,假设上面那个文件是test.py)。
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow
from test import *
class MyWindow(QMainWindow,Ui_MainWindow):
def __init__(self,parent=None):
super(MyWindow,self).__init__(parent)
self.setupUi(self)
if __name__=="__main__":
app=QApplication(sys.argv)
myWin=MyWindow()
myWin.show()
sys.exit(app.exec_())
# 业务文件中的类继承界面的主窗口类即可
然后运行这个逻辑文件,就可以看到和在.ui文件中一样界面的显示了,Ok
这样更新界面时,只需要修改.ui之后,编译成对应的.py文件即可,逻辑文件视情况进行一些调整,一般不会调整很多。
这样逻辑和界面分离,对于新手就非常友好。
3. 布局管理入门
Qt Designer提供了4种窗口布局方式,分别是:
- Vertical layout 垂直布局:控件默认按照从上到下的顺序进行纵向添加
- Horizontal layout 水平布局:控件默认按照从左到右的顺序进行横向添加
- Grid Layout 栅格布局:将窗口控件放入一个网格之中,然后将它们合理的划分成若干行(row)和列(column),并把其中的每个窗口控件放置在合适的cell单元中,这里的单元就指行和列交叉划分出的控件。
- Form Layout 表单布局:控件以两列的形式布局在表单中,其中左列包含标签,右列包含输入控件。
这四种布局选项,位于界面左侧的工具箱(Widget Box)的Layouts布局栏中(也是拖动到主窗口中进行使用)。
一般进行布局有两种方式:一是通过布局管理器进行布局;二是通过容器控件进行布局
3.1 使用布局管理器布局
可以尝试新建一个QWidget控件,在其中放入两个子控件:一个文本框lineEdit和一个按钮pushButton,选中控件,右键选中“布局”,就可以指定该空间的布局方式了,如下面所示的水平布局
(选中两个,就表明这两个一起是水平的。)
此外,在对象管理器中也可以看到(QWidget是水平管理布局以及label和button的父控件对象)
此外,如果是从工具箱拖放布局控件
的,则其属性所有的margin默认都是0。可以设置这个marigin来控制布局中控件距离这个 框的上下左右边界(布局的内边界的距离)
布局后,可以发现对象管理器中有很明显的层次关系:主窗口(MainWindow)→布局(Layout)→控件 这样的层次关系。即:窗口一般作为顶层显示,然后将控件按照我们所要求的的布局方式进行排列。
3.2 使用容器进行布局
容器Containers(左侧的Widget box有一栏内容就是Containers)。随便拖一个容器控件(例如Frame)到主窗口,在frame控件里再放几个控件
如下:
在对象控制器里可以看到:和上面区别很明显,就是Containers容器取代了layouts对象,成为了布局层次结构最底层的控件的 父控件。
但是在实际转换后的代码中,可以看到实际上,QFrame控件和子控件之间还是有一个QHBoxLayout的,所以使用容器进行控件布局的本质还是调用布局管理器进行的。
3.3 绝对布局
使用每个控件的geometry
属性:设置控件相对于主窗口的坐标和自身控件的大小。不过这种方式比较粗糙,而且每次都是手动矫正,比较麻烦,一般不会采用。
3.5 布局管理器进阶
3.5.1 geometry属性与布局管理器
说完了geometry
属性之后,还有一个情况要注意,当子控件使用布局管理器之后,可以看到,属性管理器中该子控件的geometry
属性已经变成灰色。
表明该子控件的位置与大小现在由布局管理器接管,与geometry
属性无关了。
3.5.2 Horizontal/Vertical Spacer/Line
Widget Box中Spacers栏中有Horizontal Spacer以及Vertical Spacer。Display Widgets栏中有Horizontal Line以及Vertical Line。这几个都是用来分割布局的
如果直接拖一个Vertical Spacer
到主窗口,保存时会警告
The file contains top level spacers. They will not be saved
Perhaps you forget to create a layout?
所以这个Spacer应该是放在两个布局之间,最后再用一个大的布局保括起来(注意,这个错误可能有点问题,如果确认没有Spacer控件处于最外层,有布局管理器管理,重启一下QtDesigner就好)
放置好之后,是下图的样子
全选,然后水平布局,就得到
预览的效果
如果觉得左边这个布局应该离右边的开始
按钮更远,可以选择那个Horizontal Spacer,在属性中修改:
然后这个Spacer控件就会变宽,然后就可以看到开始
按钮更远了。
- Vertical Spacer和Horizontal Spacer
Display Widgets栏中有Horizontal Line以及Vertical Line
PyQt有一个基本原则:主窗口中所有窗口控件都有自己的父类,都是从上到下逐级继承的。
3.5.3 窗口尺寸属性控制:minimumSize、maximumSize和sizePolicy
minimumSize、maximumSize属性用来设置控件在布局管理器中的最小尺寸和最大尺寸
在属性编辑器里,每个属性在点击value这一栏之后,后面都会有一个小回车箭头的按钮,用于还原默认值/设置。
sizePolicy 每个窗口控件都有属于自己的两个尺寸:
- 一个是sizeHint(尺寸提示):窗口控件的期望尺寸
- 一个是minimumSize及maximumSize:窗口控件压缩时能够被压缩到的最小或支持放大的最大尺寸。
sizePolicy的作用是:如果窗口控件在布局管理器中的布局不能满足我们的需求,就可以设置该窗口控件的sizePolicy来实现布局的微调;每个窗口控件都有这个属性,但是不同窗口控件的sizePolicy可能不同。
例如:PushButton控件的
关于Horizontal /Vertical Policy(水平/垂直策略)的选项说明:
- Fixed:窗口控件具有其sizeHint所提示的尺寸且尺寸不会改变
- Minimum:窗口控件的sizeHint所提示的尺寸就是它的最小尺寸,不能被压缩的比这个值更小
- Maximum:窗口控件的sizeHint所提示的尺寸就是它的最大尺寸,不能被放大的比这个值更大
- Preferred:窗口控件的sizeHint所提示的尺寸就是它的期望尺寸,但是也可以在Minimum和Maximum规定的值之间波动
- Ignored:无视窗口控件的sizeHint和minisizeHint所提示的尺寸,按照默认来设置。
3.6 其他要注意的
3.6.1 打破布局
选中一个布局,右击,在弹出的菜单项中选择 layout->break layout
如果是上一步操作就是选择布局,那么这个选项作用和撤销差不多,但是如果是操作了别的步骤再想撤销,就要使用这种方式。
3.6.2 设置tab键顺序
菜单栏->Edit 编辑->Edit Tab Order 设置Tab键次序
或者直接在工具栏上找到这个tab顺序界面
默认是最左侧这个,编辑控件界面。
默认是按照控件创建顺序来进行的,如果想要修改Tab键顺序,有两种方式
- 可以直接点击序号,就会按照点击次序从1开始。
- 在主窗口(编辑tab次序界面情况下)中空白处右击,选择Tab Order List 制表符顺序列表ya
就可以上下调节,或者使用reset复位。
3.7 测试程序
4. 信号和槽关联
4.1 简单介绍
- 信号(signal)和槽(slot)是Qt的核心机制。在创建事件循环之后,通过建立信号和槽的连接就可以实现对象之间的通信。
- 信号发射时(emit)。连接的槽函数将会自动执行。在PyQt5中,信号和槽通过QObject.signal.connect()函数连接。
- 所有从QObject类或者其子类(如QWidget)派生的类都能够包含信号和槽。当对象状态改变时,信号就从这个对象发射出去。槽用于接收信号,但是它们是普通的对象成员函数。信号和槽之间是一种多对多的关系(可以多对一,也可以一对多)
- 在Qt编程中,通过Qt信号槽机制对界面上的窗口控件的操作进行响应处理。(不同的控件能够发射的信号种类和触发的时机也是不同的)
为控件发射的信号指定对应的处理槽函数一般有三种方法:
- 在窗口的UI设计中操作添加信号和槽
- 代码连接信号和槽
- Eir的“生成对话框代码”的功能产生信号和槽
4.2 使用QtDesigner设置信号和槽
4.2.1 信号槽编辑界面(拖动实现)
QtDesigner提供了基本的编辑信号槽的方法。可以通过以下几种方式进行编辑:
- 直接进入信号槽编辑界面
然后点击发射者(例如:关闭窗口button),按住左键拖动到接受者(MainForm窗体上),然后就会弹出一个连接配置
框。选中左下角的复选框(显示从QWidget中继承的信号和槽),然后closeWinBtn(关闭窗口那个Button的ObjectName重命名后的名称)选择cliked()
信号,然后Form的槽函数中选择close()
,这表示按下closeWinBtn后,发射cliked()
信号,这个信号会被Form窗体的槽函数close捕获,然后出发Form窗体close行为。
类似下图:
选定信号和槽函数后,变蓝色。此时,Ctrl+R预览,点击关闭窗口按钮,已经可以触发槽函数了。
把界面文件进行转换,编译为.py文件,然后再使用业务文件进行调用,确实也实现了关闭效果。
可以看到 界面文件转换后 是由这几行代码完成信号槽的功能的。
注意:使用QObject.signal.connect()连接的槽函数不要加括号,不然会报错。
业务文件其实很简单,貌似都是统一的格式(继承,然后启动一个主函数,接受系统函数,然后显示,然后窗体程序结束整个程序就结束)
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from ui2test import Ui_MainWindow
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
myWin = MyMainWindow()
myWin.show()
sys.exit(app.exec_())
4.2.1 信号槽编辑器
通过下拉框选择发射者
,发射者的信号
, 接受者
,接受者的槽处理函数
这里的现象是:初始时两个label都呈现,复选按钮选择没有选中;点击一次选择 2(label_8)消失,1(label)仍然出现;再点击,2(label_8)出现,1(label)仍消失。
即(CheckBox每次的bool要么为true 要么为false,每次点击CheckBox,就会发送选中/未选中的信号给两个label,然后二者根据那个信号去使用槽函数做处理)
5.菜单栏与工具栏
5.1 菜单栏
主窗口在创建时默认就包含菜单栏、工具栏以及任务栏。
双击菜单栏:在这里输入(Type Here)
就可以开始输入文字,回车即可生成菜单。
还可以在菜单后面加上类似:文件(&F)
英文的括号和符号,来加入快捷键
一级菜单下还可以一直加入子菜单,(回车表示确认)
子菜单可以通过动作编辑器或者属性编辑中的shorcut来添加快捷键(创建了子菜单后,这个是自动出现的。。。)
双击需要编辑的菜单内容的Name,就可以设置它的图标、快捷键等。(在预览中,点击 打开/关闭/新建 这些功能都无效)
5.2 工具栏
使用Qt Designer默认生成的主窗口中不显示工具栏,可以通过单击鼠标右键来添加工具栏。(随便找个主窗体中空白的地方右击就好了)
然后在动作编辑器里添加一个新的动作
然后将刚刚创建的这个动作拖入新建的工具栏中(拖动的时候会出现红色的线表示定位)
5.3 测试上述功能
把界面文件转为.py。实现文件的打开/关闭/新建都是在业务代码里完成的,
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow,QWidget,QFileDialog
from ui2test import Ui_MainWindow
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
# 文件菜单栏下的关闭 关闭窗体
# actionguanbi 是 关闭 动作 的Name
self.actionguanbi.triggered.connect(self.close)
# 文件菜单栏下的打开 执行自定义函数
self.actiondakai1.triggered.connect(self.openMsg)
def openMsg(self):
file,ok=QFileDialog.getOpenFileName(self,"打开","C:/","All Files (*);;Text Files (*.txt)")
self.statusBar.showMessage(file)
if __name__ == "__main__":
app = QApplication(sys.argv)
myWin = MyMainWindow()
myWin.show()
sys.exit(app.exec_())