前言
经过上一章,我们构建了二维码生成器的基本界面,本章将实现界面中各个控件的功能。
一、工具箱的数据输入
创建一个 getQR()
方法用于生成二维码,每次生成二维码时需要对传入的参数进行验证。
def getQR(self): # 生成二维码
data_flag = self.toolBox.currentIndex() # 数据类型(所选栏目)
data = ''
if data_flag == 0: # URL
data = self.lineEdit_url.text()
elif data_flag == 1: # TEXT
data = self.textEdit.toPlainText()
elif data_flag == 2: # CARD
name = self.lineEdit_name.text()
tel = self.lineEdit_tel.text()
qq = self.lineEdit_qq.text()
mail = self.lineEdit_mail.text()
company = self.lineEdit_company.text()
website = self.lineEdit_website.text()
data = f'MECARD:N:{name};ORG:{company};TEL:{tel};EMAIL:{mail};URL:{website};NOTE:QQ :{qq};' # MECARD格式的电子名片
color = self.color_now # 颜色
imagepath = self.lineEdit_filepath.text() # 图片路径
depth = float(self.slider_depth.value())/100 # 透明度
filetype = imagepath[imagepath.rfind('.'):].lower() # 文件后缀小写
path = imagepath[:imagepath.rfind('.')] + filetype # 重置路径
# 执行生成二维码 ------
try:
pass
except:
pass
首先,通过 data_flag
变量来确定用户当前选择了工具箱中的哪一个抽屉,即选择输入哪种数据。接着,根据用户选择,单行文本框通过 text()
方法,多行文本框通过 toPlainText()
方法获取用户输入值。
data_flag = self.toolBox.currentIndex() # 数据类型(所选栏目)
data = ''
if data_flag == 0: # URL
data = self.lineEdit_url.text()
elif data_flag == 1: # TEXT
data = self.textEdit.toPlainText()
需要注意的是,当用户选择 CARD 一栏时,笔者采用的是 MECARD 格式的电子名片,其为日本的 docomo 公司制定的一种数据格式,是互联网中一种规范的文件传播格式,它的主要作用是转化传统纸质商业名片上的信息。
elif data_flag == 2: # CARD
name = self.lineEdit_name.text()
tel = self.lineEdit_tel.text()
qq = self.lineEdit_qq.text()
mail = self.lineEdit_mail.text()
company = self.lineEdit_company.text()
website = self.lineEdit_website.text()
data = f'MECARD:N:{name};ORG:{company};TEL:{tel};EMAIL:{mail};URL:{website};NOTE:QQ :{qq};' # MECARD格式的电子名片
手机扫码后效果如下:
然后,获取颜色、图片路径、透明度等参数。self.color_now
为自行添加的类成员变量,用于保存用户当前选择的颜色,默认值为 ‘#000000’ ,即黑色。而生成带背景图的二维码采用的是 MyQR 库,通过调整亮度和对比度可以改变图片的透明度,其所需参数为 float 类型,故用 depth
变量保存用户选择的透明度。最后再对读入图像的路径做一下调整即可。
color = self.color_now # 颜色
depth = float(self.slider_depth.value())/100 # 透明度
imagepath = self.lineEdit_filepath.text() # 图片路径
filetype = imagepath[imagepath.rfind('.'):].lower() # 文件后缀小写
path = imagepath[:imagepath.rfind('.')] + filetype # 重置路径
如下样例图,根据用户的选择,可以生成 logo 和背景图两种模式的二维码。生成二维码的具体方式将在后续的章节中详细说明。
二、颜色选项卡
点击各个颜色按钮时,需要实现 3 个动作。首先设置当前用户选择的颜色(修改self.color_now
的值),接着将单选按钮设为选中 logo 按钮(因选择背景图时前景色限制为黑色),最后调用 getQR()
方法。注意:本次采用的是按钮的 pressed 信号而非 clicked 信号,区别在于前者为按下,后者为按下并抬起。
for n in range (0,8): # 颜色按钮被点击时更改颜色代码, 同时修改单项按钮为logo项,并执行生成 ->
getattr(self, 'pb_%s'%n).pressed.connect(lambda c = n : self.setColor(c))
getattr(self, 'pb_%s' % n).pressed.connect(self.change2logo)
getattr(self, 'pb_%s'%n).pressed.connect(self.getQR)
下面来看两个槽函数, setColor()
为带参数的方法,接收按钮的序号,即颜色列表的索引, 之后在颜色代码文本框中显示当前颜色代码。
def setColor(self,c): # 设置默认颜色按钮
self.color_now = self.color_list[c]
self.lineEdit_color_code.setText(self.color_now)
change2logo()
方法将 logo 的单选按钮设为选中。
def change2logo(self): # 选中logo项
self.radioButton_0.setChecked(True)
除了使用预设的颜色,用户也可以选择输入其他颜色代码,所以给 Enter 按钮绑定一个槽函数,手动修改自定义的颜色。
def set_myColor(self): # 手动设置颜色代码
if self.lineEdit_color_code.text() != "":
self.color_now = self.lineEdit_color_code.text()
self.getQR()
不要忘记这里也需要再选中 logo 的单选按钮。目的是当用户选择背景图模式时,能通过选择颜色直接切换为 logo 模式,无需再通过单选按钮。
self.pb_color_enter.clicked.connect(self.change2logo)
self.pb_color_enter.clicked.connect(self.set_myColor) # 点击Enter按钮设置颜色,同时修改单项按钮为logo项,并并执行生成
三、图像选项卡
生成二维码所需的图像参数为图像的路径,利用 QFileDialog.getOpenFileName()
方法即可获取文件路径,之后再将路径显示在路径文本框中即可,限定仅可选择 png、jpg、gif 三种格式的图片。
def openFile(self): # 打开图片文件
filepath, filetype = QFileDialog.getOpenFileName(self, self.tr('Choose image'), './', '(*.png);;(*.jpg);;(*.gif)')
self.lineEdit_filepath.setText(filepath)
当选择背景图模式时,可以对背景图的透明度进行调整。笔者采用滑块的方式来调整,先设置滑块的默认值为100,即正常图像。然后添加刻度以便观察。因为不需要太精确的微调,故设置步长为10,即每次通过点击进行的改动值为10(拖动仍可微调)。
self.slider_depth.setValue(100) # 滑块默认值100
self.slider_depth.setTickPosition(QSlider.TicksAbove) # 设置刻度的位置,刻度在下方
self.slider_depth.setTickInterval(20) # 设置刻度的间隔
self.slider_depth.setSingleStep(10)
在拖动滑块的同时,希望在旁边的标签能够显示透明度的值,所以为值变化信号绑定一个槽函数。
self.slider_depth.valueChanged.connect(self.setDepth)
setDepth()
方法首先获取滑块的值,再将其转化为百分比的格式,嵌入到描述文本中。
def setDepth(self): # 显示图片透明度
d = self.slider_depth.value()
self.label_depth.setText(' Depth:' + ' '*(3-len(str(d))) + '%s%%'%d)
如此,便实现了选项卡中基本的参数设置需求。
四、菜单栏
菜单栏中有4项功能,分别为其触发信号绑定各自的槽函数。
self.actionSave_as.triggered.connect(self.saveAs) # 菜单栏-另存为
self.actionAbout.triggered.connect(self.about) # 菜单栏-关于
self.actionChinese.triggered.connect(self.trans_cn) # 菜单栏-汉化
self.actionEnglish.triggered.connect(self.trans_en) # 菜单栏-英化
另存为:生成的二维码默认文件名为“ test_qr ”,保存在根目录,故另存为的时候从根目录读入图像,再按用户指定路径保存即可。
def saveAs(self): # 另存为
try:
img = Image.open('test_qr.png')
filepath, filetype = QFileDialog.getSaveFileName(self, self.tr("Save QRcode image as"),'test_qr.png', '(*.png);;(*.gif)')
img.save(filepath)
except:
QMessageBox.about(self,self.tr('Remind'),self.tr('No new QR code generated !'))
关于:自定义一个 Dialog 窗体,弹窗对象需使用 exec()
方法等待响应。
def about(self): # 关于窗口
d = QDialog()
d.setWindowTitle(self.tr('About'))
d.resize(110,70)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(QtWidgets.QLabel(self.tr('This tool is created by @ Seon_ Pan\nUsed to generate custom QR code\nLearn and reference only')))
d.setLayout(hbox)
d.show()
d.exec()
英汉互译的部分将在之后的国际化章节详细说明。
五、状态栏
状态栏显示信息效果如下:
为测试状态栏的功能,本次笔者仅利用状态栏显示4段信息,分别是点击工具箱的4个抽屉时的提示说明。先为工具箱的选项变化信号绑定一个槽函数。
self.toolBox.currentChanged.connect(self.show_status) # 选择不同栏目时在状态栏显示说明信息
选择不同的的抽屉栏时,在状态栏中显示对应的信息。
def show_status(self):
data_flag = self.toolBox.currentIndex() # 数据类型(所选栏目)
if data_flag == 0: # URL
msg = self.tr(' ---> Explain: Input the link address according to the format')
elif data_flag == 1: # TEXT
msg = self.tr(' ---> Explain: Input unformatted text')
elif data_flag == 2: # CARD
msg = self.tr(' ---> Explain: Electronic business card in MECARD format')
elif data_flag == 3: # HTML
msg = self.tr(' ---> Explain: The function has not been realized yet...')
self.statusbar.showMessage(msg,4600)
六、回顾 __init__()
在进入核心功能代码之前,再来回顾一下界面设置,和各类控件的动作信号绑定槽函数的情况。
def __init__(self):
super(MaForm, self).__init__()
self.setupUi(self)
# 图标 ------------------------------------------------------------
self.toolBox.setItemIcon(0,QIcon(":resource/url.ico"))
self.toolBox.setItemIcon(1, QIcon(":resource/text.ico"))
self.toolBox.setItemIcon(2, QIcon(":resource/card.ico"))
self.toolBox.setItemIcon(3, QIcon(":resource/html.ico"))
self.pb_clean_text.setIcon(QIcon(":resource/x.ico"))
self.pb_clean_text.setStyleSheet("border:none;")
# 参数 ------------------------------------------------------------
self.color_list = ['#000000', '#DC143C', '#FFA500', '#FFD700', '#90EE90',
'#40E0D0', '#00BFFF','#BA55D3'] # 颜色列表
self.color_now = '#000000' # 默认为黑色
self.edit_list = ['lineEdit_url', 'lineEdit_name', 'lineEdit_tel', 'lineEdit_qq', 'lineEdit_mail',
'lineEdit_company', 'lineEdit_website', 'lineEdit_color_code', 'lineEdit_filepath',
] # 文本框列表
# 界面控件设置 ----------------------------------------------------------
palette = QPalette()
palette.setColor(QPalette.Window, Qt.white)
self.label_result.setAutoFillBackground(True)
self.label_result.setPalette(palette) # 设置结果图区域的label为白色背景
self.slider_depth.setValue(100) # 滑块默认值100
self.slider_depth.setTickPosition(QSlider.TicksAbove) # 设置刻度的位置,刻度在下方
self.slider_depth.setSingleStep(10)
self.slider_depth.setTickInterval(20) # 设置刻度的间隔
for n in range (0,len(self.edit_list)): # 设置文本框清空提示,多行文本无该功能
getattr(self, self.edit_list[n]).setClearButtonEnabled(True)
for n in range (0,len(self.color_list)): # 设置按钮颜色
getattr(self, 'pb_%s'%n).setStyleSheet("background-color: " + self.color_list[n])
# 连接槽函数 -----------------------------------------------------------
self.pb_clean_text.clicked.connect(self.clean_words) # 清空多行文本
self.textEdit.textChanged.connect(self.show_words) # 显示字数
self.pb_openfile.clicked.connect(self.openFile) # 打开图片
self.slider_depth.valueChanged.connect(self.setDepth) # 更改透明度
self.pb_color_enter.clicked.connect(self.change2logo)
self.pb_color_enter.clicked.connect(self.set_myColor) # 点击Enter按钮设置颜色,同时修改单选按钮为logo项,并执行生成 ->
for n in range (0,8): # 颜色按钮被点击时更改颜色代码, 同时修改单项按钮为logo项,并执行生成 ->
getattr(self, 'pb_%s'%n).pressed.connect(lambda c = n : self.setColor(c))
getattr(self, 'pb_%s' % n).pressed.connect(self.change2logo)
getattr(self, 'pb_%s'%n).pressed.connect(self.getQR)
self.actionSave_as.triggered.connect(self.saveAs) # 菜单栏-另存为
self.actionAbout.triggered.connect(self.about) # 菜单栏-关于
self.actionChinese.triggered.connect(self.trans_cn) # 菜单栏-汉化
self.actionEnglish.triggered.connect(self.trans_en) # 菜单栏-英化
self.toolBox.currentChanged.connect(self.show_status) # 选择不同栏目时在状态栏显示说明信息
可以注意到,目前在颜色选项卡中点击颜色按钮和 Enter 按钮时会执行 getQR()
方法生成二维码,还有哪些情况需要执行呢?需要尽可能地简化用户操作,即每当用户想对二维码的样式或内容进行修改时,就执行生成二维码的操作,无需将【生成】这一动作单独拿出来,它应该结合到每一次【修改】动作中。
因此,笔者设置每一个文本框被修改时,就执行生成。但由于多行文本输入内容造成的修改较为频繁,每次执行间隔时间太短,可能会造成界面卡顿,故为其单独设置一个生成按钮,在用户确定完成输入后,再点击生成。此外,在释放滑块的时候执行生成(注意不是在拖动的时候执行),是因为拖动时值的变化更为频繁,也会造成界面卡顿。当然界面卡顿的问题,还可以靠多线程解决。
然后在 logo 和 背景图单选按钮选择改变时,也执行生成以便在两种图像模式切换时快速得到结果。
self.slider_depth.sliderReleased.connect(self.getQR) # 释放滑块时执行生成 ->
self.radioButton_0.toggled.connect(self.getQR) # 单选按钮切换时执行生成 ->
self.pb_getQR.clicked.connect(self.getQR) # 多行文本框下方的按钮
for i in range(0, len(self.edit_list)): # 文本框被修改时执行生成 ->
getattr(self, self.edit_list[i]).textChanged.connect(self.getQR)
七、本章小结
第二章我们实现了各个控件的基本功能,例如数据输入、选择颜色、打开图像、菜单栏动作、状态栏信息等内容。下一章将继续讲解定制化二维码,包括修改二维码颜色、添加 logo 图片或背景图片、调节背景图透明度等操作,敬请关注!