目录
1、简介
PyQt基于QT库的python封装,是一个图形用户界面(GUI)工具包,允许用户使用python语言创建桌面应用程序。目前,不同版本中,PyQt5是较为流行的版本,支持python 2.7 和 python 3.x 。
PyQt支持两种开发方式,可视化和编程化。
- 编程式创建界面无需多说,pip安装成功以后,有较深基础功底的可直接上手编码。
- 可视化方式对新手非常友好,用户可基于Qt Designe工具包进行组件拖拽、布局管理等操作。
2、安装步骤
2.1 安装PyQt5
在自己的python虚拟环境中,pip安装依赖包PyQt5, pyqt5_tools
pyqt5_tools 包含一些辅助 PyQt5 的开发工具,其中就有我们要用的 Qt Designer
pip install PyQt5 PyQt5-tools
一般会报错或爆红,建议带上国内镜像源
-i https://pypi.tuna.tsinghua.edu.cn/simple
安装成功以后,win+s 搜索"designer",出现图标表示成功。
如果没有,可以使用下面链接下载安装包,一键安装即可,安装成功后,会直接弹出来该工具。
https://build-system.fman.io/qt-designer-download
2.2 Pycharm配置
python语言开发,大部分使用Pycharm集成开发工具,这里在Pycharm工具中配置QT designer外部工具,在工程开发中让界面设计和解析变得容易。
打开Pycharm,File -> Settings ->Tools -> External Tools,点击加号添加,Program我这里添加的是安装包路径,因为我安装了PyQt_tools,找不到designer。Working directory设置保存ui文件的文件,$FileDir$表示当前文件所在目录
全部设置完成以后,可在Tools ->External Tools中打开。
用designer设计好的界面,会以.ui文件形式保存,下次直接右击该文件,选择External Tools中的designer工具加载即可
保存界面的另一种方式,是将生成的.ui文件解析成python脚本,下次可以直接运行脚本文件,打开ui界面。这种方式一般在整个GUI界面设计完成后执行,当然也不失为学习编程式UGI的好办法。
解析需要用到另外一个工具pyuic,直接命令生成py文件,该工具在安装pyqt后自带,无需安装。
pyuic5 /untitled.ui -o output_ui.py
另一种方式,是将该工具配置到Pycharm中,和上面designer添加方式一样,填写信息如下。
3、界面设计介绍
打开外部工具,会弹出窗口提示,如果不想每次启动都弹,在该窗口左下角设置去掉即可。
先整体认识下designer工具,左侧是不同类型组件,右侧可查看ui结构以及设置每个组件的属性,中间灰色地带为画布。
3.1 组件
组件是构建图像界面基本的模块,pyqt提供了多种组件,这里介绍一些使用频率较高的组件
组件名 | 功能 | |
窗口组件 | Main Window | 用于创建应用程序的主窗口,包含额外功能,如菜单栏、工具栏、状态栏等 |
Widget | 通用窗口组件,无额外功能,可作为Main Window的子组件或容器小部件使用 | |
菜单类 | MenuBar | 菜单栏 |
StatusBar | 状态栏 | |
ToolBar | 工具栏 | |
展示组件 | Label | 显示文本或图像 |
Progress Bar | 显示进度条 | |
Graphics View | 显示图像,支持缩放平移以及交互 | |
List View | 以列表形式展示多行数据 | |
输入组件 | Line Eidt | 输入单行文本 |
Text Edit | 输入多行文本 | |
按钮组件 | Push Button | 按钮 |
CheckBox | 复选框 | |
RadioButton | 单选按钮,多选项中只允许选择一个 |
3.1.1 菜单类组件
菜单栏组件包含 菜单栏+状态栏+工具栏 三部分
在designer中,先创建一个主窗口, File->New->Main Window
查看主窗口右侧ui结构,自带的菜单栏,状态栏,缺少工具栏ToolBar,选中MainWindow右击添加
主窗口中双击Type Here,输入菜单栏的名称File,回车即可进入到二级菜单栏,我这创建了四个二级菜单项,如下图所示。三级菜单项创建,点击二级菜单项右侧蓝色加号创建。如果想在菜单上面添加分割线,选中菜单名右击 Insert separator。
- Type Here:输入菜单名
- Add Separator:添加分割线
工具栏按钮不同于普通按钮,本质上是动作Action,而菜单栏中的每个菜单项也是Action。在PyQt中,如果在菜单栏中已经创建了某项,比如open,此时open这个动作就已经存在了。
工具栏按钮是直接拖拽现有的动作Action生成的,所以菜单栏和工具栏中,只要是对应同一Action的按钮,响应操作是一样的。在这里,我理解的工具栏更像是快捷键,将菜单栏中较为重要的选项放在外面,便于用户操作,接下里演示如何拖拽生成。
右击选中ui结构中的工具栏(如果没有可添加),下面属性设置中选中QtoolBar,其中
- movable:工具栏移动,默认选中,主窗口左侧会出现一列
- allowedAreas:设置工具栏位置,左右上下均可
找到工具栏可拖拽放到上下左右那个位置都行,下图是放在左侧了。
接下来拖拽Action到工具栏,首先需打开动作编辑(上面总体介绍的图解哪里有),能看到刚才菜单栏中创建的四个菜单项的Action都已经存在了,用鼠标拖住向工具栏位置放。
拖拽都没成功过,手都酸了,一个也没拖进去。这里分享个比较容易拖拽的方式,调节工具栏的高度,怎么调节看下图,依次选中图中选项,设置工具栏的宽和高。
3.1.2 图标和快捷键设置
如果想为菜单栏设置快捷键以及图标,可在动作编辑器中,选中某个Action右击,Eidt。
如下图,快捷键哪直接按键盘就自动输入进去了,Icon图标设置右边下拉三角中,我用的是choose file选择文件。
为全部的Action设置完成以后,如下图右侧所示,这时候拖拽工具栏只有图标,菜单栏中文字,图标,快捷键信息都有,想查看,可用ctrl+r快捷键方式,预览整个界面效果。
切记,添加的图标文件一定要放在你的工程目录下,否则换台电脑找不到图标就报错了 ,解析成python文件后,看下代码,里面用的是绝对文件路径。
3.1.3 分页组件Tab Widget
PyQt中的Tab Widget组件,通过创建选项卡界面,允许在一个窗口中显示多个界面,并来回切换,接下来让我们看看如何实现。
使用designer工具,创建主窗口后,直接拖拽左侧容器组件中的Tab Widget组件,默认是创建两个选项卡的,如中间画布所示,右侧是属性设置中修改选项卡名字的位置。
如果想增加新的选项卡,选中其中一个选项卡,右击弹出属性设置,如下可在该选项卡前面或后面增加新的选项卡。
删除选项卡,依然是选中右击,如下图所示,右拉三角delet。
接下来大家在每个选项卡内 设计自己的界面了,每个界面也都是独立的。
3.2 整体框架
这部分介绍如何使用.py文件执行ui界面。
3.2.1 ui 解析py文件
使用designer设计界面后,保存为.ui文件,使用pyuic工具生成.py,可直接使用如下命令
pyuic5 /untitled.ui -o output_ui.py
这里生成为Mainform.py文件,着重介绍下Mainform.py文件的内容,里面定义了一个类Ui_MainWindow,两个方法setupUi,retranslateUi。用designer工具设计并解析的py文件,一般都是这样的结构模式。
在setupUi方法中,会看到很多组件的变量名,后续会引用这些组件变量做交互操作,大家要分的清自己不同组件名所对应界面的那个区域,尤其是同一种组件的多处使用,最好呢在designer设计中就按自己的想法定义每个组件的名称。
在designer设计过程中,可直接在对应组件的的属性设置中,修改objectName
3.2.2 主函数
解析后的Mainform.py文件,直接运行并不会弹出界面,需要加一些固定内容。
在同目录下,我新建了一个Main.py文件,接着填充该文件。
Main.py内容,首先,定义了一个主窗口类LMainWindow,继承基类QMainWindow,__init__方法中调用了基类的 __init__方法,初始化了 QMainWindow。(照抄即可)
from PyQt5.QtWidgets import *
from Mainform import Ui_MainWindow
class LMainWindow(QMainWindow):
def __init__(self,parent = None):
super(LMainWindow,self).__init__(parent)
其中,parent表示这个窗口没有指定父级窗口,可以独立存在。如果需要将它嵌入到其他窗口中,可以传递一个父级窗口作为parent参数。
接下来定义了一个LMain类,主要是怕类名和文件名一样,所以前面加了个L,大家可自行命名。以下代码中总共包含了3个类函数,
from tkinter import filedialog
from PyQt5.QtWidgets import *
from Mainform import Ui_MainWindow
import sys
import tkinter as tk
class LMainWindow(QMainWindow):
def __init__(self,parent = None):
super(LMainWindow,self).__init__(parent)
class LMain():
def __init__(self):
self.__mainWindow = None
self.__mainform = None
def _init_mainWindow(self):
"初始化主窗口"
self.__mainWindow = LMainWindow() # 主窗口
self.__mainform = Ui_MainWindow() # 主界面
self.__mainform.setupUi(self.__mainWindow)
def run(self):
#创建应用程序实例
app = QApplication(sys.argv)
#初始化主窗口
self._init_mainWindow()
#显示主窗口
self.__mainWindow.show()
#运行应用程序
sys.exit(app.exec_())
if __name__ == '__main__':
app = LMain()
app.run()
初始化__init__自然无需多说,大家可根据自己的需求编写。
初始化主窗口_init_mainWindow,先实例化了上面定义的窗口类LMainWindow对象,后实例化了Mainform.py文件中的Ui_MainWindow界面对象,并主窗口类对象作为参数传递给界面对象中的setupUi函数,将界面和窗口关联起来,使得主窗口能将界面展示出来,这部分照抄就行
run函数,这部分也是照抄就行,都是打开应用程序的一些基本操作。
完成之后,执行Main.py文件,就会弹出咱们设计的ui界面了,此时点击任何按钮都不会有反应。
3.3 信号和槽
使用designer设计的UI,其中设计的动作、按钮等都是没有实际作用的,因为我们并未告诉它就或者设置它,触发后应该去做什么。这部分内容主要是讲解PyQt中的信号和槽机制,为设计的UI界面,加上实际的操作,做到真正的交互。
- 信号(Signal):触发特定组件时发出的信号
- 槽(Slot):接收信号后,调用执行的函数或方法
将信号与槽连接connect,组件(Action,Button等)被触发时,连接的槽函数将被调用执行。
3.2.1 自定义信号和槽
Action动作:action_objectName.triggered.connect(槽函数);
Button按钮:button_objectName.clicked.connect(槽函数);
3.4 交互实现
3.2小节,搭建好了打开ui界面的基本框架,这一部分内容则是为组件添加信号和槽函数,做到真正的交互。以下内容均添加在Main.py文件中得LMain类内。
这是我创建的ui,主要参考标注画框软件Labelimg的界面。
初始化界面如下,图中多创建了一个List View,大家创建一个就行。
当用户分别点击左侧的Open Dir和Save Dir后,会弹出一个对话框,让用户选择一个目录,并记录下该目录的路径,同步展示在右侧的ListView中。如下图所示。
实现代码如下,其中的组件名称可能需要更换。
from tkinter import filedialog
from PyQt5.QtCore import QStringListModel
from PyQt5.QtWidgets import *
from Mainform import Ui_MainWindow
import sys
import tkinter as tk
class LMainWindow(QMainWindow):
def __init__(self,parent = None):
super(LMainWindow,self).__init__(parent)
class LMain():
def __init__(self):
self.__mainWindow = None
self.__mainform = None
self.__opendir =None
self.__savedir = None
def _init_mainWindow(self):
"初始化主窗口"
self.__mainWindow = LMainWindow() # 主窗口
self.__mainform = Ui_MainWindow() # 主界面
self.__mainform.setupUi(self.__mainWindow)
#打开文件动作的槽函数
def _action_event_opendir(self,type):
root = tk.Tk()
root.withdraw()
file_dir = filedialog.askdirectory()
if type == 'opendir':
self.__opendir = file_dir
#更新显示内容
self._init_UI(1)
elif type == 'savedir':
self.__savedir = file_dir
self._init_UI(1)
def _init_view_model(self):
self.view_model = QStringListModel()
def _init_UI(self,type):
"不同状态的界面"
#初始状态
if type == 0:
#设置显示的字符串内容
self.view_model.setStringList([])
self.__mainform.listView.setModel(self.view_model)
#点击Open Dir 动作后界面状态
elif type == 1:
#此时显示选择的opendir路径,和savedir路径
printvalue = [self.__opendir,self.__savedir]
self.view_model.setStringList(printvalue)
self.__mainform.listView.setModel(self.view_model)
def _init_mainForm_widget_event(self):
"绑定组件事件"
#绑定Action
self.__mainform.actionOpen_DIR.triggered.connect(lambda :self._action_event_opendir('opendir'))
self.__mainform.actionSave_Dir.triggered.connect(lambda :self._action_event_opendir('savedir'))
def run(self):
# 创建应用程序实例
app = QApplication(sys.argv)
self._init_mainWindow()
self._init_view_model()
self._init_UI(0)
self._init_mainForm_widget_event()
# 显示主窗口
self.__mainWindow.show()
# 运行应用程序
sys.exit(app.exec_())
if __name__ == '__main__':
app = LMain()
app.run()