PyQt5实战——翻译器的UI页面设计以及代码实现(七)
个人博客:苏三有春的博客
系类往期文章:
PyQt5实战——多脚本集合包,前言与环境配置(一)
PyQt5实战——多脚本集合包,UI以及工程布局(二)
PyQt5实战——多脚本集合包,程序入口QMainWindow(三)
PyQt5实战——操作台打印重定向,主界面以及stacklayout使用(四)
PyQt5实战——UTF-8编码器UI页面设计以及按钮连接(五)
PyQt5实战——UTF-8编码器功能的实现(六)
前言
上期文章我们讲了如何去实现一个UTF-8编码的转换器的构造以及代码实现。本期我们实现脚本集合包的第二个脚本(功能):翻译器。目前,该翻译一起支持中译英,英译中,或自动检测翻译。翻译功能的实现本质上是通过爬虫技术去爬取网页实现的翻译功能,因此翻译功能需要联网才可进行。因此,在设计之初需要注意的是(虽然笔者并不是在设计之初就意识到会有这么多问题):如何爬取一个网站,如何将需要翻译的内容上传给网站,是否需要判断当前状态是否联网,如何判断是否联网。如果处于断网状态如何处理…接下来两到三篇文章会详细讲述这些问题如何解决。
翻译器的UI布局
首先先看翻译器的UI布局
可以看到这个翻译器的主要框架分为上中下三个部分,垂直分布,因此,主布局是一个垂直布局,三个部分分别是:翻译语言选择,翻译文本框,快速翻译三个部分。
- 翻译语言选择:该部分由从左到右三个部分组成,水平布局,第一个下拉列表可选取待翻译语言的语种,包括:
自动检测
、中文
、English
三种,中间的组件是一个带有图标的按钮,可以转换左右下拉列表的语种,右边的下拉列表可选取翻译后的语种,包括:English
、中文
。 - 翻译文本框:该部分由从左到右三个部分组成,水平布局,第一个文本框是可读写文本框,可输入待翻译的内容,中间是一个按钮,点击按钮则开始翻译,右边文本框是一个只读文本框,在点击翻译按钮后,若翻译成功则会在该文本框中显示翻译后的内容。
- 快速翻译:快速翻译一个ListView,列表组件,其中存储了一个字典,显示对应的key,点击后会自动访问对应key的value,将value显示出来,无需联网,用于快速反应一些常用常翻译的内容。
代码解释
创建布局对象
根据上述UI的设计,我们可以创建对应的layout对象,并将子布局一次添加进主布局,主布局设置为widget的布局
# create a horizontal layout
layout = QVBoxLayout() # 主布局
langlayout = QHBoxLayout() # 翻译语言选择的布局
hlayout = QHBoxLayout() # 翻译文本框的布局
listlayout = QVBoxLayout() # 快速翻译列表的布局
layout.addLayout(langlayout) # 将三个子布局添加进主布局
layout.addLayout(hlayout) # 将三个子布局添加进主布局
layout.addLayout(listlayout) # 将三个子布局添加进主布局
self.setLayout(layout) # 将layout设置为该翻译器widget的布局
翻译语言的选择与切换UI设计
该部分由两个comboBox(下拉列表)和一个PushButton(按钮)组成
# lang layout
self.langfromcb = QComboBox(self) # 创建输入语言的下拉列表
self.langfromcb.addItem("自动检测") # 往下拉列表中添加元素
self.langfromcb.addItem("中文")
self.langfromcb.addItem("English")
self.langfromcb.setCurrentIndex(1) # 将下拉列表中的第二个元素(0为第一个)设置为默认选项
self.langtocb = QComboBox(self) # 创建输出语言的下拉列表
self.langtocb.addItems(['中文','English']) # 往下拉列表中添加元素
self.langtocb.setCurrentIndex(1) # 将下拉列表中的第二个元素设置为默认选项
self.exchangebtn = QPushButton(self) # 创建交换语种的按钮
pixmap = QPixmap("_internal/res/img/exchange.ico") # 将图标添加进变量
self.exchangebtn.setIcon(QIcon(pixmap)) # 为按钮设置图标
self.exchangebtn.setIconSize(self.exchangebtn.sizeHint()) # 设置图标为自适应按钮大小
self.exchangebtn.setToolTip("交换语言") # 为按钮设置提示泡泡
self.exchangebtn.clicked.connect(self.tranasexchange) # 给按钮点击状态连接事件方法,该方法将调转输入与输出语种
langlayout.addWidget(self.langfromcb) # 将三个组件都加入到第一个子布局中
langlayout.addWidget(self.exchangebtn)
langlayout.addWidget(self.langtocb)
在这一段代码值得注意的是:
-
这段代码展示了之前文章未出现的组件,即下拉列表(ComboBox)。
-
下拉列表有两种添加元素的方法,
addItem()
与addItems()
,一个参数为单个元素,一个参数为list
,可同时输入多个元素。 -
setCurrentIndex()
方法可设置下拉列表的首选项,比如:下拉列表的顺序为:自动检测,中文,English,我想让中文成为默认选项,则可使用setCurrentIndex(1)
来使得中文成为默认选项,因为python是从0开始算起的。 -
按钮可以添加图标,这个图标的地址开头是
__internal
,为什么使用这个作为最高级目录,请看往期内容PyQt5实战——多脚本集合包,程序入口QMainWindow(三)第三节初始化应用程序部分,在窗口标签部分讲过相关内容。 -
按键的提示泡泡长这样,当鼠标放在按钮上,会弹出一个小框描述该按键的功能。
翻译文本框的UI设计
该部分是由两个文本框与一个按键组成
# input editor
self.editor = QTextEdit(self) # 创建输入文本框
self.editor.setPlaceholderText("翻译内容") # 设置文本框的背景内容
hlayout.addWidget(self.editor) # 将输入文本框添加到子布局中
TextEditStyle(self.editor) # 修改文本框的样式,作者创建的方法,非第三方库调用
# translation button
self.button = QPushButton("翻译",self) # 创建翻译按钮
btnReleaseStyleA(self.button) # 修改按钮的样式,作者创建的方法,非第三方库调用
self.button.clicked.connect(self.translating) # 按钮点击状态连接到translating事件方法
hlayout.addWidget(self.button) # 将按钮添加到子布局中
# output edit
self.textedit = QTextEdit(self) # 创建输出文本框
self.textedit.setPlaceholderText("翻译结果") # 设置文本框的背景内容
self.textedit.setReadOnly(True) # 设置文本框为只读
hlayout.addWidget(self.textedit) # 将文本框添加到子布局中
TextEditStyle(self.textedit) # 修改文本框的样式,作者创建的方法,非第三方库调用
这段代码并没有什么需要特殊注意的地方,在之前的文章中我们已经用过多次了
快速翻译列表UI设计
# list layout
self.workdist = {'快进':'forward','后退':'backward','暂停':'pause','停止':'stop',
'播放':'play'}
self.model = QStandardItemModel(0, 1) # 创建一个模型类,0行1列,即没有元素
self.view = QListView(self) # 设置一个ListView对象
self.view.setModel(self.model) # 设置ListView对象的模型为上面创建的模型
for i in self.workdist.keys(): # 做一个for循环,循环从workdist中读取key
item = QStandardItem(i) # 按顺序,将key设置为模型元素对象
self.model.appendRow(item) # 将对象添加到模型中
ListViewStyle(self.view) # 修改ListView的样式,作者创建的方法,非第三方库调用
listlayout.addWidget(self.view) # 将ListView对象添加到子布局中
self.view.clicked.connect(self.modelselected) # 将listview的点击状态连接到事件方法modelselected
这里新知识比较多,尤其是出现了一个陌生的控件QListView
与一个模型类QStandardItemModel
-
QStandardItemModel
的定义在 PyQt 中,
QStandardItemModel
是一个常用的模型类,它提供了一种标准的方式来存储和管理数据,尤其是用于与视图类(如QListView
、QTreeView
和QTableView
)配合使用。QStandardItemModel
是一种基于项(Item)模型的数据结构,它通过QStandardItem
类来表示每个数据项。QStandardItemModel
主要特点:- 基于项的模型:它使用
QStandardItem
来表示数据的每一项,QStandardItem
是存储数据和管理项状态的对象。 - 支持树形和表格数据:通过
QStandardItemModel
可以管理数据,支持表格和树形结构。它可以通过层次结构来组织数据。 - 与视图(View)绑定:
QStandardItemModel
与 PyQt 中的视图类(如QListView
、QTreeView
、QTableView
)一起使用,模型控制数据,而视图负责数据的显示和交互。
- 基于项的模型:它使用
-
QListView控件与QComboBox功能相似,但是用途,行为和样式差别较大,直接问大模型会给你长长的一条差别,但在这里,使用QListView的原因是它好看,可自定义样式,且不会收束成一个单个元素,而是完整地展示所有元素。
布局代码解释完了,接下来我们看一下实现交互功能的代码:
tranasexchange
def tranasexchange(self):
cba = self.langfromcb.currentText()
cbb = self.langtocb.currentText()
if cba == "自动检测":
cba = "English"
self.langfromcb.setCurrentText(cbb)
self.langtocb.setCurrentText(cba)
这个代码逻辑很简单实际上就是一个获取两个QComboBox的当前选择内容,然后交换它,因为langto
并没有自动检测
这一选项,所以当langfrom
选择的是自动检测
,则切换成English
。
translating
def translating(self):
print(self.langfromcb.currentText()," to ",self.langtocb.currentText())
text = self.editor.toPlainText()
tran = Translation.translating(text,self.langfromcb.currentText(),self.langtocb.currentText())
if tran == "website is not reachable":
MsgClass().show_HTTP_error("Website is not reachable")
else:
self.textedit.setText(tran)
- 首先将从什么语种翻译到什么语种打印到log面板上
text
变量获取当前输入文本框中的文本内容- 将
text
中的文本内容,当前选择的两个语种,一共3个参数传入Translation对象的translating方法中,该方法才是真正实现翻译功能的方法,该方法会返回一个字符串,tran
获取这个字符串 - 该
if
的其实就是回应我们开头提出的问题:“需不需要判断网络问题”以及“如果网络异常该怎么办”,在一开始,笔者并没有处理这个异常,直到有一次笔者挂了clash访问GitHub时,无法访问翻译网站,结果导致程序闪退,所以,必须要判断网络问题,才做了这个if
判断来抛出异常,使脚本在遇到网络问题时依然能够正常运行。 - MsgClass是笔者写的一个消息处理类,主要功能是获取文本,制作一个弹窗,弹窗内容就是获取到的文本,在这里是
Website is not reachable
- 如果网络没有问题,则不会将获取的文本输出到输出文本框中
- 当然这个网络异常逻辑设计并不完美,因为有可能翻译的内容恰恰是
website is not reachable
,这样也会导致UI这一部分误以为是网络异常,从而弹窗警告。更优解时返回两个变量,一个变量是翻译内容,一个变量是网站访问情况。if
检测网站访问情况,如果非200,如404,则抛出弹窗警告。
modelselected
def modelselected(self):
print(self.view.currentIndex().data(),'to',self.workdist[self.view.currentIndex().data()])
self.editor.setText(self.view.currentIndex().data())
self.textedit.setText(self.workdist[self.view.currentIndex().data()])
如果是点击快速翻译中的某一项,则无需点击翻译按钮,直接完成翻译
- 在log面板上打印当前选择的元素以及元素所对应的value(元素为字典中的key)
- 将元素设置为输入文本框的内容
- 将元素对应的value设置为输出文本框的内容
本文要点
- 翻译器的主要UI布局其实是模仿目前市面上大多数的翻译器设计的,主要是模仿微软翻译,即:搜索 Microsoft Translator - 从英语翻译到中文 (简体)。其实,该翻译器的实现,主要也是爬取了微软翻译,翻译器模仿成用户,向微软翻译发送翻译文本,并获取返回的response,拿到翻译后的结果。之所以不爬取有道翻译,是因为有道翻译对爬虫限制很高,现在甚至无法打开开发者工具(如果在有道翻译页面强行打开开发者工具,会导致闪退该页面)。
- 在这个页面的UI设计中,我们加入了许多新玩意儿,比如
ListView
,模型类QStandardItemModel
,QComboBox
,等等有趣的东西,想使用但却没有实际例子模仿的小伙伴可以借鉴参考。 - 在本文中我们回答了开头的五个问题中的两个,“是否需要判断当前状态是否联网”答案是:需要判断当前状态是否联网,如果不加以判断,当处于断网时,爬虫无法正常爬取网页,会抛出Error或Exception,如果处理,则会导致程序闪退。
- “如果处于断网状态如何处理”,答案是,当判断出断网状态时,会抛出弹窗警告,弹窗警告的设计是笔者自做,后续系列更新会介绍。