@@ -233,18 +233,72 @@ static PyObject* PythonQtSignalFunction_typeName(PythonQtSignalFunctionObject* t
233233 return PythonQtMemberFunction_typeName (type->m_ml );
234234}
235235
236+ // Find out if we can connect directly to the given Python object, without creating a separate receiver.
237+ // If yes, provide target object and signature for the connect call.
238+ static bool extractSignalTarget (PyObject* object, QObject*& targetObj, QByteArray& targetSignature)
239+ {
240+ static PyObject* qtSlots = PyUnicode_FromString (" _qtSlots" );
241+ if (PyObject_TypeCheck (object, &PythonQtSignalFunction_Type)) {
242+ PythonQtSignalFunctionObject* type = (PythonQtSignalFunctionObject*)object;
243+ PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)type->m_self ;
244+ if (self->_obj ) {
245+ // connecting to another signal
246+ targetObj = self->_obj ;
247+ targetSignature = QByteArray (" 2" ) + type->m_ml ->signature ();
248+ return true ;
249+ }
250+ }
251+ else if (PyMethod_Check (object)) {
252+ PyObject* instance = PyMethod_Self (object);
253+ if (PyObject_TypeCheck (instance, &PythonQtInstanceWrapper_Type)) {
254+ PythonQtInstanceWrapper* typedInstance = (PythonQtInstanceWrapper*)instance;
255+ if (!typedInstance->_wrappedPtr ) {
256+ // It's a QObject-derived class
257+ targetObj = typedInstance->_obj ;
258+ PyObject* function = PyMethod_Function (object);
259+ if (PyObject_HasAttr (function, qtSlots)) {
260+ // connecting to a slot
261+ PyObject* signatures = PyObject_GetAttr (function, qtSlots);
262+ Py_ssize_t count = PyList_Size (signatures);
263+ // TODO: Find the best matching signature;
264+ // currently we only connect to the actual slot if only one slot signature is associated
265+ // with this callable - if there are more, we just connect to the callable and let
266+ // the callable figure the arguments out - but in this case Qt::DirectConnection is
267+ // used instead of Qt::AutoConnection, which can be suprising if threads are involved.
268+ if (count == 1 ) {
269+ PyObject* signature = PyList_GET_ITEM (signatures, 0 );
270+ // Retrieve slot signature
271+ QByteArray sig = PyUnicode_AsUTF8 (signature);
272+ targetSignature = QByteArray (" 1" ) + sig.split (' ' )[1 ]; // include slot prefix
273+ return true ;
274+ }
275+ }
276+ }
277+ }
278+ }
279+ return false ;
280+ }
281+
236282static PyObject* PythonQtSignalFunction_connect (PythonQtSignalFunctionObject* type, PyObject* args)
237283{
238284 if (PyObject_TypeCheck (type->m_self , &PythonQtInstanceWrapper_Type)) {
239285 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)type->m_self ;
240286 if (self->_obj ) {
241287 Py_ssize_t argc = PyTuple_Size (args);
288+ QByteArray sourceSignature = QByteArray (" 2" ) + type->m_ml ->signature ();
242289 if (argc == 1 ) {
243290 // connect with Python callable
244291 PyObject* callable = PyTuple_GET_ITEM (args, 0 );
245- bool result =
246- PythonQt::self ()->addSignalHandler (self->_obj , QByteArray (" 2" ) + type->m_ml ->signature (), callable);
247- return PythonQtConv::GetPyBool (result);
292+ QObject* targetObj;
293+ QByteArray targetSignature;
294+ if (extractSignalTarget (callable, targetObj, targetSignature)) {
295+ // Do a regular signal/slot (or signal/signal) connect.
296+ QObject::connect (self->_obj , sourceSignature, targetObj, targetSignature, Qt::AutoConnection);
297+ return PythonQtConv::GetPyBool (true );
298+ } else {
299+ bool result = PythonQt::self ()->addSignalHandler (self->_obj , sourceSignature, callable);
300+ return PythonQtConv::GetPyBool (result);
301+ }
248302 } else {
249303 PyErr_SetString (PyExc_ValueError, " Called connect with wrong number of arguments" );
250304 }
@@ -259,15 +313,23 @@ static PyObject* PythonQtSignalFunction_disconnect(PythonQtSignalFunctionObject*
259313 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)type->m_self ;
260314 if (self->_obj ) {
261315 Py_ssize_t argc = PyTuple_Size (args);
262- QByteArray signal = QByteArray (" 2" ) + type->m_ml ->signature ();
316+ QByteArray sourceSignature = QByteArray (" 2" ) + type->m_ml ->signature ();
263317 if (argc == 1 ) {
264318 // disconnect with Python callable
265319 PyObject* callable = PyTuple_GET_ITEM (args, 0 );
266- bool result = PythonQt::self ()->removeSignalHandler (self->_obj , signal, callable);
267- return PythonQtConv::GetPyBool (result);
320+ QObject* targetObj;
321+ QByteArray targetSignature;
322+ if (extractSignalTarget (callable, targetObj, targetSignature)) {
323+ // Do a regular signal/slot (or signal/signal) disconnect.
324+ QObject::disconnect (self->_obj , sourceSignature, targetObj, targetSignature);
325+ return PythonQtConv::GetPyBool (true );
326+ } else {
327+ bool result = PythonQt::self ()->removeSignalHandler (self->_obj , sourceSignature, callable);
328+ return PythonQtConv::GetPyBool (result);
329+ }
268330 } else if (argc == 0 ) {
269- bool result = PythonQt::self ()->removeSignalHandler (self->_obj , signal , nullptr );
270- result |= QObject::disconnect (self->_obj , signal , nullptr , nullptr );
331+ bool result = PythonQt::self ()->removeSignalHandler (self->_obj , sourceSignature , nullptr );
332+ result |= QObject::disconnect (self->_obj , sourceSignature , nullptr , nullptr );
271333 return PythonQtConv::GetPyBool (result);
272334 } else {
273335 PyErr_SetString (PyExc_ValueError, " Called disconnect with wrong number of arguments" );
0 commit comments