制作程序UI界面,一般可以通过UI制作工具(例如:Qt Designer)和纯代码编写两种方式。在PyQt中,也可以采用这两种方式,以下主要是介绍使用Qt Designer来制作UI界面。
D:\anaconda\envs\ppocr\Scripts
。这个位置与以前版本有很大变化。打开QtDesigner,会自动弹出新建窗口
对话框,在模板选项中,最常见的就是Widget(通用窗口)和Main Window(主窗口)。
PS:在PyQt5中,Widget被分离出来,用来代替Dialog,并将Widget放入了QtWidget模块库中。
模板选择Main Window
,就会创建一个主窗口,可以看到主窗口默认添加了菜单栏
、工具栏
和状态栏
。
如果电脑上不幸安装了UltralSO,那就会看到你的.ui文件默认是这个软件打开的。。将.ui文件拖入文本编辑器,就可以看到xml格式的文本内容了。
直接修改.ui文件中的内容与在界面上进行修改效果是一样的,即使用Qt Designer就能够更快的开发出程序界面,避免了使用纯代码编写带来的繁琐
方式1
如果使用的是Eirc6 IDE工具,则只需要切换到“窗体”选项卡,选中.ui文件,右击选择“编译窗体”即可。操作完成后,切换回“源代码”选项卡,就可以看到生成的.py文件。然后双击.py文件,点击“开始”菜单→“运行脚本”,或者按F2键,运行结果与.ui呈现结果一致,说明转换成功了。
方式2
使用命令行进行转换
pyuic5 -o XXX.py XXX.ui
pyuic5这个程序默认安装在D:\anaconda\envs\ppocr\Scripts
注意:在命令行使用的时候要激活特定的conda环境,不然会报错(如果不修改-o的地址,默认就产生在当前命令行目录下)
转换后类似下图(真是厉害!):
我们将由.ui文件编译来的.py文件称为界面文件
。
由于界面文件每次编译时都会初始化,所以需要新建一个.py文件调用界面文件,这个新建的.py文件被称为逻辑文件
或者业务文件
。
界面文件和逻辑文件是两个相对独立的文件,这样就实现了界面和逻辑的分离。
实现界面和逻辑分离的方法很简单,新建一个xx.py文件,并继承界面文件的主窗口类即可。
例如:(在上面文件里,窗口类是 Ui_MainWindow,假设上面那个文件是test.py)。
import sys
from PyQt5.QtWidgets import QApplication,QMainWindowfrom 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文件即可,逻辑文件视情况进行一些调整,一般不会调整很多。
这样逻辑和界面分离,对于新手就非常友好。
Qt Designer提供了4种窗口布局方式,分别是:
这四种布局选项,位于界面左侧的工具箱(Widget Box)的Layouts布局栏中(也是拖动到主窗口中进行使用)。
一般进行布局有两种方式:一是通过布局管理器进行布局;二是通过容器控件进行布局
可以尝试新建一个QWidget控件,在其中放入两个子控件:一个文本框lineEdit和一个按钮pushButton,选中控件,右键选中“布局”,就可以指定该空间的布局方式了,如下面所示的水平布局
(选中两个,就表明这两个一起是水平的。)
此外,在对象管理器中也可以看到(QWidget是水平管理布局以及label和button的父控件对象)
此外,如果是从工具箱拖放布局控件
的,则其属性所有的margin默认都是0。可以设置这个marigin来控制布局中控件距离这个 框的上下左右边界(布局的内边界的距离)
布局后,可以发现对象管理器中有很明显的层次关系:主窗口(MainWindow)→布局(Layout)→控件 这样的层次关系。即:窗口一般作为顶层显示,然后将控件按照我们所要求的的布局方式进行排列。
容器Containers(左侧的Widget box有一栏内容就是Containers)。随便拖一个容器控件(例如Frame)到主窗口,在frame控件里再放几个控件
如下:
在对象控制器里可以看到:和上面区别很明显,就是Containers容器取代了layouts对象,成为了布局层次结构最底层的控件的 父控件。
但是在实际转换后的代码中,可以看到实际上,QFrame控件和子控件之间还是有一个QHBoxLayout的,所以使用容器进行控件布局的本质还是调用布局管理器进行的。
使用每个控件的geometry
属性:设置控件相对于主窗口的坐标和自身控件的大小。不过这种方式比较粗糙,而且每次都是手动矫正,比较麻烦,一般不会采用。
说完了geometry
属性之后,还有一个情况要注意,当子控件使用布局管理器之后,可以看到,属性管理器中该子控件的geometry
属性已经变成灰色。
表明该子控件的位置与大小现在由布局管理器接管,与geometry
属性无关了。
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控件就会变宽,然后就可以看到开始
按钮更远了。
PyQt有一个基本原则:主窗口中所有窗口控件都有自己的父类,都是从上到下逐级继承的。
minimumSize、maximumSize属性用来设置控件在布局管理器中的最小尺寸和最大尺寸
在属性编辑器里,每个属性在点击value这一栏之后,后面都会有一个小回车箭头的按钮,用于还原默认值/设置。
sizePolicy 每个窗口控件都有属于自己的两个尺寸:
sizePolicy的作用是:如果窗口控件在布局管理器中的布局不能满足我们的需求,就可以设置该窗口控件的sizePolicy来实现布局的微调;每个窗口控件都有这个属性,但是不同窗口控件的sizePolicy可能不同。
例如:PushButton控件的
关于Horizontal /Vertical Policy(水平/垂直策略)的选项说明:
选中一个布局,右击,在弹出的菜单项中选择 layout->break layout
如果是上一步操作就是选择布局,那么这个选项作用和撤销差不多,但是如果是操作了别的步骤再想撤销,就要使用这种方式。
菜单栏->Edit 编辑->Edit Tab Order 设置Tab键次序
或者直接在工具栏上找到这个tab顺序界面
默认是最左侧这个,编辑控件界面。
默认是按照控件创建顺序来进行的,如果想要修改Tab键顺序,有两种方式
参考书籍配套代码PyQt5/Chapter03/
为控件发射的信号指定对应的处理槽函数一般有三种方法:
QtDesigner提供了基本的编辑信号槽的方法。可以通过以下几种方式进行编辑:
连接配置
框。选中左下角的复选框(显示从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_MainWindowclass 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_())
通过下拉框选择发射者
,发射者的信号
, 接受者
,接受者的槽处理函数
这里的现象是:初始时两个label都呈现,复选按钮选择没有选中;点击一次选择 2(label_8)消失,1(label)仍然出现;再点击,2(label_8)出现,1(label)仍消失。
即(CheckBox每次的bool要么为true 要么为false,每次点击CheckBox,就会发送选中/未选中的信号给两个label,然后二者根据那个信号去使用槽函数做处理)
主窗口在创建时默认就包含菜单栏、工具栏以及任务栏。
双击菜单栏:在这里输入(Type Here)
就可以开始输入文字,回车即可生成菜单。
还可以在菜单后面加上类似:文件(&F)
英文的括号和符号,来加入快捷键
一级菜单下还可以一直加入子菜单,(回车表示确认)
子菜单可以通过动作编辑器或者属性编辑中的shorcut来添加快捷键(创建了子菜单后,这个是自动出现的。。。)
双击需要编辑的菜单内容的Name,就可以设置它的图标、快捷键等。(在预览中,点击 打开/关闭/新建 这些功能都无效)
使用Qt Designer默认生成的主窗口中不显示工具栏,可以通过单击鼠标右键来添加工具栏。(随便找个主窗体中空白的地方右击就好了)
然后在动作编辑器里添加一个新的动作
然后将刚刚创建的这个动作拖入新建的工具栏中(拖动的时候会出现红色的线表示定位)
把界面文件转为.py。实现文件的打开/关闭/新建都是在业务代码里完成的,
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow,QWidget,QFileDialog
from ui2test import Ui_MainWindowclass MyMainWindow(QMainWindow, Ui_MainWindow):def __init__(self, parent=None):super(MyMainWindow, self).__init__(parent)self.setupUi(self)# 文件菜单栏下的关闭 关闭窗体# actionguanbi 是 关闭 动作 的Nameself.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_())