[PyQt] moveToThread() does not cope with functools.partial

Giuseppe Corbelli giuseppe.corbelli at copanitalia.com
Mon Apr 29 12:44:09 BST 2013

On 23/04/2013 17:52, Phil Thompson wrote:
>>>> Please see the minimal test code below. In short connecting a signal
> to
>>> a
>>>> functools.partial slot breaks the expected moveToThread behaviour.
>>>> Reasonable, I'd say, once you think about it. I'm just asking some
>>>> suggestion
>>>> about how this issue can be addressed. I thought about reimplementing
>>> some
>>>> signal class with a connect_partial method that stores bound args.
>>>> Python 2.7.3, Qt 4.8.2, PyQt 4.10 on Linux.
>>> Should be fixed in tonight's PyQt4 snapshot.
>> I'm curious to understand how you fixed it. Can I ask for a diff or some
>> other
>> pointer to look at?
> Look at get_receiver() in qpy/QtCore/qpycore_pyqtboundsignal.cpp. It just
> strips off the functools.partial wrappers until it gets to the QObject. The
> proxy that is created is then moved to the same thread as the QObject.

Works correctly with pyqtSignal.
I'm not able to reproduce the desired behaviour with QtCore.QObject.connect.
Any suggestion? What I'm missing?

# -*- coding: utf-8 -*-

import sys
import functools
import logging

from PyQt4 import QtCore


logger = logging.getLogger('blah')

class CApp(QtCore.QCoreApplication):
     def __init__(self):
         QtCore.QCoreApplication.__init__(self, sys.argv)
         self.signature_noargs = QtCore.SIGNAL("signature_noargs()")
         self.signature_1arg = QtCore.SIGNAL("signature_1arg(PyQt_PyObject)")

     def emit_noargs(self):
         QtCore.QCoreApplication.emit(self, self.signature_noargs)

     def emit_1arg(self, *args):
         QtCore.QCoreApplication.emit(self, self.signature_1arg, *args)

class CReceiver(QtCore.QObject):
     def slot_noargs(self):
         logger.debug("slot_noargs %s", QtCore.QThread.currentThreadId())

     def slot_1arg(self, arg):
         logger.debug("slot_1arg %s %s", arg, QtCore.QThread.currentThreadId())

class CPartialProxy(QtCore.QObject):
     def __init__(self, functools_partial_instance):
         real_code_object = functools_partial_instance.func
         t = real_code_object.im_self.thread()
         self._obj = real_code_object
         self._args = functools_partial_instance.args
         self._kwargs = functools_partial_instance.keywords

     def __call__(self, *args, **kwargs):
         kw = dict(kwargs)
         if self._kwargs is not None:
         self._obj(*(self._args + args), **kw)

app = CApp()
app_thread = app.thread()
logger.info("Main thread ID %s", QtCore.QThread.currentThreadId())

thread = QtCore.QThread()
while not thread.isRunning():

a = CReceiver()

receiver_partial = functools.partial(a.slot_1arg, "Partial ARG")
receiver_partial_proxy = CPartialProxy(receiver_partial)

QtCore.QObject.connect(app, app.signature_noargs, a.slot_noargs, 
QtCore.QObject.connect(app, app.signature_noargs, receiver_partial_proxy, 

timer = QtCore.QTimer()


             Giuseppe Corbelli
WASP Software Engineer, Copan Italia S.p.A
Phone: +390303666318  Fax: +390302659932
E-mail: giuseppe.corbelli at copanitalia.com

More information about the PyQt mailing list