PyQt5 界面显示无响应的实现


Posted in Python onMarch 26, 2020

在GUI程序中,主线程也叫GUI线程,因为它是唯一被允许执行GUI相关操作的线程。对于一些耗时的操作,如果放在主线程中,就是出现界面无法响应的问题。

界面假死分析

在编写QT的界面程序时,当我们调用QApplication.exec()时,我们就启动了QT的事件循环。在开始的时候,QT会发出一些事件来显示和绘制窗口部件。在这之后,事件循环就开始运行,不断地检查是不是有事件发生并且把这些事件发送给应用程序中的QObject。

当一个事件被处理时,其他事件也可能会产生并且追加到QT的事件队列中。如果我们在处理一个特定的事件上耗费过多的时间,用户界面就会变得不能够响应。例如在OCS保存一个观测流程的过程中,一直到文件保存完毕,窗口系统产生的一些事件才会被处
理。在保存过程中,这个应用程序就不能响应窗口系统的请求来重绘自己。

解决方法

  • 方式一使用多线程:一个处理应用程序用户界面的线程,另外一个执行文件保存的线程。
  • 方法二:调用QApplication.processEvents()

博主推荐使用第二种方法,该方法是在事件处理程序中调用QApplication.processEvents()。

这个函数告诉QT处理来处理任何没有被处理的事件,并且将控制权返回给调用者。实际上,QApplication.exec()就是一个不停调用QApplication.processEvents()函数的小while循环。这种方式的危险性在于,也许用户在观测流程未保存好之前就关闭了主窗口,或者在界面上通过鼠标或键盘执行了其它的输入,以至于观测流程未保存好就企图被程序使用。对于这个问题的解决办法是把 qApp -> processEvents(); 替换为 qApp -> eventLoop() -> processEvents( QEventLoop::ExcludeUserInput ); 通过这个调用告诉QT忽略鼠标和键盘事件。

...
  def downfile(self,file, url):
  print("开始下载:", file, url)
  try:
   r = requests.get(url, stream=True)
   with open(file, 'wb') as fd:
    for chunk in r.iter_content():
     fd.write(chunk)
     QApplication.processEvents()
  except Exception as e:
   print("下载失败了", e)
 ...

------------------------------------------补充一下方法一--------------------------》》》》》

说实话快有大半年没怎么使用过python了,关于多线程的处理方式,解释可能不是那么清楚。(目前是一个phper,上半年基本是补PHP方面的基础知识,也就是够用还不精通的一个状态)

先上一个半年前的小作品,是关于微信公众号方面的一些。

PyQt5 界面显示无响应的实现

这里就不谈用途与使用方法了,大概的讲一下,遇到界面假死的处理方法之一。话不多说,先上代码

from PyQt5.QtCore import QThread, pyqtSignal
 
class interface(QMainWindow, Ui_MainWindow):
 """
 Class documentation goes here.
 """
 def xxxx():
  "此处省略无数行代码......"
  self.Work()
 
 def Work(self):
  self.thread = RunThread()
  self.thread.start()
 
class RunThread(QThread):
 # python3,pyqt5与之前的版本有些不一样
 # 通过类成员对象定义信号对象
 # _signal = pyqtSignal(str)
 
 trigger = pyqtSignal()
 
 def __init__(self, parent=None):
  super(RunThread, self).__init__()
 
 def __del__(self):
  self.wait()
 
 def run(self):
  # 处理你要做的业务逻辑,这里是通过一个回调来处理数据,这里的逻辑处理写自己的方法
  dlg.Config['user'] = dlg.check_account['account']
  dlg.Config['passwd'] = dlg.check_account['password']
  dlg.Config['jk'] = 'http://xxx.com'
  if dlg.num != 1:
   dlg.operato.config_item(dlg.Config, dlg.wx_update) # 初始化配置
  else:
   dlg.operato.config_item(dlg.Config, dlg.wx_create) # 初始化配置
 
  self.trigger.emit()

说实话还是蛮喜欢python的这种简洁的写法的,所以在很长的一段时间里,一直是比较注重代码的简洁度与良好的注释。em...,不过在其它语言中很难保持这种初心,现在是比较注重性能,响应时间,并发、安全等问题。

这里的interface是主窗口类,如果想在自己的窗口中实现,加一个RunThread类,并在主窗口中定义一个函数,用于调用Work类方法就可以了。通过代码可以看到,不到50行的代码就实现了方法一中的功能了。pyqt5有很多自己的方法,包括多线程等等。这里提供的是一种思路。当然还有很多种方式实现,大家可以去探索一下,好的方法可以一起分享讨论。

========================================7月24号更新=================================

先放一个效果图,

PyQt5 界面显示无响应的实现

正常情况下会将一些耗时函数扔进Qthread线程中来避免页面假死的情况。

但并不是所有的都是行的通的,

PyQt5 界面显示无响应的实现

当使用异步协程的时候,pyqt5推荐的是使用quamash

import sys
import asyncio
import time
 
from PyQt5.QtWidgets import QApplication, QProgressBar
from quamash import QEventLoop, QThreadExecutor
 
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop) # NEW must set the event loop
 
progress = QProgressBar()
progress.setRange(0, 99)
progress.show()
 
async def master():
 await first_50()
 with QThreadExecutor(1) as exec:
  await loop.run_in_executor(exec, last_50)
 # TODO announce completion?
 
async def first_50():
 for i in range(50):
  progress.setValue(i)
  await asyncio.sleep(.1)
 
def last_50():
 for i in range(50,100):
  loop.call_soon_threadsafe(progress.setValue, i)
  time.sleep(.1)
 
with loop: ## context manager calls .close() when loop completes, and releases all resources
 loop.run_until_complete(master())

还有一种情况,就是在UI主线程中执行,需要注意的是,如果是耗时任务则会造成界面的卡死,并不大友好。

到此这篇关于PyQt5 界面显示无响应的实现的文章就介绍到这了,更多相关PyQt5 界面显示无响应内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python时间日期函数与利用pandas进行时间序列处理详解
Mar 13 Python
Python实现抓取HTML网页并以PDF文件形式保存的方法
May 08 Python
Python操作mongodb的9个步骤
Jun 04 Python
python实现将一个数组逆序输出的方法
Jun 25 Python
python实现图片批量压缩程序
Jul 23 Python
浅谈django rest jwt vue 跨域问题
Oct 26 Python
python+openCV利用摄像头实现人员活动检测
Jun 22 Python
python实现简易学生信息管理系统
Apr 05 Python
在pytorch中实现只让指定变量向后传播梯度
Feb 29 Python
Python利用Faiss库实现ANN近邻搜索的方法详解
Aug 03 Python
python爬虫beautifulsoup库使用操作教程全解(python爬虫基础入门)
Feb 19 Python
python中__slots__节约内存的具体做法
Jul 04 Python
Python基于class()实现面向对象原理详解
Mar 26 #Python
Python文件读写w+和r+区别解析
Mar 26 #Python
Python装饰器实现方法及应用场景详解
Mar 26 #Python
pycharm中导入模块错误时提示Try to run this command from the system terminal
Mar 26 #Python
Python多进程编程常用方法解析
Mar 26 #Python
简单了解python调用其他脚本方法实例
Mar 26 #Python
Python tornado上传文件的功能
Mar 26 #Python
You might like
星际争霸任务指南——虫族
2020/03/04 星际争霸
新的一年,新的期待:DC在2020年的四部动画电影
2020/01/01 欧美动漫
CakePHP去除默认显示的标题及图标的方法
2008/10/22 PHP
php 上传功能实例代码
2010/04/13 PHP
PHP针对常规模板引擎中与CSS/JSON冲突的解决方法
2014/08/19 PHP
php UNIX时间戳用法详解
2017/02/16 PHP
JAVASCRIPT 对象的创建与使用
2021/03/09 Javascript
BOOM vs RR BO5 第一场 2.14
2021/03/10 DOTA
jquery 查找iframe父级页面元素的实现代码
2011/08/28 Javascript
JS 打印界面的CSS居中代码适用所有浏览器
2014/03/19 Javascript
javascript实现控制浏览器全屏
2015/03/30 Javascript
javascript实现根据iphone屏幕方向调用不同样式表的方法
2015/07/13 Javascript
QQ登录背景闪动效果附效果演示源码下载
2015/09/22 Javascript
详解jQuery移动页面开发中的ui-grid网格布局使用
2015/12/03 Javascript
用JS动态设置CSS样式常见方法小结(推荐)
2016/11/10 Javascript
node.js操作mongodb简单示例分享
2017/05/25 Javascript
javascript将list转换成树状结构的实例
2017/09/08 Javascript
vue+element-ui集成随机验证码+用户名+密码的form表单验证功能
2018/08/05 Javascript
vue-cli打包后本地运行dist文件中的index.html操作
2020/08/12 Javascript
[42:20]2014 DOTA2华西杯精英邀请赛5 24 DK VS NewBee
2014/05/25 DOTA
[22:59]VGJ.S vs VG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python基础教程之异常详解
2019/01/10 Python
Python设计模式之迭代器模式原理与用法实例分析
2019/01/10 Python
Python Pickle 实现在同一个文件中序列化多个对象
2019/12/30 Python
python入门之井字棋小游戏
2020/03/05 Python
Python函数基本使用原理详解
2020/03/19 Python
Python闭包与装饰器原理及实例解析
2020/04/30 Python
HTML5单页面手势滑屏切换原理分析
2017/07/10 HTML / CSS
中国酒类在线零售网站:酒仙网
2016/08/20 全球购物
Vans英国官方网站:美国南加州的原创极限运动潮牌
2017/01/20 全球购物
电子商务个人自荐信
2013/12/12 职场文书
教师新年寄语
2014/04/03 职场文书
研究生简历自我评
2015/03/11 职场文书
小学英语听课心得体会
2016/01/14 职场文书
学校运动会开幕词
2016/03/03 职场文书
Java练习之潜艇小游戏的实现
2022/03/16 Java/Android