@@ -233,18 +233,71 @@ 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+ } else if (PyMethod_Check (object)) {
251+ PyObject* instance = PyMethod_Self (object);
252+ if (PyObject_TypeCheck (instance, &PythonQtInstanceWrapper_Type)) {
253+ PythonQtInstanceWrapper* typedInstance = (PythonQtInstanceWrapper*)instance;
254+ if (!typedInstance->_wrappedPtr ) {
255+ // It's a QObject-derived class
256+ targetObj = typedInstance->_obj ;
257+ PyObject* function = PyMethod_Function (object);
258+ if (PyObject_HasAttr (function, qtSlots)) {
259+ // connecting to a slot
260+ PyObject* signatures = PyObject_GetAttr (function, qtSlots);
261+ Py_ssize_t count = PyList_Size (signatures);
262+ // TODO: Find the best matching signature;
263+ // currently we only connect to the actual slot if only one slot signature is associated
264+ // with this callable - if there are more, we just connect to the callable and let
265+ // the callable figure the arguments out - but in this case Qt::DirectConnection is
266+ // used instead of Qt::AutoConnection, which can be suprising if threads are involved.
267+ if (count == 1 ) {
268+ PyObject* signature = PyList_GET_ITEM (signatures, 0 );
269+ // Retrieve slot signature
270+ QByteArray sig = PyUnicode_AsUTF8 (signature);
271+ targetSignature = QByteArray (" 1" ) + sig.split (' ' )[1 ]; // include slot prefix
272+ return true ;
273+ }
274+ }
275+ }
276+ }
277+ }
278+ return false ;
279+ }
280+
236281static PyObject* PythonQtSignalFunction_connect (PythonQtSignalFunctionObject* type, PyObject* args)
237282{
238283 if (PyObject_TypeCheck (type->m_self , &PythonQtInstanceWrapper_Type)) {
239284 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)type->m_self ;
240285 if (self->_obj ) {
241286 Py_ssize_t argc = PyTuple_Size (args);
287+ QByteArray sourceSignature = QByteArray (" 2" ) + type->m_ml ->signature ();
242288 if (argc == 1 ) {
243289 // connect with Python callable
244290 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);
291+ QObject* targetObj;
292+ QByteArray targetSignature;
293+ if (extractSignalTarget (callable, targetObj, targetSignature)) {
294+ // Do a regular signal/slot (or signal/signal) connect.
295+ QObject::connect (self->_obj , sourceSignature, targetObj, targetSignature, Qt::AutoConnection);
296+ return PythonQtConv::GetPyBool (true );
297+ } else {
298+ bool result = PythonQt::self ()->addSignalHandler (self->_obj , sourceSignature, callable);
299+ return PythonQtConv::GetPyBool (result);
300+ }
248301 } else {
249302 PyErr_SetString (PyExc_ValueError, " Called connect with wrong number of arguments" );
250303 }
@@ -259,15 +312,23 @@ static PyObject* PythonQtSignalFunction_disconnect(PythonQtSignalFunctionObject*
259312 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)type->m_self ;
260313 if (self->_obj ) {
261314 Py_ssize_t argc = PyTuple_Size (args);
262- QByteArray signal = QByteArray (" 2" ) + type->m_ml ->signature ();
315+ QByteArray sourceSignature = QByteArray (" 2" ) + type->m_ml ->signature ();
263316 if (argc == 1 ) {
264317 // disconnect with Python callable
265318 PyObject* callable = PyTuple_GET_ITEM (args, 0 );
266- bool result = PythonQt::self ()->removeSignalHandler (self->_obj , signal, callable);
267- return PythonQtConv::GetPyBool (result);
319+ QObject* targetObj;
320+ QByteArray targetSignature;
321+ if (extractSignalTarget (callable, targetObj, targetSignature)) {
322+ // Do a regular signal/slot (or signal/signal) disconnect.
323+ QObject::disconnect (self->_obj , sourceSignature, targetObj, targetSignature);
324+ return PythonQtConv::GetPyBool (true );
325+ } else {
326+ bool result = PythonQt::self ()->removeSignalHandler (self->_obj , sourceSignature, callable);
327+ return PythonQtConv::GetPyBool (result);
328+ }
268329 } else if (argc == 0 ) {
269- bool result = PythonQt::self ()->removeSignalHandler (self->_obj , signal , nullptr );
270- result |= QObject::disconnect (self->_obj , signal , nullptr , nullptr );
330+ bool result = PythonQt::self ()->removeSignalHandler (self->_obj , sourceSignature , nullptr );
331+ result |= QObject::disconnect (self->_obj , sourceSignature , nullptr , nullptr );
271332 return PythonQtConv::GetPyBool (result);
272333 } else {
273334 PyErr_SetString (PyExc_ValueError, " Called disconnect with wrong number of arguments" );
0 commit comments