PyQt5 + Python 3:传递列表,命令作为跨线程的信号参数

问题描述 投票:4回答:2

我正在使用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中传递列表。谢谢。

multithreading thread-safety python-3.4 signals-slots pyqt5
2个回答
2
投票

当使用信号在线程之间传递容器类型时,PyQt的行为与Qt不同。

具体地说,没有自动转换[1]到等效的Qt类型,因此没有隐式复制。不过,PyQt确实提供了一种机制,可以明确地请求此类转换。为此,您可以使用QVariantListQVariantMap定义自定义信号:

    py_sig_send_data = pyqtSignal('QVariantList')

但是,请注意QVariantMap仅支持字符串键。

总而言之,在通过线程间的信号传递它们之前,显式复制可变的python类型可能更简单,更清晰,更安全。

[1]或至少not any more


0
投票

我尝试了上述操作(使用QVariantList而不是list),外加一些额外的行以尝试告诉您正在运行多少个线程以及这些线程的名称是什么。代码如下。我正在使用Anaconda3 / Python 3.8.2 / Spyder4.1.3。我的笔记本电脑最多有8个线程。我用过:

qthreadname=threading.current_thread().name
print('Class/Function thread name: ',qthreadname) 

在不同的位置检测正在使用哪个线程,所有报告:MainThread。使用John的脚本时,我做错了(可能),或者没有创建新线程。我没有50个声誉,因此无法添加此评论。我很想知道这里的问题。谢谢!

代码:

# -*- coding: utf-8 -*-
"""
Created on Sun May 24 14:57:33 2020 
@authors: John/Ekhumoro
"""

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

threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % threadpool.maxThreadCount())

for thread in threading.enumerate(): 
    print(thread.name)
print('^^^Running threads in ClassProcessing^^^')

class ClassProcessing(QObject):

    #py_sig_send_data = pyqtSignal(list)
    py_sig_send_data = pyqtSignal('QVariantList')

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

    def worker(self):

        for thread in threading.enumerate(): print(thread.name)
        print('^^^Running threads in ClassProcessing/worker^^^')

        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)

        for thread in threading.enumerate(): print(thread.name)
        print('^^^Running threads in ClassProcessingThread/init^^^')

        if self.objThread.isRunning():
            print('yep')
        else:
            print('nope')

        self.objThread.started.connect(self.processing.worker)
        self.objThread.start()
        qthreadname=threading.current_thread().name
        print('ClassProcessingThread thread name (just started): ',qthreadname)
        if self.objThread.isRunning():
            print('yep!!!')
        else:
            print('nope!!!')
        qthreadname=threading.current_thread().name
        print('ClassProcessingThread thread name: ',qthreadname)   
        self.objThread.terminate()

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

        for thread in threading.enumerate(): print(thread.name)
        print('^^^Running threads in SomeClass/init^^^')
        qthreadname=threading.current_thread().name
        print('SomeClass/init thread name: ',qthreadname)   

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

        # for thread in threading.enumerate(): print(thread.name)
        # print('^^^Running threads in SomeClass/receive_data^^^')
        qthreadname=threading.current_thread().name
        print('receive_data thread name: ',qthreadname)

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()
© www.soinside.com 2019 - 2024. All rights reserved.