在主线程ui界面点击登录后,加延时10s,(模拟调用接口登录,假设耗时10s),ui主线程在等待请求返回结果,ui界面卡主,转圈,如下图。这个现象在软件中肯定是不能出现的!
为了解决这个问题,就必须引入多线程,在点击按钮后,主线程触发一个子线程去执行,让子线程执行耗时任务,主线程就会处于空闲状态,那么界面就不会出现转圈卡顿的现象!当子线程任务执行完成后,给主线程自定义信号发送消息,主线程收到,消息,反馈到主界面来!
完整的案例代码,如下,点击登录按钮是走主线程执行,会出现卡顿;点击忘记密码按钮,会触发子线程执行任务,主线程就处于空闲状态。当子线程任务执行完成后,给主线程自定义信号发送消息,主线程收到,消息,反馈到主界面来!
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(“./qt_ui/login_ui.ui”) # 加载 qt 设计师,设计出的ui
login_ui.ui文件
<ui version="4.0">
<class>MainWindowclass>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0x>
<y>0y>
<width>819width>
<height>600height>
rect>
property>
<property name="windowTitle">
<string>MainWindowstring>
property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>30x>
<y>120y>
<width>751width>
<height>271height>
rect>
property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>账号:string>
property>
widget>
item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
item>
layout>
item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>密码:string>
property>
widget>
item>
<item>
<widget class="QLineEdit" name="lineEdit_2"/>
item>
layout>
item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>登录string>
property>
widget>
item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>忘记密码?string>
property>
widget>
item>
layout>
item>
layout>
item>
<item>
<widget class="QTextEdit" name="textEdit"/>
item>
layout>
widget>
widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0x>
<y>0y>
<width>819width>
<height>23height>
rect>
property>
widget>
<widget class="QStatusBar" name="statusbar"/>
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", "忘记密码?"))