pyqt5多线程(子线程执行将结果返回到主线程上,提示对话框)
1.为什么要多线程执行
在主线程ui界面点击登录后,加延时10s,(模拟调用接口登录,假设耗时10s),ui主线程在等待请求返回结果,ui界面卡主,转圈,如下图。这个现象在软件中肯定是不能出现的!
2.解决思路
为了解决这个问题,就必须引入多线程,在点击按钮后,主线程触发一个子线程去执行,让子线程执行耗时任务,主线程就会处于空闲状态,那么界面就不会出现转圈卡顿的现象!当子线程任务执行完成后,给主线程自定义信号发送消息,主线程收到,消息,反馈到主界面来!
3.完整的案例代码
完整的案例代码,如下,点击登录按钮是走主线程执行,会出现卡顿;点击忘记密码按钮,会触发子线程执行任务,主线程就处于空闲状态。当子线程任务执行完成后,给主线程自定义信号发送消息,主线程收到,消息,反馈到主界面来!
https://gitee.com/HP_mojin/pyqt_ui/tree/master/pyqt_ui_01
main.py 文件
import sys
import time
from PyQt5 import uic,QtWidgets
from PyQt5.Qt import QApplication, QWidget, QThread
from PyQt5.QtCore import pyqtSignal
class MyThread(QThread): #子线程
def __init__(self,signal,kwargs ):
super().__init__()
self.start_complete_signal=signal #将自定义信号传递过来,
self.kwargs=kwargs
def run(self):
#延时 10秒
for i in range(10):
print("是MyThread线程中执行....%d" % (i + 1))
time.sleep(1)
self.start_complete_signal.emit(f'子线程,运行完成:{self.kwargs}') #给自定义信号发送消息
class MyWin(QWidget):#主线程 主界面
# 声明一个信号,只能写在函数外面
forgot_status_signal = pyqtSignal(str) #自定义信号,接受子线程,执行完成的消息
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):#主界面元素
self.ui = uic.loadUi("./qt_ui/login_ui.ui") # 加载 qt 设计师,设计出的ui
# 从ui文件中加载控件
login_btn1 = self.ui.pushButton #登录按钮
forgot_btn2 = self.ui.pushButton_2 #忘记密码按钮
# 给2个按钮绑定槽函数
login_btn1.clicked.connect(self.login) # 绑定登录按钮的槽函数
forgot_btn2.clicked.connect(self.forgot_password) # 绑定忘记密码按钮的槽函数
self.forgot_status_signal.connect(self.forgot_status) #绑定自定义信号的槽函数,收到子线程消息,执行绑定的槽函数
def login(self): #登录按钮绑定的槽函数,主线程执行,出现卡顿
#耗时 延时10秒
for i in range(10):
print("是UI线程中执行....%d" % (i + 1))
time.sleep(1)
user_name=self.ui.lineEdit.text() #获取账号输入框text信息
password = self.ui.lineEdit_2.text() #获取密码输入框text信息
dic={
"user_name":user_name,
"password":password
}
#将返回消息写入显示框
self.ui.textEdit.setText(f'主线程运行完成:{dic}')
#输入框刷新显示
self.ui.textEdit.repaint()
# 对话框弹框
QtWidgets.QMessageBox.information(None, '主线程', f'主线程运行完成:{dic}', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.Yes)
def forgot_password(self): #忘记密码按钮 绑定的槽函数,执行后启动子线程
user_name=self.ui.lineEdit.text() #获取账号输入框text信息
password = self.ui.lineEdit_2.text() #获取密码输入框text信息
dic={
"user_name":user_name,
"password":password
}
self.my_thread = MyThread(self.forgot_status_signal,dic) # 创建线程
self.my_thread.start() # 开始线程 启动子线程执行任务
def forgot_status(self,srt): #收到子线程消息,执行绑定的槽函数
print(f'收到子线程消息:{srt}')
print("主线程:收到了槽函数,打印消息")
#将返回消息写入显示框
self.ui.textEdit.setText(srt)
#输入框刷新显示
self.ui.textEdit.repaint()
# 对话框弹框
QtWidgets.QMessageBox.information(None, '主线程打印', srt, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.Yes)
if __name__ == "__main__":
app = QApplication(sys.argv)
myshow = MyWin()
myshow.ui.show()
app.exec()
self.ui = uic.loadUi(“./login_ui.ui”)
login_ui.ui 文件
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>846</width>
<height>536</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>类型1</string>
</attribute>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>821</width>
<height>431</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QLabel" name="label_80">
<property name="text">
<string>第二位数:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="label_78">
<property name="text">
<string>最大值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_45"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QLabel" name="label_83">
<property name="text">
<string>最小值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_48"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QLabel" name="label_85">
<property name="text">
<string>保留小数:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_43"/>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="label_82">
<property name="text">
<string>第一位数:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_84">
<property name="text">
<string>最大值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_47"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_79">
<property name="text">
<string>最小值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_46"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_81">
<property name="text">
<string>保留小数:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_44"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_16">
<item>
<widget class="QLabel" name="label_77">
<property name="text">
<string>输出路径:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
<widget class="QLineEdit" name="lineEdit_7"/>
</item>
<item>
<widget class="QPushButton" name="pushButton_7">
<property name="text">
<string>浏览</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_18">
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>开始执行</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="6" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_17">
<item>
<widget class="QLabel" name="label_97">
<property name="text">
<string>日志输出:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_75">
<property name="text">
<string>运算方法:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="checkBox_29">
<property name="text">
<string>加法</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_30">
<property name="text">
<string>减法</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_31">
<property name="text">
<string>乘法</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_32">
<property name="text">
<string>除法</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_76">
<property name="text">
<string>选择题型:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="7" column="0">
<widget class="QTextEdit" name="textEdit"/>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>类型2</string>
</attribute>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>821</width>
<height>431</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_19">
<item>
<widget class="QLabel" name="label_90">
<property name="text">
<string>选择题型:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_2"/>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_20">
<item>
<widget class="QLabel" name="label_87">
<property name="text">
<string>运算方法:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="checkBox_33">
<property name="text">
<string>加法</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_34">
<property name="text">
<string>减法</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_35">
<property name="text">
<string>乘法</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_36">
<property name="text">
<string>除法</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_25">
<item>
<widget class="QLabel" name="label_92">
<property name="text">
<string>第一位数:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_24">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QLabel" name="label_91">
<property name="text">
<string>最大值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_49"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<widget class="QLabel" name="label_88">
<property name="text">
<string>最小值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_50"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_23">
<item>
<widget class="QLabel" name="label_94">
<property name="text">
<string>保留小数:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_53"/>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_29">
<item>
<widget class="QLabel" name="label_96">
<property name="text">
<string>第二位数:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_27">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_26">
<item>
<widget class="QLabel" name="label_86">
<property name="text">
<string>最大值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_51"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_95">
<property name="text">
<string>最小值:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_54"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_28">
<item>
<widget class="QLabel" name="label_89">
<property name="text">
<string>保留小数:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_52"/>
</item>
<item>
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_30"/>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_34">
<item>
<widget class="QLabel" name="label_93">
<property name="text">
<string>输出路径:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_8"/>
</item>
<item>
<widget class="QPushButton" name="pushButton_8">
<property name="text">
<string>浏览</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_31">
<item>
<spacer name="horizontalSpacer_12">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>开始执行</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="7" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_32">
<item>
<widget class="QLabel" name="label_98">
<property name="text">
<string>日志输出:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="8" column="0">
<widget class="QTextEdit" name="textEdit_2"/>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>846</width>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menu">
<property name="title">
<string>设置</string>
</property>
<addaction name="actionpeiz_wj"/>
<addaction name="actionguanyu"/>
</widget>
<addaction name="menu"/>
</widget>
<action name="actionpeiz_wj">
<property name="text">
<string>配置文件</string>
</property>
</action>
<action name="actionguanyu">
<property name="text">
<string>关于</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
login_ui.py 文件
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'login_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# 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_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(819, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(30, 120, 751, 271))
self.widget.setObjectName("widget")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.widget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.lineEdit = QtWidgets.QLineEdit(self.widget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_2 = QtWidgets.QLabel(self.widget)
self.label_2.setObjectName("label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.lineEdit_2 = QtWidgets.QLineEdit(self.widget)
self.lineEdit_2.setObjectName("lineEdit_2")
self.horizontalLayout_2.addWidget(self.lineEdit_2)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.pushButton = QtWidgets.QPushButton(self.widget)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout_3.addWidget(self.pushButton)
self.pushButton_2 = QtWidgets.QPushButton(self.widget)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout_3.addWidget(self.pushButton_2)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.horizontalLayout_4.addLayout(self.verticalLayout)
self.textEdit = QtWidgets.QTextEdit(self.widget)
self.textEdit.setObjectName("textEdit")
self.horizontalLayout_4.addWidget(self.textEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 819, 23))
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.label.setText(_translate("MainWindow", "账号:"))
self.label_2.setText(_translate("MainWindow", "密码:"))
self.pushButton.setText(_translate("MainWindow", "登录"))
self.pushButton_2.setText(_translate("MainWindow", "忘记密码?"))