一,前言
当pyQT的主线程界面需要处理耗时的操作容易卡住时,可以考虑用多进程,此时使用多线程还是有可能卡住,但是多进程肯定不会把界面卡住(前提是电脑的内存够用。电脑内存够用时,多线程还是可能会卡住,但是多进程就不会,这就是多进程的优点。)
为什么不直接在pyQT里面使用普通的多进程呢?因为pyQT里面有专门的多进程模块,即QProcess。
使用QProcess一共就俩步骤:
- 创建进程:
process = QtCore.QProcess()
- 启动进程:
process.start('cmd.exe', ['dir'])
其中第一个参数是外部程序,后面的列表是给该程序的参数。
二,简单调用.py文件
我们用QProcess来调用.py文件,或者说在.py文件里面写一个子进程完成耗时操作,然后用QProcess调用这个.py文件,让这个.py文件协助我们进行一些耗时操作。可以这样写:
self.process = QProcess()#创建进程
args = []#QProcess调用参数列表,参数可以不止一个,但是第一参数必须是py文件的路径.所有参数都应是字符串形式的,否则可能会报错
process_path = r'C:\237端点\软件\python\pycharm工作路径\试验区\test.py'#这是被调用的py文件的绝对路径
args.append(process_path)
# Qprocess的start函数有两个参数,第一个参数是外部程序,第二个参数是扔给这个外部程序的参数
self.process.start("python", args)
self.process.finished.connect(self.finished)#将子进程结束之后会调用的函数设定为self.finished
上面是关键代码,下面来看具体效果:
首先在同一目录下有两个文件,分别是:process_example1.py和test.py。其中前一个文件有pyQT的图形界面(QProcess将在这里被创建),test是被它调用的py文件,在test里面可以做耗时的操作,它相当于开启了一个子进程。
process_example1.py的代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'process_example1.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication, QMainWindow
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(150, 80, 300, 23))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "点击该按钮将开启进程,进程运行结果将在控制台输出"))
# 以上代码是自动生成的,以下代码是自己手写的
self.process = None # 多进程
self.pushButton.clicked.connect(self.show_msg)#绑定点击事件函数
self.msg = ''
def handle_stderr(self): # 处理报错信息的函数
if self.process is not None:
data = self.process.readAllStandardError()
stderr = bytes(data).decode("utf8") # 字符串格式的报错信息
self.msg += stderr
print(self.msg)
def handle_stdout(self): # 处理正常输出信息的函数
if self.process is not None:
data = self.process.readAllStandardOutput()
stdout = bytes(data).decode("utf8") # 字符串形式的输出信息
self.msg += stdout
print(self.msg)
def finished(self):
print('这是进程结束后会调用的方法!可以自己设置,这部分内容不是由子进程执行的,子进程一结束就会执行这个函数!')
def show_msg(self):
self.process = QProcess()#创建进程
if self.process is not None:
self.process.readyReadStandardOutput.connect(self.handle_stdout)#绑定正常信息输出函数
self.process.readyReadStandardError.connect(self.handle_stderr)#绑定错误信息输出函数
args = []#QProcess调用参数列表,参数可以不止一个,但是第一参数必须是py文件的路径.所有参数都应是字符串形式的,否则可能会报错
process_path = r'C:\237端点\软件\python\pycharm工作路径\试验区\test.py'#这是调用的py文件的绝对路径
args.append(process_path)
# Qprocess的start函数有两个参数,第一个参数是外部程序,第二个参数是扔给这个外部程序的参数
self.process.start("python", args)
self.process.finished.connect(self.finished)#子进程结束之后会调用的函数,对应的还有一个进程开始时能绑定的函数
def show_UI():
# 实例化,传参
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
# 创建对象
mainWindow = QMainWindow()
# 创建ui,引用Ui_MainWindow类
ui = Ui_MainWindow()
# ui.sig1.connect(lambda :close_main_window(ui.sig1, mainWindow))
# 调用Ui_MainWindow类的setupUi,创建初始组件
ui.setupUi(mainWindow)
# 创建窗口
mainWindow.show()
# 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)
sys.exit(app.exec_())
if __name__ == '__main__':
show_UI()
test.py的代码:
if __name__ == '__main__':
print('子进程被调用了,这是在子进程的内部!')#在这里可以写处理耗时操作的代码
调用效果:
在上面的界面点击了按钮之后,控制台输出如下:
三,QProcess通信
1,从被调用的.py文件发消息给QProcess
从被调用的.py文件发消息给QProcess参考以上代码的这两个函数:
其中,被调用的.py文件中print语句输出的信息会被第二个函数接收,错误信息会被第一个函数接收
def handle_stderr(self): # 处理报错信息的函数
if self.process is not None:
data = self.process.readAllStandardError()
stderr = bytes(data).decode("utf8") # 字符串格式的报错信息
self.msg += stderr
print(self.msg)
def handle_stdout(self): # 处理正常输出信息的函数
if self.process is not None:
data = self.process.readAllStandardOutput()
stdout = bytes(data).decode("utf8") # 字符串形式的输出信息
self.msg += stdout
print(self.msg)
2,从QProcess发消息给.py文件
文件层次:
其中,process_example2.py有图形界面,在其中点击按钮将会调用test.py并传递一些参数给test.py(代码被修改了)
process_example2.py代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'process_example2.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QProcess
from PyQt5.QtWidgets import QMainWindow, QApplication
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
MainWindow.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(360, 110, 75, 23))
self.pushButton.setObjectName("pushButton")
self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit_2.setGeometry(QtCore.QRect(50, 160, 300, 100))
self.textEdit_2.setObjectName("textEdit_2")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(360, 200, 200, 20))
self.label.setObjectName("label")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(50, 110, 300, 20))
self.lineEdit.setObjectName("lineEdit")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "发送"))
self.label.setText(_translate("MainWindow", "接收的信息将在第二个文本框内显示"))
# 以上代码是自动生成的,以下代码是自己手写的
self.process = None # 多进程
self.pushButton.clicked.connect(self.send_msg)
def send_msg(self):
msg = self.lineEdit.text()
self.process = QProcess()
if self.process is not None:
self.process.readyReadStandardOutput.connect(self.handle_stdout)#绑定正常信息输出函数
self.process.readyReadStandardError.connect(self.handle_stderr)#绑定错误信息输出函数
args = []#QProcess调用参数列表,参数可以不止一个,但是第一参数必须是py文件的路径.所有参数都应是字符串形式的,否则可能会报错
process_path = r'C:\237端点\软件\python\pycharm工作路径\试验区\test.py'#这是调用的py文件的绝对路径
args.append(process_path)
args.append(msg)
self.process.start("python", args)
def handle_stderr(self): # 处理报错信息的函数
if self.process is not None:
data = self.process.readAllStandardError()
stderr = bytes(data).decode("utf8") # 字符串格式的报错信息
self.textEdit_2.setText(stderr)
print(stderr)
def handle_stdout(self): # 处理正常输出信息的函数
if self.process is not None:
data = self.process.readAllStandardOutput()
stdout = bytes(data).decode("utf8") # 字符串形式的输出信息
self.textEdit_2.setText(stdout)
print(stdout)
def show_UI():
# 实例化,传参
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
# 创建对象
mainWindow = QMainWindow()
# 创建ui,引用Ui_MainWindow类
ui = Ui_MainWindow()
# ui.sig1.connect(lambda :close_main_window(ui.sig1, mainWindow))
# 调用Ui_MainWindow类的setupUi,创建初始组件
ui.setupUi(mainWindow)
# 创建窗口
mainWindow.show()
# 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)
sys.exit(app.exec_())
if __name__ == '__main__':
show_UI()
test.py代码:
import sys
if __name__ == '__main__':
print('子进程被调用了,这是在子进程的内部!')
msg = sys.argv[1]
print('子进程接收到的消息为:'+msg)
运行界面:
点击发送之后:
控制台:
事实上,QProcess不光能调用.py文件,还可以调用其他文件,比如exe文件,读者可以去试试。