PySide2:如何使装饰槽在其工作线程上执行?
Posted
技术标签:
【中文标题】PySide2:如何使装饰槽在其工作线程上执行?【英文标题】:PySide2: How to make a decorated slot execute on its worker thread? 【发布时间】:2020-03-20 15:04:33 【问题描述】:使用 Python 3.7 和 PySide2,我创建了一个 worker object on a dedicated QThread 来执行一个长时间运行的函数。这在下面的代码中进行了说明。
import threading
from time import sleep
from PySide2.QtCore import QObject, QThread, Signal, Slot
from PySide2.QtWidgets import QApplication
class Main(QObject):
signal_for_function = Signal()
def __init__(self):
print('The main thread is "%s"' % threading.current_thread().name)
super().__init__()
self.thread = QThread(self)
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.start()
self.signal_for_function.connect(self.worker.some_function)
def some_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
class Worker(QObject):
# @some_decorator
def some_function(self):
print('some_function is running on thread "%s"' % threading.current_thread().name)
app = QApplication()
m = Main()
m.signal_for_function.emit()
sleep(0.100)
m.thread.quit()
m.thread.wait()
如果我在没有装饰器的情况下使用 some_function,我会按预期得到:
The main thread is "MainThread"
some_function is running on thread "Dummy-1"
但是,如果我应用装饰器(即取消注释“@some_decorator”),我会得到:
The main thread is "MainThread"
some_function is running on thread "MainThread"
为什么会发生这种情况,如何让装饰函数按照我的意图在工作线程上运行?
【问题讨论】:
经过一番研究,这里有一些相关的问题:***.com/questions/43937897/…和***.com/questions/23317195/… 【参考方案1】:解决方案:
你必须使用@functools.wrap
:
import functools
# ...
def some_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
输出:
The main thread is "MainThread"
some_function is running on thread "Dummy-1"
说明:
要分析使用@functools.wrap
与不使用的区别,则必须使用以下代码:
def some_decorator(func):
print(func.__name__, func.__module__, func.__doc__, func.__dict__)
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
print(wrapper.__name__, wrapper.__module__, wrapper.__doc__, wrapper.__dict__)
return wrapper
通过删除@functools.wrap
,您应该得到以下信息:
some_function __main__ None
wrapper __main__ None
通过不删除@functools.wrap
,您应该得到以下信息:
some_function __main__ None
some_function __main__ None '__wrapped__': <function Worker.some_function at 0x7f610d926a60>
主要区别在于__name__,在@functools.wrap 的情况下,它使包装函数与“func”同名,这有什么区别?它用于识别函数是否属于 Worker 类,即在创建 Worker 类时,会创建一个存储方法、属性等的字典,但是当信号调用 some_function 时,它会返回包装器名称为“wrapper”的名称不在 Worker 的字典中,但在使用 @functools.wrapper 的情况下,调用 some_function 然后返回名称为“some_function”的 wrapper,导致 Worker 对象调用它。
【讨论】:
该修复程序适用于这种特定情况,但我不确定您的解释是否完全正确。如果您在 Worker 的__init__
构造函数中定义 some_function
,some_function
将在 Worker 的字典中并且具有正确的名称,但它仍然不会在工作线程上执行。为什么?
@derren 1) 在 Worker 的 __init__ 构造函数中定义 some_function 是什么意思?我在你指出的内容中明确表示,2) PySide2 创建一个字典创建实例时的模式(在 _new_ 方法中调用 _init_ 之前的一步)。但一般来说,您应该始终使用 functools.wrap 以便装饰器(最终是方法包装器)的行为与函数相同。以上是关于PySide2:如何使装饰槽在其工作线程上执行?的主要内容,如果未能解决你的问题,请参考以下文章
PySide2 和 Matplotlib:如何让 MatPlotLib 在单独的进程中运行? ..因为它不能在单独的线程中运行