下面讲GUI界面设计的问题
六.设计流程图
七.绘制界面
这里使用Qt的开发环境:Qt Creater来实现UI窗体可视化设计,其中起主要作用的工具软件是Qt Designer。
(1)在Qt Creator 中点击菜单项“File”——“New File or Project…”,在出现的对话框里选择“Qt”分组里的“Qt Designer Form”出现如图对话框:
在此对话框里选择Project类型为Appliction,中间的模板里选择Qt Widgets Application,这是常见的GUI应用程序项目
(2)点击Choose按钮,出现如图的新建项目向导
设置项目名为QtApp,点击Browse按钮,选择faster-rcnn文件夹,这样创建的项目就保存在了faster-rcnn/QtApp文件夹下了
(3)继续下一步,在出现选择编译工具的页面选择一个Desktop
Qt 5.12.0 MinGW 64-bit即可,因为不需要编译项目,所以选哪个都可以
(4)继续下一步,出现如图的创建UI窗体的界面,这个窗体将作为应用程序的窗体,这里选择基于QMainWindow类的窗体,具有主窗口的特性,窗口上有主菜单栏,工具栏,状态栏等
MainWindow.ui是窗体UI文件,双击这个文件,会打开内置的UI Designer进行窗体可视化设计
本设计用不到菜单栏和工具栏,所以鼠标在相应位置右击remove,删除,然后将窗体大小扩大到适宜大小,如图:
中间左右两侧各放置QScrollArea组件scrollArea,分别表示原图和检测图显示的位置,当显示的图片大小超过scrollArea可显示区域的范围时,scrollArea会自动显示水平或垂直方向的滚动条,用于显示更大范围的区域。scrollArea内部放置QWidget组件用于matplotlib绘图的显示
ScrollArea上方分别用QLabel组件,在右侧工具栏编辑test内容分别标注左侧为原图,右侧为绘图结果
ScrollArea下方放置两个QPushButton,一个用来实现读取并显示本地图片的按钮,一个实现将图片进行检测并显示的按钮
检测按钮下方放置一个QTestEdit组件来显示检测结果
设计结果图最终如下:
窗体以及各组件的主要属性设置
ObjectName 类名称 属性设置 备注
MainWindow QMainWindow Width=1201
Height=630 整个窗体
label QLabel Test=“原图” 设置标签显示文字
label_2 QLabel Test=“检测结果” 设置标签显示文字
scrollArea QScrollArea Width=511
Height=371 框住图片显示的位置
scrollArea_2 QScrollArea Width=511
Height=371 框住图片显示的位置
LabPicture QmyFigureCanvas
(QWidget) Width=451
Height=331 matplotlib绘图区域
picture QmyCanvas
(QWidget) Width=451
Height=331 matplotlib绘图区域
btnOpen QPushButton Text=“打开图片” 打开图片按钮
btnJian QPushButton Text=“检测” 控制检测的按钮
jieguo QTextEdit Width=511
Height=87 显示检测结果的标签和置信度
表中的LabPicture和picture的类名称为QWidget,然后用组件提升的方法,将这个QWidget分别提升为QmyFigureCanvas和QmyCanvas类(下面说明提升方法)。
窗体设计完成后,将这个窗体保存为文件MainWindow.ui,窗体文件MainWindow.ui文件实际上是一个XML文件,它记录了窗体上各组件的属性以及位置分布。MainWindow.ui的XML文件内容不必去深入研究,它是由UI Designer根据可视化设计的窗体自动生成的。
八.将ui文件编译为py文件
使用UI Designer设计好窗口并保存为文件MainWindow.ui后,要在Python里使用这个窗体,需要将这个ui文件编译转化为对应的Python语言程序文件。上面我们在pycharm已经配置好这个外部工具,在pycharm中右击MainWindow.ui选择External Tools的PyUIC即可生成MainWindow.py。
打开MainWindow.py,分析这个文件的代码,可以发现这个文件实际上定义了一个类Ui_MainWindow
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
(1)Ui_MainWindow类的父类是object,而不是QWidget
(2)UI_MainWindow类定义了一个函数setupUi(),其传入的参数有两个,其中self是函数自己,MainWindow是一个传入的参数,而不是Ui_MainWindow定义的变量。
setupUi()函数前两行语句是:
MainWindow.setObjectName(“MainWindow”)
MainWindow.resize(1201, 630)
MainWindow是窗体,是一个QWidget对象,其名字就是在UI Designer里设计的窗体的objectName。但是这个MainWindow不是在类UI_MainWindow里创建的,而是作为一个参数传入的。
(3)MainWindow.py文件里的类名称Ui_MainWindow与MainWindow.ui文件里窗体的objectName有关,是在窗体的objectName名称前面加“Ui_”自动生成的
(4)Ui_MainWindow类的函数setupUi()用于窗体的初始化,它创建了窗体上的所有组件并设置其属性
(5)Ui_MainWindow类并不创建窗体MainWindow,窗体MainWindow时外部传入的,作为所有界面组件的父容器。MainWindow.py文件只是定义了一个类Ui_MainWindow,这个文件并不能直接运行,而是需要在其他地方编程使用这个文件里定义的类Ui_MainWindow。
九.界面与逻辑分离的GUI程序框架
界面与业务逻辑分离的设计方法不是唯一的,这里介绍单继承方法定义一个类QmyMainWindow,并保存为为文件myMainWindow.py,其代码如下
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from MainWindow import Ui_MainWindow
class QmyMainWindow(QMainWindow):
def init(self, parent=None):
super().init(parent)#调用父类构造函数,创建QMainWindow窗体
self.ui = Ui_MainWindow()#创建Ui对象
self.ui.setupUi(self)#构造Ui
if name == “main”: # 用于当前窗体测试
app = QApplication(sys.argv) # 创建GUI应用程序
form = QmyMainWindow() # 创建窗体
form.show()
sys.exit(app.exec_())
(1)新定义的窗体业务逻辑类QmyMainWindow只有一个基类QMainWindow
(2)在QmyMainWindow的构造函数中,首先调用父类(也就是QMainWindow)的构造函数,这样self就是一个QMainWindow对象
(3)显式地创建了一个Ui_MainWindow类的私有属性self.ui,包含了可视化设计的UI窗体上的所有组件,所以,只有通过self.ui才可以访问窗体上的组件,包括调用其创建界面组件的setupUi()函数。
(4)app是创建的应用程序对象,最后执行的app.exec_()开启了应用程序的事件处理循环。应用程序会对事件队列中排队的事件进行处理,还可以对相同事件进行合并处理。
(5)现在运行程序myMainWindow程序就会出现我们所设计的窗体,点击按钮并没有任何功能
十.组件信号与内建槽函数的关联
Qt的界面组件都是从QWidget继承而来的,都支持信号与槽的功能。每个类都有一些内建的信号与槽函数,例如QPushButton按钮类常用的信号是clicked(),在按钮被单击时发射此信号。下面为窗体上的“打开图片”按钮编写槽函数,首先要找到应该使用该按钮的那个信号。在Qt Creator 中打开QtApp项目,在打开窗体MainWindow.ui,选中“打开图片”按钮,点击右键调出其快捷菜单,在菜单中点击“Go to solt…”菜单项,会打开如图所示的Go to slot对话框
这个对话框显示的是QPushButton类及其所有父类的信号。按钮最常用的信号是clicked(),就是点击按钮时发射的信号。在图中选择clicked()信号,然后点击“OK”按钮,这样会在QtApp项目的MainWindow.cpp文件里生成如图的C++槽函数框架
我们不需要编写任何C++程序,而只需要自动生成的这个槽函数的名称,复制此函数名称,在myMainWindow.py文件的QmyMainWindow类里定义一个同名的函数并编写代码
def on_btnOpen_clicked(self):
在该函数编写函数,当点击按钮时就会执行该函数。
同样的给检测按钮编写槽函数的定义为
def on_btnJian_clicked(self):
我们点击打开图片按钮需要完成的功能是读取本地文件的图片,PyQt5为应用程序设计提供一些常用的标准对话框,这里用打开文件对话框QFileDialog,若要打开一个文件,调用类函数QFileDialog.getOpenFileName(parent:QWidget=None,caption:str=‘’,directory:str=‘’,filter:str=‘’,initialFilter:str=‘’,options:Union[QFileDialog.Options,QFileDialog.Option]=0)->Tuple[str,str]所有输入参数都有默认值,这些参数的意义分别如下:
.对话框的父类器parent:一般设置为调用对话框的窗体对象,也可以设置为None
.对话框标题caption:这里设置为“选择一个图片”
.初始目录directory:打开对话框时的初始目录,这里用类函数QDir.currentPath()获取当前目录
.文件过滤器filter:设置选择不同后缀的文件,可以设置多组文件,如:filt=“所有文件(.);;文本文件(.txt);;图片文件(.jpg*.gif*.png)”每组文件之间用两个分号隔开,同一组文件内不同后缀之间用空格隔开。
.初始的文件过滤器initialFilter:设置初始的文件过滤器,如“文本文件(.txt)”
.对话框选项options:时枚举类型QFileDialog.Option的取值的组合
getOpenFileName0函数返回结果是一个含有两个元素的Tuple数据,在程序中用两个变量分别获得两部分的数据:
def on_btnOpen_clicked(self): # 打开图片按钮
curPath = QDir.currentPath()
dlgTitle = “选择一个图片”
filt = \”所有文件(.);;文本文件(.txt);;图片文件(*.jpg)\”
self.filename, filtUsed = QFileDialog.getOpenFileName(self, dlgTitle, curPath, \"Images(*.jpg)\")
filename是选择文件的带路径文件名,filtUsed是使用的文件过滤器字符串。现在运行myMainWindow.py文件,点击“打开图片”按钮,就可以打开一个对话框,可以选择一个图片,并把路径赋值给filename。