接着上一篇对 PyQt5
项目:武汉大学建筑知识系统的总体概述之后,接下来介绍软件的开发流程和我的一些理解,虽然不能让同学们开发出很牛的软件,但是希望能帮助同学们快速的开发一款能够让老师看得上的一款软件(毕竟在结课展示的时候其他组的软件都被老师痛批)。下面将分几篇文章来展示本软件从初代到最终版本的迭代历程!
从接到这个项目后,用了一个星期的空余时间了解了 PyQt5
这个基于 Python
的 Qt
框架,当时我看的是【《PyQt5 快速开发与实战》王硕 孙洋洋 著】这本书。同时也了解了如何在软件 Qt designer
(下简称 designer
) 里面构建我们的 UI
界面,关于软件 designer
如何下载获取、配置以及使用,同学们可以去 CSDN
或者 B站
搜索学习,目前这方面的内容还是很多的。那么如何把 PyQt5
用起来,去开发一个项目,我看的是屏信佳(B站用户)的一个实践视频。通过这一个星期的学习,我开始开发软件的第一个版本(同学们可以在网上找找版本的命名方法,一开始我也不是很清楚,所以版本号都是随便就升一个等级,可以看到下面图片的版本号只有2位)。
下面给出版本1.0.0的界面和搭建思路:
作为一款能让用户上传图片并进行预测的软件,大家应该不难想到一个最基本的功能就是要设计一个东西能让用户上传图片吧,那么在 PyQt5
里面是怎么实现呢?很简单,我们的文件是不是都存放在文件资源管理器(磁盘)里面,那么只需要通过一个 pushButton
按钮 + PyQt5
的 PyQt5.QtWidgets.QFileDialog.getOpenFileName
就可以完成,QFileDialog.getOpenFileName
就是 PyQt5
提供给程序员用于打开用户文件资源管理器的一个接口。
说到这里,同学们可能有疑惑了, pushButton
按钮在哪里呢?QFileDialog.getOpenFileName
这个接口又要如何使用呢?那么相信你肯定没有完成前置步骤(学习 PyQt5
这个框架以及了解软件 designer
的使用)!
下面简单介绍一下,但是还是希望同学们先去了解相关的知识点再来看本次的项目分享(学习框架 PyQt5
可以看书,图书馆有很多这种书籍,网上也有很多教程;而开发软件可以先在软件 designer
里面把左侧提供的控件全部拖出来试一遍、看一遍,这样了解更深,这也是我之前忽略的一点,后续会继续强调这点)!我们打开软件 designer
选择创建窗体 Widget
。
在左侧可以看到很多控件,一个个拖出来了解一遍,它将是你开发软件时提供解决方法的一个重要步骤!!!
那么上图画红框的就是 pushButton
按钮啦~
在 PyQt5
里面最重要的一点就是要知道信号和槽机制了,这是一个控件通过一些操作能够发生反应的东西,简单来说就是:你要 pushButton
按钮点击之后出现别的功能,那么就要用到这个概念。当然不要被这个名词搞懵了,其实在 PyQt5
里面就是一个语句的事情,即(下面我用的是“森林背词”里面的一个例子,因为在“武汉大学建筑知识系统”我是通过另一种方法实现的)。
方法一:通过 connect
进行功能函数连接
self.ui.btn_show_answer.clicked.connect(self.showAnswer)
# self.ui 是我的整个UI对象
# btn_show_answer 是我的一个按钮名字
# clicked.connect 表示这个被点击了,同时发出了信号,这个信号开始通过槽机制调用后面的括号里面的函数(自己定义的一个功能函数)
# self.showAnswer 则是一个方法(功能函数),在“森林背词”里面用于显示答案的
方法二:通过 PyQt5
提供的 pyqtSlot
来进行功能函数连接
@pyqtSlot()
def on_btn_show_answer_clicked(self):
......
# 在定义一个功能函数前加上 @pyqtSlot()
# 然后用 def on_按钮名称_clicked(self): 来实现 showAnswer 这个功能
这个方法是 B站
的屏信佳提到的,主要用于 pushButton
按钮(其它的控件好像也能用,但是我没全部试过,应该是对于 Buttons
里面的按钮都能用),当你不知道怎么给一个函数命名或者纠结函数叫什么名字的时候(选择困难症用户),用这个可以帮助你抛去这些烦恼,你只要知道点击这个按钮就会实现相应的功能就可以了。但是我建议同学们还是用第一种方法!因为当你开发大型项目的时候,可能会跨文件交互(比如一个类调用另一个类——父类、子类),这个时候方法二可能会失效,同时比如你要实现点击用户头像跳转到用户设置界面,这是就只能用方法一了,所以方法二有一定的局限性!
那么知道了一个按钮的使用,怎么打开用户的文件资源管理器呢?使用 QFileDialog.getOpenFileName
,注意因为我们只要用户上传一张图片来进行识别,所以选这个接口,当然还有可以选多个文件或者选文件夹的接口,同学们可以自行查阅了解。下面是打开文件的代码示例:
filename, _ = QFileDialog.getOpenFileName(self, '打开文件', QDir.currentPath())
# filename 和 _ 见下方图片的解释,即 filename 是用户选择的文件绝对路径,_ 是用户可选择的文件类型
# '打开文件' 是指打开用户文件资源管理器时左上角的名称(见下图左上角)
# QDir.currentPath() 是指当前程序主文件所在的路径(当前程序所在的文件夹路径)
那么具体的实现代码同学们可以去我们已经开源的代码里面查看(武汉大学建筑知识系统源代码),这里就不再展示具体代码了,只给同学们介绍思路!
接下来是不是就会想到要保存我们用户上传的图片呀,于是我们可以通过 QImageReader
来获取这个图片,再用 read
方法读取图片,最后用 save
方法保存图片,是不是看的有点懵,下面给出对应代码:
image_reader = QImageReader(filename)
image_reader.setAutoTransform(True) # 启用自动旋转
image = image_reader.read()
image.save('.\\predict.jpg', "JPG", 100) # 第一个参数是保存后图片叫什么名字,第二个参数是保存图片的后缀(不一定要和用户上传图片一样的后缀),第三个参数则是要保存图片的质量(数值越高,画质越高)
这时候同学们看懂了吗?filename
就是我们上面第一步打开文件时返回的一个参数哦,即用户上传的图片绝对路径。我们只有知道用户上传的图片在用户电脑的哪个位置,我们才能将用户上传的图片保存下来!这里相信同学们看到上面多了一句启用自动旋转的语句吧,这是因为不加这句话,那么上传的竖屏图片会被保存为横屏图片!这个方法我当时也是试了很多次才发现的。
来到这里,有没有同学想到一个潜在的 bug
问题呢?看到这里先闭眼想 10
秒,现在有什么问题呢?
.
.
.
.
.
.
.
.
.
.
.
答案揭晓:之前提到 QImageReader
是不是只是读取图片,但是它不会明显的飞出一个弹窗告诉你读取的是否成功,它只会给你返回一个地址!那么如果一个很皮的用户或者粗心的用户不上传图片文件,而是不小心(故意)上传一个 py
文件,那么这是不是会导致后续在预测过程中出现 bug
(因为我们预测的时候要读取这张照片,然后用神经网络来预测嘛)!
所以我们要在用户上传文件之后去判断用户上传的文件是不是图片文件,如果不是,那么我们就要通过反馈告诉用户要上传图片文件吧,那么怎么判断这个文件是不是图片呢?然后又如何告知用户呢?
答案是用 isNull
方法,其实 QImageReader
是返回一个地址的,当 read
读取失败时,这个地址内容是 null
。那么如何进行提示呢,那就是用消息框,了解了 PyQt5
小伙伴们肯定知道这个东西了,在 PyQt5
中有很多消息框可供我们选择,我采用的是 QMessageBox
中的 information
来实现,综合起来就是:
if image.isNull():
QMessageBox.information(self, '打开图片', '不能加载文件%s.\n请打开图片文件!如后缀为.jpg .png的文件。' % filename)
return
好的,那么如何打开图片以及保存图片讲解到此结束!
想想还是给同学们一个简单的代码示例吧,我们先创建一个 .py
文件,就叫 ui.py
吧,这个文件是我们的 UI
界面代码,是通过在软件 designer
搭建好界面,然后通过命令 pyuic5
来将 ui
文件转为 py
文件,具体转换可以自行搜索,这里也提供转换命令供同学们参考。
pyuic5 ui.ui -o ui.py
# ui.ui 是我的用软件 designer 搭建的 UI 界面文件
# ui.py 是指将 ui 文件转为一个名叫 ui.py 的 py 文件
# 注意:如果 ui 文件或者要转换的 py 文件的路径不是当前路径,则需加上相对路径进行转换哦~
创建好 ui.py
后,输入以下代码:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(278, 64)
self.gridLayout = QtWidgets.QGridLayout(Form)
self.gridLayout.setObjectName("gridLayout")
self.btn_openimage = QtWidgets.QPushButton(Form)
self.btn_openimage.setObjectName("btn_openimage")
self.gridLayout.addWidget(self.btn_openimage, 0, 0, 1, 1)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.btn_openimage.setText(_translate("Form", "打开图片"))
方法一:(connect
方法)然后再创建一个 py
文件,叫做 main.py
,然后输入以下代码:
import os
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QFileDialog, QMessageBox
from PyQt5.QtCore import pyqtSlot, QDir
from PyQt5.QtGui import QImageReader
from ui import Ui_Form
class QmyWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent) # 调用父类构造函数,创建窗体
self.ui = Ui_Form() # 创建UI对象
self.ui.setupUi(self) # 构造UI界面
self.setWindowTitle("武汉大学建筑知识系统") # 设置程序窗口名称
self.ui.btn_openimage.clicked.connect(self.openImage)
# 在识别界面打开图片
def openImage(self):
filename, _ = QFileDialog.getOpenFileName(self, '打开文件', QDir.currentPath())
if filename:
image_reader = QImageReader(filename)
image_reader.setAutoTransform(True) # 启用自动旋转
image = image_reader.read()
if image.isNull():
QMessageBox.information(self, '打开图片', '不能加载文件%s.\n请打开图片文件!如后缀为.jpg .png的文件。' % filename)
return
filePath = os.getcwd()
print(f'预测图片已保存({filePath}' + '\\predict.jpg)')
image.save('.\\predict.jpg', "JPG", 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = QmyWidget()
w.show()
sys.exit(app.exec_())
方法二:(pyqtSlot
方法)然后再创建一个 py
文件,叫做 main2.py
,然后输入以下代码:
import os
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QFileDialog, QMessageBox
from PyQt5.QtCore import pyqtSlot, QDir
from PyQt5.QtGui import QImageReader
from ui import Ui_Form
class QmyWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent) # 调用父类构造函数,创建窗体
self.ui = Ui_Form() # 创建UI对象
self.ui.setupUi(self) # 构造UI界面
self.setWindowTitle("武汉大学建筑知识系统") # 设置程序窗口名称
# 在识别界面打开图片
@pyqtSlot()
def on_btn_openimage_clicked(self):
filename, _ = QFileDialog.getOpenFileName(self, '打开文件', QDir.currentPath())
if filename:
image_reader = QImageReader(filename)
image_reader.setAutoTransform(True) # 启用自动旋转
image = image_reader.read()
print(image_reader)
print(image)
if image.isNull():
QMessageBox.information(self, '打开图片', '不能加载文件%s.\n请打开图片文件!如后缀为.jpg .png的文件。' % filename)
return
filePath = os.getcwd()
print(f'预测图片已保存({filePath}' + '\\predict.jpg)')
image.save('.\\predict.jpg', "JPG", 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = QmyWidget()
w.show()
sys.exit(app.exec_())
这时 main.py
所在的文件夹是不是出现了一个名为 predict.jpg
图片咧,这就是你刚刚上传的图片啦~
你再试试上传别的图片,是不是图片 predict.jpg
也改变了,这样我们之后就可以通过指定路径来读取图片,从而进行预测啦!
下一篇博客将会介绍识别出用户的图片后将要干些什么?显然是显示建筑的相关信息吧,那我们下篇博客见~
上一篇文章传送门:【PyQt5 实战项目1】武汉大学建筑知识系统–思路分享1(总体概述)
下一篇文章传送门:【PyQt5 实战项目1】武汉大学建筑知识系统–思路分享3(软件版本1.0.0介绍之显示预测的建筑信息、图片)