使用python threading模块

from threading import Thread , Lock , RLock , Semaphore , BoundedSemaphore , Event from PySide2 . QtCore import QTimer , QDate , QTime , QDateTime , QThread , QThreadPool , Qt , QObject , Signal from PySide2 . QtWidgets import * from PySide2 . QtGui import QIcon , QFont import PySide2 import logging logging . basicConfig ( level = logging . DEBUG , format = "%(asctime)s-%(message)s" ) # 自定义信号 class Laufing ( QObject ) : notify = Signal ( object ) # 自定义线程 class MyThread ( Thread ) : def __init__ ( self , * args , ** kwargs ) : self . args = args self . kwargs = kwargs Thread . __init__ ( self ) def run ( self ) : minField , maxField , sig = self . args minVal = int ( minField . text ( ) ) if minField . text ( ) else None maxVal = int ( maxField . text ( ) ) if maxField . text ( ) else None # 通知 必须输入最小值、最大值 if minVal is None or maxVal is None : sig . notify . emit ( None ) else : index = minVal while index < maxVal : index += 1 # QProgressBar 只能设置整数,自动计算百分比 # 这里待更新 pert = ( index - minVal ) / ( maxVal - minVal ) * 100 print ( "当前百分比:" , pert ) sig . notify . emit ( pert ) # 如下最少必须为1, 若为0.5 则无效,可采用msleep(500) QThread . sleep ( 1 ) class MyWidget ( QWidget ) : def __init__ ( self ) : super ( MyWidget , self ) . __init__ ( ) # 设置窗口标题s self . setWindowTitle ( "laufing" ) self . setWindowIcon ( QIcon ( "./imgs/dog.jpg" ) ) # 窗口居中 desk = QDesktopWidget ( ) width , height = desk . width ( ) , desk . height ( ) self . resize ( 800 , 600 ) self . move ( width // 2 - self . width ( ) // 2 , height // 2 - self . height ( ) // 2 ) self . laufing = Laufing ( ) self . laufing . notify . connect ( self . updateUi ) self . setUi ( ) def setUi ( self ) : gridLayout = QGridLayout ( self ) gridLayout . setContentsMargins ( 10 , 10 , 10 , 10 ) minLabel = QLabel ( "最小值" ) minLabel . setStyleSheet ( "border: 1px solid red; border-radius: 10px; align:center;" ) maxLabel = QLabel ( "最大值" ) self . minField = QLineEdit ( ) self . minField . setPlaceholderText ( "输入最小值" ) self . maxField = QLineEdit ( ) self . maxField . setPlaceholderText ( "输入最大值" ) self . progress = QProgressBar ( ) btn = QPushButton ( "点击下载" ) btn . clicked . connect ( self . download ) gridLayout . addWidget ( minLabel , 0 , 0 ) gridLayout . addWidget ( self . minField , 0 , 1 ) gridLayout . addWidget ( maxLabel , 1 , 0 ) gridLayout . addWidget ( self . maxField , 1 , 1 ) gridLayout . addWidget ( self . progress , 2 , 0 , columnSpan = 2 , alignment = Qt . AlignCenter ) gridLayout . addWidget ( btn , 3 , 0 , columnSpan = 2 , alignment = Qt . AlignCenter ) gridLayout . setColumnStretch ( 2 , 10 ) def download ( self ) : # 注意避免线程对象被垃圾回收 self . th = MyThread ( self . minField , self . maxField , self . laufing ) # 设置守护线程,主线程退出,则不管子线程有没有执行完,子线程都退出 self . th . setDaemon ( True ) self . th . start ( ) def updateUi ( self , arg ) : if arg is None : mb = QMessageBox ( self ) mb . setModal ( True ) mb . setWindowTitle ( "提示" ) mb . setText ( "<span style='color: red;'>必须输入最小值和最大值</span>" ) mb . setTextFormat ( Qt . TextFormat . RichText ) mb . setInformativeText ( "子标题" ) confirm = mb . addButton ( "确定" , QMessageBox . ButtonRole . YesRole ) cancle = mb . addButton ( "取消" , QMessageBox . ButtonRole . NoRole ) # 按钮被点击 mb . buttonClicked . connect ( lambda x : mb . hide ( ) if x == confirm else None ) mb . show ( ) else : self . progress . setValue ( arg ) # 拖动窗口空白区域可移动窗口 def mousePressEvent ( self , event : PySide2 . QtGui . QMouseEvent ) : self . originX = event . pos ( ) . x ( ) self . originY = event . pos ( ) . y ( ) def mouseMoveEvent ( self , event : PySide2 . QtGui . QMouseEvent ) : self . move ( event . screenPos ( ) . x ( ) - self . originX , event . screenPos ( ) . y ( ) - self . originY - 35 ) if __name__ == '__main__' : import sys app = QApplication ( sys . argv ) win = MyWidget ( ) win . show ( ) sys . exit ( app . exec_ ( ) )

使用QThread实现

以上重写run方法的形式,同样可以使用QThread实现。
简单修改上述的线程类定义即可。
注意线程不能被垃圾回收,否则报错

# 防止线程对象被垃圾回收,增加self引用
self.th = MyThread(self.minField, self.maxField, self.laufing)
# QThread 线程默认是守护线程
# run方法是在子线程中执行
# run执行后,再次调用start将不会进行任何处理
# run运行时,self.th.quit/exit 无法中断线程
# run 运行时, self.th.terminate() 可以立即中断子线程,但不安全
# run运行时,可以在主线程设置一个变量标识,子线程中运行时检测该变量,从而控制子线程的中断(注意加锁)。
# mutex = QMutex()
# mutex.lock()/unlock()
# QThread子线程的信号
# self.th.started.connect(func)   子线程正常开始后
# self.th.finished.connect(self.th.deleteLater)   子线程正常结束后
# 正常结束后,一般使用deleteLater释放内存

使用QThreadPool实现

pending

本文主要讲解使用多线程模块QThread解决PyQt界面程序唉执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题。用户使用工具过程中出现这些问题时会误以为程序出错,从而把程序关闭。这样,导致工具的用户使用体验不好。下面我们通过模拟上述出现的问题并讲述使用多线程QThread模块解决此类问题的方法。 PyQt程序卡顿和无法实时显示问题现象 使用PyQt界面程序,点击运行按钮后,程序在显示框中每秒打印1个数字。程序代码如下: # -*- coding: utf-8 -*- import sys import time from PyQt5.QtCore import QThrea
书签一个用于VFX和动画工作的简单资产管理器。 书签提供了项目资产和文件的基本概述。 使用它来创建新作业,快照或浏览现有内容。 与Slack伙伴共享路径和注释,并使用OpenImageIO和Shotgun RV预览渲染,或者添加版本化的模板文件来管理文件命名。 该项目从Maya脚本开始就可以更改工作空间,但此后已发展成为一个独立的多线程资产管理器。 对于Maya艺术家,书签具有用于加载和保存场景和缓存(问候增量保存! ),预览/导入图像的工具。 非玛雅艺术家,甚至是中小型工作室,可能会发现自己需要简单的资产管理器,而其中不需要Shotgun等。 我用它来管理自己的自由职业项目,因
PySide2多线程问题示例:创建新线程、子线程发射信号到主界面 本文是小白在pyside学习过程中的记录,从无子线程、子线程在主程序中直接操作Qt界面、子线程发射信号操作主界面三个步骤依次进阶,记录对多线程的一种处理方式。如有不合理的地方恳请路过大神赐教! 一、示例问题(随便整了一个简单的):创建一个界面,在一个界面输入最大最小值,在另一个界面依次打印出以下信息:从最小值开始,每次加1,到最大值后结束。 先给出三个方式的结果: 1、只有一个主程序:在操作界面填入信息,点击开始按钮后,无法再点击界面上其他任
PyQt5 和 PySide2 的区别 他们背后原理的差别我就不细说了(我也不知道),你只要记住使用上基本差不多就行,网上搜索他们用法的时候,以哪个为关键词搜索都行吧,官网 给出了他们的差异,聚焦我们要讲的问题,在信号与槽机制和多线程机制上,他们的差别如下: # PyQt5 引入线程类和信号
在使用 Qt For Python 编写GUI时遇到个问题,GUI界面有一个主进程,在不使用QProcess, QThread情况下,都是默认单线程与单进程的。例如新建一个按钮,该按钮调用一个槽函数 是 time.sleep( 10000) ,那在这个槽函数运行完之前,整个UI界面都是假死状态(无响应),要等整个槽函数运行完后才能GUI恢复控制。 所以在有一个槽函数需要消耗很多时间的情况下,必须...
用 Python 和 PySide2 实现鼠标右键触发剪切列表内容的代码可以这样写:listView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) listView.customContextMenuRequested.connect(lambda pos: self.trigger_cut(listView, pos))def trigger_cut(self, listView, pos): index = listView.indexAt(pos) if index.isValid(): listView.model().removeRow(index.row())