[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?
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import functools
import logging
from PyQt4 import QtCore
logging.basicConfig(level=logging.DEBUG)
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):
QtCore.QObject.__init__(self)
real_code_object = functools_partial_instance.func
t = real_code_object.im_self.thread()
self.moveToThread(t)
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:
kw.update(self._kwargs)
self._obj(*(self._args + args), **kw)
app = CApp()
app_thread = app.thread()
logger.info("Main thread ID %s", QtCore.QThread.currentThreadId())
thread = QtCore.QThread()
thread.start()
while not thread.isRunning():
pass
a = CReceiver()
a.moveToThread(thread)
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.Qt.QueuedConnection)
QtCore.QObject.connect(app, app.signature_noargs, receiver_partial_proxy,
QtCore.Qt.QueuedConnection)
timer = QtCore.QTimer()
timer.setInterval(1000)
timer.timeout.connect(app.emit_noargs)
timer.start()
app.exec_()
--
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