上篇文章介绍了如何优化浏览界面以及为我们的控件添加QSS样式。下面这篇文章将会介绍之前留的一个坑——导航,同时还会介绍多线程的实际用法,以及对于之前没有提到的一些优化与小知识点进行分享。
在“武汉大学建筑知识系统”中,我们有两处地方用到了导航,分别是识别界面和浏览界面,两个界面的导航其实是有一些区别的,同学们可以先自己想想两者有哪些区别,答案会在后面揭晓。
那么对于导航操作,其实我当时是想到 3
种方法:
(1)自己开发一个导航系统,通过我们小组实地考察学校路径,与水利水电学院进行合作来共同开发武汉大学导航系统。但是仔细想想,要真正开发好这个系统,一个月的时间肯定是不够的,其次使用我们系统的用户也不一定是从武汉大学的某个地方出发,那么我们的这个导航系统就有局限性!所以这个方案直接 pass
。
(2)使用现在主流的导航软件接口,比如百度地图、高德地图等,通过申请获取使用的 API
接口,从而实现文字导航,这也是其他组展示时使用导航的一种方法。但是他们的导航只有文字(比如直行200米左转之类的),没有 2D
或 3D
视图导航,而且没有实时 GPS
导航,直接显示从起点到终点的行走路线,我觉得这种方法对于用户来说很不方便,尤其是第一次来武汉大学的游客!于是我用的就是第三种方法。
(3)使用目的地在地图软件中的链接网址,直接打开用户浏览器跳转到用户要去的目的地的导航目标。相当于我们帮助用户直接在地图软件中搜索好要去的目的地,用户可以直接选择交通方式,如何去目的地。现在这么说可能大家不是很明白,下面给出图片说明。
而我们要做的是这样的,用户点击我们的导航按钮直接进入目的地链接,不用用户手动输入目的地:
那么如何实现这个方法呢?是不是首先要收集我们数据库中所有建筑在百度地图中的相应 url
地址,所以这是一项需要劳动力的方法。不过它能让用户有更好的体验感啊~我们既然都用了别人的 API
了,而且又只有文字导航,那为何我们不直接彻底白嫖,直接使用这些地图公司的网页版本呢!
大家应该都知道 url
是什么吧,在用 python
爬虫的时候相信大家都用过,不过这里还是简单介绍一下怎么获取这个 url
吧。其实 url
就是一个网页页面的网址,下面以 Microsoft Edge
浏览器为例介绍如何获取百度地图的 url
。
方法一:大家可以打开百度地图,然后按下键盘上的 F12
(笔记本可能是 Fn
+ F12
),选择打开开发工具:
选择第一个文件标头就可以找到 url
了:
获取完地址并存入我们的数据库后,那就是要考虑怎么在代码中使用这些 url
啦,那么在 PyQt5
提供了打开外部浏览器和打开 PyQt5
提供的浏览器窗口来显示网页,为了方便我选择直接打开用户浏览器,那么使用代码示例如下:
from PyQt5.QtGui import QDesktopServices
_url = QUrl(url) # 获取 url 对象
QDesktopServices.openUrl(_url) # 通过此 url 打开用户浏览器搜索此 url 并显示
那么我们的导航功能就完成了!
那么现在回答文章一开始提出的问题,即识别界面和浏览界面的导航有什么区别?
(1)识别界面
在识别界面中,一开始是没有建筑信息的,所以我们的导航按钮要设置不可用,或者是设置 url
为空。
self.ui.btn_navigate_identify.setVisible(False) # 直接隐藏按钮
或者self.ui.btn_navigate_identify.setEnabled(False) # 按钮看得见,但是无法点击
url
,初始值为空,那么 QDesktopServices.openUrl(_url)
操作就不会打开任何网页啦~(2)浏览界面
在浏览界面中,url
则应该默认为我们的第一个选项,即——“枫园食堂”。
所以这是我们需要注意的一个地方!
在之前我们是不是提到预测图片是要放在神经网络中进行前向计算来得到预测结果。因为一开始我也不清楚我们的预测时间要多久,而在我们开发的软件中,如果采用单线程来运行的话,那么 CPU
的所有资源都会用于模型预测,此时我们的界面就会出现无响应情况,即宕机。那么怎么解决这种情况呢?
答案当然是采用多线程了,主线程来运行我们的 UI 界面,子线程来执行那些复杂耗时的操作。那么怎么用代码实现呢?下面给出示例代码:
我们需要先定义一个多线程类:
class WorkThread(QThread):
trigger = pyqtSignal()
def __int__(self):
super(WorkThread, self).__init__()
def run(self):
print('开始预测!')
# 接入模型预测
global pre_name
pre_name = pre()
# 循环完毕后发出信号
print(f'预测结束,预测建筑为: {pre_name}')
self.trigger.emit()
然后在我们的业务代码中这样使用:
# 在识别界面打开图片
@pyqtSlot()
def on_btn_openimage_identify_clicked(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}' + '\\images\\predict\\predict.jpg)')
image.save('.\\images\\predict\\predict.jpg', "JPG", 100)
# 更新用户上传图片
self.ui.label_userimage.setPixmap(QPixmap("ui\\../images/predict/predict.jpg"))
# 更新预测图片
# self.ui.label_pre.setPixmap(QPixmap("ui\\../images/background/loading.png"))
self.movie = QMovie("./images/background/loading.gif")
self.ui.label_pre.setMaximumSize(QSize(124, 124))
self.ui.label_pre.setMovie(self.movie)
self.movie.start()
# 删除导航网址
global url
url = ''
# 显示文字 --> 预测中...
_translate = QCoreApplication.translate
self.ui.pre_text.setHtml(_translate("Form",
"\n"
"\n"
"预测中,请稍后...
"))
# 开启子线程完成图片预测
print("开启子线程")
self.workThread = WorkThread()
self.work()
def work(self):
# 子线程开始
self.workThread.start()
# 当获得循环完毕的信号时,停止计数
self.workThread.trigger.connect(self.timeStop)
def timeStop(self):
print(f'更改图片: {pre_name}\n')
# 运行数据库查找功能(通过建筑名找它的图片位置 以及 导航网址 以及 建筑信息)
# --------------------------------------
global content
content = gainContent_name_pre(pre_name)
# content = gainContent_name_pre(pre_name[:-1])
# --------------------------------------
# 更改图片
self.ui.label_pre.setPixmap(QPixmap(f"./images/buildings/{content[1]}.jpg"))
self.ui.label_pre.setMaximumSize(QSize(711, 400))
# 更新导航网址
global url
url = content[6]
# 更新预测建筑的信息
self.ui.pre_text.setText("\n"
f"建筑名:{content[2]}
\n"
f"类 别:{content[3]}
\n"
"简 介:
\n"
f" {content[4]}
\n"
f"地 址:{content[5]}
")
这样就实现多线程了。
注:其实前向计算还是很快的,所以此处要不要用多线程全凭大家自己决定,但是在开发过程中,我个人认为还是要思考的更远一些。不过很多程序员基本上都只实现老板要求的功能,这确实是挺方便的,因为工作量会大大减少,不过对于我们自己写的小项目,我觉得还是要深远考虑,这对你的编程思维会有质的提升!
到这里,我们的“武汉大学建筑知识系统”以及差不多完工啦,接下来就是收尾工作(美化)。
在识别界面,用户上传图片进行预测是不是会有一小段的等待时间(尽管前向计算很快,但是始终会有一段时间),这时我们是不是可以对界面做一些改变,即用户上传图片后,我们有一个加载的动画过程反馈给用户,让用户知道我们在进行预测,这样是不是用户的使用感会更好!效果如下:
这样加载过程中有个 loading
是不是更符合我们的认知~
除了之前提到的一些可优化的地方给到同学们练习,这里再给出几个我认为可以继续优化的地方给同学们自行练习!
左侧菜单栏
在我们的源代码中不难看出我们的左侧菜单栏是一个个 pushButton
组成的,大家可以尝试使用之前我在浏览界面介绍的滑动框来实现。
导航页面嵌入
我们在导航时是打开外部网页,这样的交互性对用户不是很友好,大家可以尝试将地图嵌入到我们的系统里面,这样就无需打开很多的用户浏览器窗口。
联网判定
在导航时,因为我们需要访问外部网页,这就要消耗用户流量,所以在软件使用前,可以使用弹窗询问用户是否愿意消耗流量使用导航功能;或者在导航旁边加上一个 checkBox
来让用户选择是否联网。
那么本篇文章到此结束啦!下一篇文章将会总体介绍我们的最后一个版本 1.5.1
,这个版本我将会从头到尾进行介绍,即将前面的所有内容组合到一起,让大家有个更直观的认识!
那我们下一篇文章见啦~
上一篇文章传送门:【PyQt5 实战项目1】武汉大学建筑知识系统–思路分享6(优化浏览界面以及为我们的控件添加QSS样式)
下一篇文章传送门:【PyQt5 实战项目1】武汉大学建筑知识系统–思路分享8(版本1.5.1——收官之作)