1、概述
最近做一个模块的开发,刚开始参数是在主函数修改,后来项目需要有一个用户交互界面,方便调试,于是在网上学习了python Qt图形界面的开发(Python Qt 图形界面编程,很实用也很详细的视频课程),顺利完成项目需求,为此写一篇使用教程,供大家参考。
先看一个自己以租车比价做的例子。有一款名为IGO的租车平台,它有分时用车、2小时套餐、4小时套餐、日租套餐等,每个都有包含的公里数和小时数(分时用车除外),有时候自己租车会计算比价,就想着自己做一个软件出来方便选择套餐,其中的参数均可以手动修改,部分参数有默认值即平台设置的值。界面及操作演示如下:
本文主要演示如何使用自己设计的图像界面,例如如何使用多线程打印输出以及不使用多线程会有什么问题,还有就是如何将自己的代码和用户交互、如何弹出提示信息,如何发布自己的程序。
2、相关库安装
pip install pyside2
如果安装过程较慢,可以参考:通过国内镜像下载python库
安装好以后后,在所在python的库安装目录中就有Qt designer的启动程序。
新建一个widget:可以设计自己的界面,具体模块可以参考上述视频教程,这里给出即将要演示的已经做好的demo界面,主要是有一个输出文本框(lineEdit)、展示文本框(textBrowser)、两个命令按钮(pushButton),界面如下:
将设计模块保存后(.ui文件,本文保存为:demo.ui,demo ui链接, 提取码:5188 )即可利用PySide2中的相关模块进行动态加载。
3、示例代码
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QWidget, QApplication, QMessageBox
from PySide2.QtCore import Signal, QObject
from threading import Thread
import time
class MySignals(QObject):
# 定义一种信号,参数为字符串
text_print = Signal(str)
class Stats(QWidget):
def __init__(self):
super().__init__()
self.ui = QUiLoader().load('demo.ui')
# 实例化
self.ms = MySignals()
# 自定义信号的处理函数
self.ms.text_print.connect(self.printToGui)
self.ui.pushButton.clicked.connect(self.print_info_multithread)
self.ui.pushButton_2.clicked.connect(self.output_msg)
def printToGui(self, text):
"""
向文本展示框中添加输出信息
"""
self.ui.textBrowser.append(str(text))
def output_msg(self):
"""
演示弹出对话框信息
"""
QMessageBox.information(self, "提示", "参数已保存!")
def print_info_multithread(self):
"""
演示利用多线程打印输出信息
"""
def run():
# 通过Signal 的 emit 触发执行 主线程里面的处理函数
# emit参数和定义Signal的数量、类型必须一致
input = int(self.ui.lineEdit.text()) # 演示用户交互接口
for i in range(input):
self.ms.text_print.emit('输出内容_{}'.format(i))
time.sleep(1)
thread = Thread(target=run)
thread.start()
def print_info_no_multithread(self):
"""
演示不使用多线程打印输出信息
"""
for i in range(5):
self.ui.textBrowser.append('输出内容_{}'.format(i))
time.sleep(1)
app = QApplication()
stats = Stats()
stats.ui.show()
app.exec_()
4、演示结果
(1)多线程的打印输出演示:
(2)不使用多线程输出演示:
即在class Stats的初始化中:
self.ui.pushButton.clicked.connect(self.print_info_multithread)
更改为:
self.ui.pushButton.clicked.connect(self.print_info_no_multithread)
上述两者的区别是,当点击输出时,利用多线程打印展示框时同步输出的,而不使用双线程打印,文本展示框的是阻塞的。
自己设计界面肯定是用多线程的方法,这里主要展示有可能遇到的错误(自己刚开始也没搞通这个,因为看视频理解错误了)。
5、程序发布
当我们的界面和程序代码完成后,就可以完成发布了,生成.exe供别人使用。手续需要安装程序打包的python库——pyinstaller:
pip install pyinstaller
安装完成后进入到,自己程序所在目录,例如运行的python文件为Qt_designer_demo.py:
pyinstaller Qt_designer_demo.py --noconsole --hidden-import PySide2.QtXml
最后在所在文件目录中的dist文件夹下,会得到一个包含Qt_designer_demo.exe的Qt_designer_demo文件,记得要把demo.ui放到Qt_designer_demo文件夹中,否则加载不了.ui文件,双击exe程序可以看到跟直接运行代码效果一致。
至此,一个基本的Qt 图像交互界面就完成了,过程也不是很麻烦,自己可以通过Qt designer设计适合自己的界面。
6、.ui转化成python文件
可能有时候我们不想用动态加载.ui的方法,因为我们每次发布程序后都需要把.ui文件手动移到pyinstaller 生成的文件夹下。这是我们可以利用pyuic5命令将我们生成的.ui文件转化成python文件,然后再调用即可,转化命令为:
pyuic5 -o demo_ui.py demo.ui
这是我们得到一个demo_ui.py文件,里面是一个包含设计界面的class:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'multithread_print.ui'
# Created by: PyQt5 UI code generator 5.15.0
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(445, 307)
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
'''此处代码较长,省略部分代码'''
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Qt演示"))
self.label.setText(_translate("Form", "请输入你的数字:"))
self.pushButton.setText(_translate("Form", "输出"))
self.pushButton_2.setText(_translate("Form", "保存"))
为了保持与之前的使用PySide2模块的代码基本一致(PyQt5与PySide2的模块架构基本类似),我们把demo_ui.py中的:
from PyQt5 import QtCore, QtGui, QtWidgets
更改为:
from PySide2 import QtCore, QtGui, QtWidgets
我们再对原来的运行程序进行修改即可,我们这里我们做了以下更改:
(1)增加模块导入:from demo_ui import Ui_Form
(2)class Stats(QWidget)更改为:class Stats(QWidget, Ui_Form)
(3)初始化中增加:self.setupUi(self)
(4)将相关的self.ui.xxx中的.ui去掉,例如:self.ui.textBrowser.append(str(text))更改为:self.textBrowser.append(str(text))
具体修改后的代码如下,运行后与动态加载.ui文件方法一致
from PySide2.QtCore import Signal, QObject
from PySide2.QtWidgets import QWidget, QApplication, QMessageBox
from threading import Thread
from demo_ui import Ui_Form
import time
class MySignals(QObject):
# 定义一种信号,参数为字符串
text_print = Signal(str)
class Stats(QWidget, Ui_Form):
def __init__(self):
super(Stats,self).__init__()
self.setupUi(self)
self.ms = MySignals()
# # 自定义信号的处理函数
self.ms.text_print.connect(self.printToGui)
self.pushButton.clicked.connect(self.print_info_multithread)
self.pushButton_2.clicked.connect(self.output_msg)
def printToGui(self, text):
"""
向文本展示框中添加输出信息
"""
self.textBrowser.append(str(text))
def output_msg(self):
"""
演示弹出对话框信息
"""
QMessageBox.information(self, "提示", "参数已保存!")
def print_info_multithread(self):
"""
演示利用多线程打印输出信息
"""
def run():
# 通过Signal 的 emit 触发执行 主线程里面的处理函数
# emit参数和定义Signal的数量、类型必须一致
input = int(self.lineEdit.text()) # 演示用户交互接口
for i in range(input):
self.ms.text_print.emit('输出内容_{}'.format(i))
time.sleep(1)
thread = Thread(target=run)
thread.start()
app = QApplication()
stats = Stats()
stats.show()
app.exec_()