PyQt5 + Python 3: передача списков, диктов в качестве сигнальных аргументов между потоками

Я использую pyqtSignal для отправки списка python в качестве аргумента из рабочего потока в основной поток. Когда qt создает копию объекта, передаваемого в качестве аргумента. Согласно: http://www.embeddeduse.com/2013/06/29/copied-or-not-copied-arguments-signals-slots/ qt должен сделать копию объекта. Однако в приведенном ниже примере основной поток может изменить содержимое списка, отправляемого из другого потока.

import sys
import time
from PyQt5.QtCore import QThread, QObject, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QApplication

class ClassProcessing(QObject):

    py_sig_send_data = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        # initialize some variables
        self.data = [1, 2, 3, 4, 5]

    def worker(self):
        print(self.data)
        self.py_sig_send_data.emit(self.data)
        time.sleep(1)
        print("modfied data in thread", self.data)

class ClassProcessingThread(QObject):
    def __init__(self):
        super().__init__()
        self.objThread = QThread()
        self.objThread_id = 1
        self.objThread_finished = False
        self.processing = ClassProcessing()
        self.processing.moveToThread(self.objThread)
        self.objThread.started.connect(self.processing.worker)
        self.objThread.start()

class SomeClass(QObject):
    def __init__(self):
        super().__init__()

    @pyqtSlot(list)
    def receive_data(self, data):
        print("received data", data)
        data[1] = 42
        print("modified data", data)

def main():
    app = QApplication(sys.argv)
    processing_thread = ClassProcessingThread()
    some_class = SomeClass()
    processing_thread.processing.py_sig_send_data.
        connect(some_class.receive_data)
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Выход:

[1, 2, 3, 4, 5]
received data [1, 2, 3, 4, 5]
modified data [1, 42, 3, 4, 5]
modified data in thread [1, 42, 3, 4, 5]

Может кто-нибудь объяснить мне, как передать список в pyqtSignal потокобезопасным способом. Спасибо.


person John    schedule 30.03.2015    source источник


Ответы (1)


PyQt ведет себя не так, как Qt, когда речь идет о передаче типов контейнеров между потоками с использованием сигналов.

В частности, отсутствует автоматическое преобразование [1] в эквивалентные типы Qt и, следовательно, неявное копирование. Однако PyQt предоставляет механизм для явного запроса таких преобразований. Для этого вы можете определить пользовательский сигнал, используя либо QVariantList, либо QVariantMap:

    py_sig_send_data = pyqtSignal('QVariantList')

Однако важно отметить, что QVariantMap поддерживает только строковые ключи.

В целом, однако, вероятно, проще, понятнее и безопаснее просто явно копировать изменяемые типы Python, прежде чем передавать их через сигналы через потоки.

[1] Или, по крайней мере, больше не.

person ekhumoro    schedule 30.03.2015
comment
Интересно, что это нигде не указано более явно. Я долго искал, но видимо не в том месте. Явное копирование, вероятно, полезно для однократной отправки сигнала; данные копируются в data_buffer и data_buffer передаются в качестве аргумента. Как насчет непрерывной отправки (например, 50 раз в секунду)? Отправляющий и принимающий потоки могут одновременно обращаться к data_buffer. - person John; 31.03.2015
comment
@Джон. Если вы хотите избежать копирования, вам нужно использовать какой-то механизм блокировки. - person ekhumoro; 31.03.2015