Python PySide:若通过lambda调用该方法,则不会在线程上下文中执行该方法

Python PySide:若通过lambda调用该方法,则不会在线程上下文中执行该方法,python,lambda,pyside,qthread,Python,Lambda,Pyside,Qthread,我有一个Worker对象,并使用其方法moveToThread将其放入线程中 现在我把它的work方法称为: 如果我直接调用该方法,它将在其对象所在的线程中执行 如果使用lambda调用该方法,则该方法将在主线程中执行 例如: from PySide.QtCore import * from PySide.QtGui import * import sys class Worker(QObject): def __init__(self): super().__in

我有一个
Worker
对象,并使用其方法
moveToThread
将其放入线程中

现在我把它的
work
方法称为:

  • 如果我直接调用该方法,它将在其对象所在的线程中执行
  • 如果使用lambda调用该方法,则该方法将在主线程中执行
例如:

from PySide.QtCore import *
from PySide.QtGui import *
import sys

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

    def work(self):
        print(self.thread().currentThread())


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.btnInThread = QPushButton('in thread')
        self.btnNotInThread = QPushButton('not in thread')
        layout = QVBoxLayout()
        layout.addWidget(self.btnInThread)
        layout.addWidget(self.btnNotInThread)
        self.setLayout(layout)

        self.worker = Worker()
        self.Thread = QThread()
        self.worker.moveToThread(self.Thread)
        self.Thread.start()

        self.btnInThread.clicked.connect(self.worker.work)
        self.btnNotInThread.clicked.connect(lambda: self.worker.work())

        self.show()
        print('{0} <- Main Thread'.format(self.thread().currentThread()))


def main():
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

我想我有答案了

我尝试编写代码并更改了单击的
connect
方法以使用
QtCore.Qt.QueuedConnection
QtCore.Qt.DirectConnection
connect(self.worker.work,Qt.QueuedConnection)
)。直接连接使两个函数以相同的方式工作(lambda的工作方式),它们都在主线程中运行。但是,排队连接使它们的工作方式不同。lambda函数仍在主线程中运行,而辅助函数调用在单独的线程中运行。注意:如果不在
connect
中提供参数,您使用的是自动连接,它将使用QueuedConnection

我通读了文件

队列连接当控制返回到接收方线程的事件循环时,将调用插槽。插槽在接收器的线程中执行

所以我相信lambda正在主线程中运行,因为lambda正在创建一个新函数。新的lambda函数不是插槽,它存在于主线程中。lambda函数接收器的线程是主线程。同时,
worker.work
方法是一个具有不同接收器线程的插槽。因此,信号知道在工作线程中调用
self.worker.work
,而在主线程中调用lambda函数,然后在主线程中调用
self.worker.work()

我知道这很不方便,因为lambda对于向函数传递参数很有用

使用信号映射器传递值

from PySide import QtCore
from PySide import QtGui
import sys
import time


def create_map(obj, func, args=None):
    """Create a signal mapper to associate a value with a function.

    Args:
        obj (QObject): Object to map the value to with the signal mapper
        func (function): Function to run when the signal mapper.map function is called.
        args (tuple)[None]: Arguments you want to pass to the function.

    Returns:
        map_callback (function): Map function to connect to a signal.
        mapper (QSignalMapper): You may need to keep a reference of the signal mapper object.
    """
    mapper = QtCore.QSignalMapper()
    mapper.setMapping(obj, args)
    mapper.mapped.connect(func)
    return mapper.map, mapper


class Worker(QtCore.QObject):

    def __init__(self):
        super().__init__()

    def work(self, value=0):
        print(self.thread().currentThread())
        time.sleep(2)
        print("end", value)


class Example(QtGui.QWidget):
    def __init__(self):
        super().__init__()
        self.btnInThread = QtGui.QPushButton('in thread')
        self.btnNotInThread = QtGui.QPushButton('not in thread')
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.btnInThread)
        layout.addWidget(self.btnNotInThread)
        self.setLayout(layout)

        self.worker = Worker()
        self.Thread = QtCore.QThread()
        self.worker.moveToThread(self.Thread)
        self.Thread.start()

        self.btnInThread.clicked.connect(self.worker.work)

        # Use a signal mapper
        # self.mapper = QtCore.QSignalMapper()
        # self.mapper.setMapping(self.btnNotInThread, 1)
        # self.mapper.mapped.connect(self.worker.work)
        # self.btnNotInThread.clicked.connect(self.mapper.map)

        # Alternative mapper method from above
        callback, self.mapper = create_map(self.btnNotInThread, self.worker.work, 1)
        self.btnNotInThread.clicked.connect(callback)

        self.show()
        print('{0} <- Main Thread'.format(self.thread().currentThread()))


def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
从PySide导入QtCore
从PySide导入QtGui
导入系统
导入时间
def create_映射(对象、函数、参数=无):
“”“创建信号映射器以将值与函数关联。
Args:
obj(QObject):使用信号映射器将值映射到的对象
func(function):调用signal mapper.map函数时运行的函数。
args(tuple)[None]:要传递给函数的参数。
返回:
map_回调(函数):连接到信号的map函数。
映射器(QSignalMapper):您可能需要保留信号映射器对象的引用。
"""
mapper=QtCore.QSignalMapper()
mapper.setMapping(对象,参数)
mapper.mapped.connect(func)
返回mapper.map,mapper
类工作者(QtCore.QObject):
定义初始化(自):
super()。\uuuu init\uuuuu()
def功(自身,值=0):
打印(self.thread().currentThread())
时间。睡眠(2)
打印(“结束”,值)
类示例(QtGui.QWidget):
定义初始化(自):
super()。\uuuu init\uuuuu()
self.btnInThread=QtGui.QPushButton('在线程中')
self.btnnointhread=QtGui.QPushButton('不在线程中')
layout=QtGui.QVBoxLayout()
layout.addWidget(self.btnInThread)
layout.addWidget(self.btnnointhread)
self.setLayout(布局)
self.worker=worker()
self.Thread=QtCore.QThread()
self.worker.moveToThread(self.Thread)
self.Thread.start()
self.btnInThread.clicked.connect(self.worker.work)
#使用信号映射器
#self.mapper=QtCore.QSignalMapper()
#self.mapper.setMapping(self.btnNotInThread,1)
#self.mapper.mapped.connect(self.worker.work)
#self.btnnointhread.clicked.connect(self.mapper.map)
#从上面选择映射器方法
回调,self.mapper=create_map(self.btnnointhread,self.worker.work,1)
self.btnnointhread.clicked.connect(回调)
self.show()

打印({0}我对线程的印象是,代码必须在run方法中才能在线程中运行。否则,我觉得代码应该始终在main中,特别是当您从信号触发
work
时。我认为信号总是在主线程中运行代码,除非您将信号设置为使用直接连接。我在使用Python 3.4 PySide 1.2.2的Windows上,您的代码刚刚崩溃,所以我帮不了什么忙。我更新了我的代码。在Windows下不再崩溃可能的重复