Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 连接到Qt信号don';在不同的线程中创建时不会运行_Python_Multithreading_Qt_Lambda - Fatal编程技术网

Python 连接到Qt信号don';在不同的线程中创建时不会运行

Python 连接到Qt信号don';在不同的线程中创建时不会运行,python,multithreading,qt,lambda,Python,Multithreading,Qt,Lambda,当在不同的线程中创建QObject并使用QObject.moveToThread将其移回主线程时,lambda发出“断开连接”的信号(它们不会触发)。我的猜测是,常规插槽链接到移动到主线程的QObject,因此它们在主线程的事件循环中运行,但是lambda函数没有链接到QObject,因此它们没有要运行的事件循环 这可以在以下简短的Python代码中看到: if __name__ == "__main__": from traits.etsconfig.api import ETSCon

当在不同的线程中创建
QObject
并使用
QObject.moveToThread
将其移回主线程时,
lambda
发出“断开连接”的信号(它们不会触发)。我的猜测是,常规插槽链接到移动到主线程的
QObject
,因此它们在主线程的事件循环中运行,但是
lambda
函数没有链接到
QObject
,因此它们没有要运行的事件循环

这可以在以下简短的Python代码中看到:

if __name__ == "__main__":
    from traits.etsconfig.api import ETSConfig
    ETSConfig.toolkit = 'qt4'

import threading
from PySide import QtGui, QtCore

class MyObject(QtCore.QObject):
    def __init__(self, button):
        super(MyObject, self).__init__()
        button.clicked.connect(self.mySlot)
        button.clicked.connect(lambda: self.mySlot('lambda'))
    #
    def mySlot(self, printing='object'):
        print printing
#
myObj = None
# global variable to keep it in memory
def myThread(mainThread, button):
    global myObj
    myObj = MyObject(button)
    myObj.moveToThread(mainThread)
#
if __name__ == '__main__':
    appQT = QtGui.QApplication([])
    #
    myWidget = QtGui.QWidget()
    myButton = QtGui.QPushButton('Press to see output')
    myLayout = QtGui.QVBoxLayout(myWidget)
    myLayout.addWidget(myButton)
    myWidget.show()
    #
    mainThread = QtCore.QThread.currentThread()
    if True:
        # run myThread in a new thread
        # prints only the first slot (object slot)
        threading.Thread(target=myThread, args=[mainThread, myButton]).start()
    else:
        # run myThread in this thread
        # prints both slots (object and lambda slots)
        myThread(mainThread, myButton)
    #
    appQT.exec_()
通过将条件从
True
更改为
False
,您可以看到结果与预期的不同

当设置为
True
时,单击按钮后的输出为:

object
object
lambda
当设置为
False
时,单击按钮后的输出为:

object
object
lambda
我的问题是,是否有人能更准确地解释它为何会以这种方式运行,以及在将
QObject
移回主线程时是否有一种简单的方法来保持
lambda
插槽工作?

好的,我了解了一些有关发生了什么的细节。这仍然是一个部分答案,但我认为它更适合作为一个答案,而不是更新的问题

在我最初的问题中,我似乎是正确的,即插槽链接到其实例对象的
QObject
事件循环(线程),但前提是该插槽是绑定方法(具有对象实例)

如果查看,您将看到它根据接收的插槽类型定义接收器(接收信号的
QObject
)。因此,如果您将
QtSignal.connect()
函数传递给绑定对象方法,则接收器被定义为(即
PyMethod\u GET\u SELF(回调)
)。如果您向它传递一个未绑定的通用可调用对象(例如
lambda
函数)(无
\uuuuuu self\uuuuu
属性),则接收器只需设置为
NULL
。接收器告诉Qt要将信号发送到哪个事件循环,因此如果它是
NULL
,它不知道要将信号发送到主事件循环

以下是PySide源代码的一个片段:

static bool getReceiver(QObject *source, const char* signal, PyObject* callback, QObject** receiver, PyObject** self, QByteArray* callbackSig)
{
    bool forceGlobalReceiver = false;
    if (PyMethod_Check(callback)) {
        *self = PyMethod_GET_SELF(callback);
        if (%CHECKTYPE[QObject*](*self))
            *receiver = %CONVERTTOCPP[QObject*](*self);
        forceGlobalReceiver = isDecorator(callback, *self);
    } else if (PyCFunction_Check(callback)) {
        *self = PyCFunction_GET_SELF(callback);
        if (*self && %CHECKTYPE[QObject*](*self))
            *receiver = %CONVERTTOCPP[QObject*](*self);
    } else if (PyCallable_Check(callback)) {
        // Ok, just a callable object
        *receiver = 0;
        *self = 0;
    }

    ...
    ...
}
这是否有助于我们解决
lambda
函数的问题?不是真的。。。如果我们使用以下方法(使用
types.MethodType
)绑定
lambda
函数,则行为不会改变:

import types

class MyObject(QtCore.QObject):
    def __init__(self, button):
        super(MyObject, self).__init__()
        button.clicked.connect(self.mySlot)
        thisLambda = lambda self=self : self.mySlot('hello')
        self.myLambda = types.MethodType( thisLambda, self )
        button.clicked.connect(self.myLambda)
    #
    def mySlot(self, printing='object'):
        print printing
输出:

object
object
global2
这种绑定肯定是问题的一部分,因为我在下面演示了非绑定全局方法也会出现相同的行为,通过使用
types.MethodType()
绑定它们,它解决了问题:

import types

def abc(self):
    print 'global1'

def xyz(self):
    print 'global2'

class MyObject(QtCore.QObject):
    def __init__(self, button):
        super(MyObject, self).__init__()
        button.clicked.connect(self.mySlot)
        self.xyz = types.MethodType( xyz, self )
        button.clicked.connect(abc)
        button.clicked.connect(self.xyz)
    #
    def mySlot(self, printing='object'):
        print printing
输出:

object
object
global2

无论如何,最简单的解决方案似乎就是首先不要在单独的线程中创建QObject,但这个答案是理解它为什么不能正常工作的一个步骤。

尝试用
lambda-self=self:self.mySlot('lambda')
替换
lambda-self=self.mySlot('lambda')
;我认为您遇到了范围问题。谢谢chepner,我刚刚尝试了它,但它给出了相同的结果,所以这似乎不是问题所在。从未在Qt中使用Python,甚至Python本身使用得太多,但您不认为lambda连接是通过一些隐藏的QObject实现的吗?尝试将事件循环添加到该线程中,它可能会出现。另外,你可以用GDB来探索它,Qt C++代码非常可调试,特别是如果你在Debian之类的东西上。谢谢Velkan的评论,这是一个很好的下一步,把Python线程变成QPosits,看看会发生什么。有机会的话我会试试的。