9.PyQt主界面卡顿解决办法

前言

在PyQt界面开发过程,有时候会遇到界面卡顿情况。经过多次试验,大部分原因是由于界面在进行数据查询过程耗时导致界面卡顿。解决方法建议采用多线程、多进程解决。


一、卡顿模拟

在界面更新函数加入time.sleep(2)即可阻断主界面执行完成,导致卡顿现象

	def say(self,msg):
		# 主界面更新如果耗时,那么主界面就会出现卡顿现象,下面这一行模拟主界面耗时,注释掉,主界面不会出现卡顿现象
		time.sleep(3)

		self.idx += 1
		self.txtOutput.setText('%s[%d]' % (msg,self.idx)

执行效果如下:
9.PyQt主界面卡顿解决办法_第1张图片

二、具体代码如下

代码名称:UiMain.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file '.\UiMain.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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(400, 300)
        self.gridLayout = QtWidgets.QGridLayout(Form)
        self.gridLayout.setObjectName("gridLayout")
        self.label = QtWidgets.QLabel(Form)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.lnInput = QtWidgets.QLineEdit(Form)
        self.lnInput.setObjectName("lnInput")
        self.gridLayout.addWidget(self.lnInput, 0, 1, 1, 1)
        self.bntBackupSelect = QtWidgets.QPushButton(Form)
        self.bntBackupSelect.setObjectName("bntBackupSelect")
        self.gridLayout.addWidget(self.bntBackupSelect, 0, 2, 1, 1)
        self.txtOutput = QtWidgets.QTextBrowser(Form)
        self.txtOutput.setObjectName("txtOutput")
        self.gridLayout.addWidget(self.txtOutput, 1, 0, 1, 3)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "输入信息"))
        self.bntBackupSelect.setText(_translate("Form", "后台查询"))

代码名称:testThread.py

# -*- coding: utf-8 -*-
import time
import pythoncom
import UiMain

from PyQt5 import QtCore

class ThreadCheckAnti(QtCore.QThread):
	sinOut 		= QtCore.pyqtSignal(str) # 自定义信号,执行run()函数时,从相关线程发射此信号
	def __init__(self,parent=None):
		super(ThreadCheckAnti, self).__init__(parent)

	def run(self):
		pythoncom.CoInitialize()
		while True:
			time.sleep(1)
			self.sinOut.emit('hi')

		#释放资源
		pythoncom.CoUninitialize()

from PyQt5.QtWidgets import QApplication , QWidget, QMessageBox
class WdtMain(QWidget,UiMain.Ui_Form):
	def __init__(self, parent=None):
		super(WdtMain, self).__init__(parent=parent)

		self.setupUi(self)
		self.initUI()

		self.idx = 0

	def __del__(self):
		print(self.__class__,'del')
		#self.thread.stop()

	def initUI(self):
		# 菜单模块
		self.thread = ThreadCheckAnti()
		self.thread.sinOut.connect(self.say)
		self.thread.start()
		#self.openBackRun()
	def say(self,msg):
		# 主界面更新如果耗时,那么主界面就会出现卡顿现象,下面这一行模拟主界面耗时,注释掉,主界面不会出现卡顿现象
		time.sleep(3)

		self.idx += 1
		self.txtOutput.setText('%s[%d]' % (msg,self.idx))


if __name__=="__main__":
	import sys
	app = QApplication(sys.argv)
	demo = WdtMain()
	demo.show()
	sys.exit(app.exec_())

三、卡顿解决办法

以上案例虽然用到了线程,但是由于主界面的say函数由于比较耗时(2秒完成),而线程执行的速度又是1秒发送一次信号,导致主界面应接不暇。
解决思路:

A 消息驱动机制

say函数与线程互动,线程每次发送一次消息等待say返回信息再执行后续循环。

B 锁机制

say与线程之间共用一个锁,每次线程执行之前加锁,发送完成之后,解锁;say函数修改界面之前加锁,修改完成之后解锁。

以上代码改动如下:

# -*- coding: utf-8 -*-
import time
import pythoncom
import UiMain

from PyQt5 import QtCore
from PyQt5.QtCore import QMutex
from multiprocessing import Queue

class ThreadCheckAnti(QtCore.QThread):
	sinOut 		= QtCore.pyqtSignal(str) # 自定义信号,执行run()函数时,从相关线程发射此信号
	def __init__(self,q_msg:Queue,q_lock:QMutex,parent=None):
		super(ThreadCheckAnti, self).__init__(parent)
		self.q_msg=q_msg
		self.q_lock=q_lock

	def run(self):
		pythoncom.CoInitialize()
		while True:
			print('thread 准备 加锁...')
			self.q_lock.lock()
			print('thread 加锁 成功 ...')
			time.sleep(1)
			self.sinOut.emit('hi')
			self.q_lock.unlock()
			print('thread 释放锁 ...')

			# 等待主界面消息
			print('thread wait msg')
			msg = self.q_msg.get()
			print('say 反馈消息:', msg )


		#释放资源
		pythoncom.CoUninitialize()

from PyQt5.QtWidgets import QApplication , QWidget, QMessageBox
class WdtMain(QWidget,UiMain.Ui_Form):
	def __init__(self, parent=None):
		super(WdtMain, self).__init__(parent=parent)

		self.setupUi(self)
		self.initUI()

		self.idx = 0

	def __del__(self):
		print(self.__class__,'del')
		#self.thread.stop()

	def initUI(self):
		# 菜单模块
		self.q_lock=QMutex()
		self.q_msg =Queue()

		self.thread = ThreadCheckAnti(q_lock=self.q_lock, q_msg=self.q_msg)
		self.thread.sinOut.connect(self.say)
		self.thread.start()
		#self.openBackRun()
	def say(self,msg):
		self.q_lock.lock()
		# 主界面更新如果耗时,那么主界面就会出现卡顿现象,下面这一行模拟主界面耗时,注释掉,主界面不会出现卡顿现象
		time.sleep(3)

		self.idx += 1
		self.txtOutput.setText('%s[%d]' % (msg,self.idx))
		self.q_lock.unlock()


		self.q_msg.put('go')


if __name__=="__main__":
	import sys
	app = QApplication(sys.argv)
	demo = WdtMain()
	demo.show()
	sys.exit(app.exec_())

解决效果:主界面在拖动过程不会出现“未响应”提示;由于say中sleep(3)在拖动过程还是会出现卡的感觉,这种是由于主界面是单进程缘故,所以在say执行耗时尽量减少到毫秒级别。

你可能感兴趣的:(PyQt快速开发框架项目,python,pyqt5)