PySide2: Как заставить оформленный слот выполняться в его рабочем потоке? - PullRequest
2 голосов
/ 20 марта 2020

Используя Python 3.7 и PySide2, я создал рабочий объект на выделенном 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"

Почему это происходит, и как заставить украшенную функцию запускаться в рабочем потоке, как я намеревался?

1 Ответ

1 голос
/ 20 марта 2020

Решение:

Вы должны использовать @functools.wrap:

<b>import functools</b>
# ...

def some_decorator(func):
    <b>@functools.wraps(func)</b>
    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 это делает функцию-оболочку с тем же именем, что и "fun c ", а какая разница? Он служит для определения, принадлежит ли функция классу Worker или нет, то есть, когда создается класс Worker, создается словарь, в котором хранятся методы, атрибуты и т. Д. c., Но когда сигнал вызывает some_function, тогда он возвращает обертку с именем «обертка», которой нет в словаре Worker, но в случае использования @ functools.wrapper вызывается some_function, а затем возвращается обертке с именем «some_function», вызывающей объект Worker.

...