Qt Designer极速开发python桌面小工具详解
原创 测试小酋 51STE软件测试部落 昨天
今天来讲下如何使用Qt Designer设计UI,快速实现pyhon桌面小工具的开发,且UI与逻辑分离。
我的环境如下:
Windows 7
Python 3.7.1
PyCharm PROFESSIONAL 2019.3 (你可以用各种同类IDE替代或者安装它)
PyQt5
Qt Designer
本文并不讨论Python和PyCharm的安装,关于PyCharm具体安装可以参见文章《2020最新Pycharm四步破解教程(永久破解)》(再次提醒,PyCharm版本需要与文中版本一致,否则可能导致破解失败)
一、安装PyQt5
下面直接使用pip来安装PyQt5,此处可能是pip/pip3,或者两者皆可,后面不再重复
1、直接pip安装PyQt5
pip install PyQt5
2、由于Qt Designer已经在Python3.5版本从PyQt5转移到了tools,因此我们还需要安装pyqt5-tools
pip install pyqt5-tools
3、到这一步,PyQt5就安装完成了,你可以通过下面来检查是否已经安装成功:
在cmd中输入pyuic5,如果返回“Error: one input ui-file must be specified”说明安装成功。
二、初识Qt Designer
根据参考文章:在win10中可以用Win+S呼出Cornata主面板(搜索框)来启动各种应用,那么这里就是在搜索框中输入designer并敲回车,就能够启动Qt Designer了。
但我的系统为win7,并不能这样操作,最后只有在python安装路径下搜索“designer.exe”
最终路径为 :
[python安装目录]/Lib/site-packages/qt5_applications/Qt/bin
双击启动。初次启动会弹出这个“新建窗体”窗口,一般来说默认选择“Main Window”然后点击“创建”就可以了。下方有个“启动时显示这个对话框”的勾选框,如果不想每次启动都看到这个“新建窗体”窗口,可以取消勾选。
创建“Main Window”之后,我们会看到如下画面:
下面就来简单介绍下整个画面的构成:
左侧的“Widget Box”就是各种可以自由拖动的组件;
中间的“MainWindow - untitled”窗体就是画布;
右上方的"对象查看器"可以查看当前ui的结构;
右侧中部的"属性编辑器"可以设置当前选中组件的属性;
右下方的"资源浏览器"可以添加各种素材,比如图片,背景等等,目前可以不管;
大致了解了每个板块之后,就可以正式开始编写第一个UI了。
三、绘制一个简单的汇率转换器UI
1、添加输入框
在左侧的“Widget Box”栏目中找到“Input Widgets”分类,将“Line Edit”拖拽到屏幕中间的“MainWindow”画布上,你就获得了一个单行输入框,如下图所示。
2、添加文本
在左侧的“Widget Box”栏目中找到“Display Widgets”分类,将“Label”拖拽到“Line Edit”旁边,你就获得了一个仅用于显示文字的文本框,如下图所示。
双击上图中的“TextLabel”,就可以对文本进行编辑,这里我们将其改成美元缩写“USD”,如下图所示。如果文字没有完全展示出来,可以自行拖拽空间改变尺寸。
特别提醒:编辑完文本之后记得敲击回车令其生效!
3、添加按钮
使用同样的方法添加一个按钮(PushButton)并将其显示的文本改成“转换”,如下图所示。
4、按照上面1、2步骤,完善汇率UI,如下图所示。
5、修改窗口标题
下面修改窗口标题。选中右上方的"Object Inspector"中的“MainWindow”,然后在右侧中部的"属性编辑器"中找到“windowTitle”这个属性,在Value这一栏进行修改(改为“汇率换算器”),修改完记得敲击回车。
6、编辑菜单栏
注意到画布的左上方有个“在这里输入”(Type Here),双击它即可开始编辑菜单栏。菜单栏支持创建多级菜单以及分割线(separator)。我随意创建了一些菜单项目,如下图所示。
7、预览
使用快捷键Ctrl+R预览当前编写的GUI(或者从菜单栏的Form > Preview / Preview in进入)
此时你尝试去缩放窗口,会发现组件并不会自适应缩放,因此我们需要回到Qt Designer中进行一些额外的设置。
8、组件自适应
点击画布空白处,然后在上方工具栏找到 下图四种布局,在本例中我们使用栅格布局(grid layout),即第三种。
我们再次使用Ctrl+R预览,这次组件可以自适应了!
9、保存
如果觉得完成了,那就可以保存成*.ui的文件(快捷键:Ctrl+S),这里我们保存为conversion.ui。为了方便演示,我将文件保存到D盘新建目录QT下。
10、生成Python代码
使用cmd将目录切到目录 D:/QT 并执行下面的命令。请自行将下面命令中的name替换成文件名,比如本例中的“conversion.ui”
pyuic5 -o conversion.py conversion.ui
生成的代码应该类似下图所示
11、运行Python代码
此时尝试运行刚刚生成的“conversion.py”是没用的,因为生成的文件并没有程序入口。因此我们在同一个目录下另外创建一个程序叫做“main.py”,并输入如下内容。在本例中,gui_file_name就是conversion,请自行替换。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import gui_file_name
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = gui_file_name.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
然后运行“main.py”,你就能看到刚刚编写的GUI了!
四、实现汇率换算逻辑
刚刚写的汇率换算器中,我们设置的按钮(PushButton)是没有实际作用的,因为我们并没有告诉这个按钮应该做什么。实际上,要让这个按钮做点什么只需要增加一行代码就可以了。
1、获取按钮id
打开conversion.ui,在designer中选中对应的按钮,从“属性编辑器”中可以得知这个按钮的“objectName”叫做“pushButton”,如下图所示。
2、设置触发
QT中有“信号和槽(signal and slot)”这个概念,不过目前无需深究,也无需在Designer中去设置对应按钮的“信号和槽”,直接在“main.py”中“MainWindow.show()”的后面加入下面这样的一行代码
ui.pushButton.clicked.connect(click_success)
下面简单解释下这行代码:
pushButton就是刚刚获取的按钮id;
clicked就是信号,因为是点击,所以我们这里用clicked;
click_success就是对应要调用的槽,注意这里函数并不写成click_success();
3、设置函数
既然刚刚设置了按钮的触发并绑定了一个函数click_success,我们就要在“main.py”中实现它。示例如下
def click_success(): print("搞定了!")
4、运行!
UI跟逻辑分离的好处就在这里,我们这次不用去管“conversion.py”了,直接运行修改完的“main.py”。点击按钮,这次你会发现在控制台中有了我们预设的输出。
5、回到正题,我们这里需要通过UI传参实现汇率的转换
对于传参,有两种解决方案,一种是使用lambda,还有一种是使用functool.partial。在接下来的环节中我们会使用partial。
partial的用法如下所示:
partial(function, arg1, arg2, ......)
既然使用partial传参,那么我们就要在程序(main.py)的头部加上下面这行。
from functools import partial
然后我们把步骤2的按钮触发那行代码修改成下面这样。
ui.pushButton.clicked.connect(partial(convert, ui))
6、编写convert函数
首先,我们要获取用户输入的数字。为了使得教程简洁易懂,我们这次只讲解单向的汇率转换。既然是单向的转换,那么我们只需要获取左侧的文本框id。在本例中,左侧的文本框id为lineEdit。如果你对此感到一头雾水,请停下并回头复习。
获取文本使用的是text()方法,因此读取用户输入的代码如下
input = ui.lineEdit.text()
接着我们进行汇率转换,注意这里要进行类型转换
result = float(input) * 6.5344
最后我们让右边的文本框显示结果
ui.lineEdit_2.setText(str(result))
下面是main.py的完整代码
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from functools import partial
import conversion
def convert(ui):
input = ui.lineEdit.text()
result = float(input) * 6.5344
ui.lineEdit_2.setText(str(result))
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = conversion.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
ui.pushButton.clicked.connect(partial(convert, ui))
sys.exit(app.exec_())
一个简单的汇率转换器就这样诞生了!
那么,如何知道一个组件都有什么方法呢?直接去Qt官方文档(https://doc.qt.io/archives/qt-5.13/qtgui-module.html)查看就可以了。本节使用到的lineEdit的相关方法在这里(https://doc.qt.io/archives/qt-5.13/qtextedit.html)
五、线程执行逻辑
当我们实现复杂的逻辑,处理时间较长时,此时界面就会出现“未响应”。如我实现一个对网站生成sitemap的工具,点击按钮运行后出现卡死,如下图所示。
1、问题原因
在GUI程序中,主线程也叫GUI线程,因为它是唯一被允许执行GUI相关操作的线程。对于一些耗时的操作,如果放在主线程中,就是出现界面无法响应的问题。
在编写QT的界面程序时,当我们调用QApplication.exec()时,我们就启动了QT的事件循环。在开始的时候,QT会发出一些事件来显示和绘制窗口部件。在这之后,事件循环就开始运行,不断地检查是不是有事件发生并且把这些事件发送给应用程序中的QObject。
当一个事件被处理时,其他事件也可能会产生并且追加到QT的事件队列中。如果我们在处理一个特定的事件上耗费过多的时间,用户界面就会变得不能够响应。
2、解决思路
解决该问题的方法有很多,如使用QTimer不断应答的,这里我还是喜欢以线程的方式来处理。
如把类似按钮触发代码:
ui.pushButton.clicked.connect(partial(sitemapgo, ui))
改为:
thread = RunThread(ui) # 创建一个线程ui.pushButton.clicked.connect(lambda: thread.start())
3、实现处理线程
1)首先加入QtCore包的引用
from PyQt5 import QtCorefrom PyQt5.QtCore import *
2)把原来的sitemapgo函数处理逻辑填入run中进行简单的改造,具体怎么改造,相信有python基础的朋友来说都不难。如果没有python基础,建议学习下runoob.com中的基础教程。
具体线程类实现代码如下:
class RunThread(QThread):
# python3,pyqt5与之前的版本有些不一样
# 通过类成员对象定义信号对象
# _signal = pyqtSignal(str)
trigger = pyqtSignal()
def __init__(self, ui):
super(RunThread, self).__init__()
self.ui = ui
def __del__(self):
self.wait()
def run(self):
#线程处理逻辑
try:
…… #填入网站生成sitemap的逻辑
except:
print("出现异常错误!")
finally:
self.trigger.emit()
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = sitedog.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
thread = RunThread(ui) # 创建一个线程
ui.pushButton.clicked.connect(lambda: thread.start())
sys.exit(app.exec_())
4、最后再来运行工具,就再也不会出现“未响应”错误了。
六、实现控制台显示
我们发现程序的输出都是在控制台打印的,非常不方便,这是该怎么输出到前端textBrowser中呢?这里我们修改gui_file_name.py文件(gui_file_name是什么?一头雾水请再复习下前面)
1、首先引入sys
import sys
2、定义发送信号槽
class EmittingStr(QtCore.QObject): textWritten = QtCore.pyqtSignal(str) #定义一个发送str的信号 def write(self, text): self.textWritten.emit(str(text))
3、在类Ui_MainWindow中开始处添加
class Ui_MainWindow(object):
def __init__(self):
# 下面将输出重定向到textBrowser中
sys.stdout = EmittingStr(textWritten=self.outputWritten)
sys.stderr = EmittingStr(textWritten=self.outputWritten)
def outputWritten(self, text):
cursor = self.textBrowser.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.textBrowser.setTextCursor(cursor)
self.textBrowser.ensureCursorVisible()
4、再来运行上面的sitemap生成工具,控制台没有再打印输出,都在textBrowser中显示了。
到了这一步,我们的PyQT5工具开发就基本告一段落了。怎么样,很简单吧?
参考文章:
https://blog.csdn.net/azuremouse/article/details/90338961
https://www.cnblogs.com/hhh5460/p/5175322.html
https://blog.csdn.net/weixin_38308549/article/details/105200032
http://www.45fan.com/article.php?aid=19062535244902109151948616
-- End --
文末寄语: 用自己的努力换取成功,然后成功就会像一个大巴掌,打在那些曾经看不起你的人脸上,要多响有多响,要多爽有多爽。