From ed0b262de97dd92c831127909ea4c059962b86ce Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 18 Nov 2011 10:57:04 +0100 Subject: QObject::disconnect with new syntax This add an overload to disconnect which is symetrical to the new syntax of connect. It is possible to diconnect connection like this: QObject::connect( sender, &Sender::valueChanged, receiver, &Receiver::updateValue ); QObject::disconnect( sender, &Sender::valueChanged, receiver, &Receiver::updateValue ); This overload only work with pointer to member function, and not static functions or functors. The test is copied from tst_QObject::disconnect(), just changed the syntax of the connection and disconnection Change-Id: Ia8f819100cb12098e32877522b97b732b1e676a8 Reviewed-by: Bradley T. Hughes --- .../snippets/code/src_corelib_kernel_qobject.cpp | 7 ++ src/corelib/kernel/qmetaobject_p.h | 6 +- src/corelib/kernel/qobject.cpp | 109 +++++++++++++++++++-- src/corelib/kernel/qobject.h | 35 +++++++ tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 105 ++++++++++++++++++++ 5 files changed, 249 insertions(+), 13 deletions(-) diff --git a/doc/src/snippets/code/src_corelib_kernel_qobject.cpp b/doc/src/snippets/code/src_corelib_kernel_qobject.cpp index 11b70cc1ab..b6d0e39ab0 100644 --- a/doc/src/snippets/code/src_corelib_kernel_qobject.cpp +++ b/doc/src/snippets/code/src_corelib_kernel_qobject.cpp @@ -470,7 +470,14 @@ QObject::connect(socket, &QTcpSocket::connected, [=] () { }); //! [46] +//! [47] +disconnect(myObject, &MyObject::mySignal(), 0, 0); +//! [47] +//! [48] +QObject::disconnect(lineEdit, &QLineEdit::textChanged, + label, &QLabel::setText); +//! [48] //! [meta data] //: This is a comment for the translator. diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 0476558a92..b99907e822 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -142,11 +142,11 @@ struct QMetaObjectPrivate const QMetaObject *rmeta = 0, int type = 0, int *types = 0); static bool disconnect(const QObject *sender, int signal_index, - const QObject *receiver, int method_index, + const QObject *receiver, int method_index, void **slot, DisconnectType = DisconnectAll); static inline bool disconnectHelper(QObjectPrivate::Connection *c, - const QObject *receiver, int method_index, - QMutex *senderMutex, DisconnectType); + const QObject *receiver, int method_index, void **slot, + QMutex *senderMutex, DisconnectType = DisconnectAll); #endif }; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 2be0a7c759..fc7df79e19 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2758,7 +2758,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal, } if (!method) { - res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1); + res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1, 0); } else { const QMetaObject *rmeta = receiver->metaObject(); do { @@ -2768,7 +2768,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal, rmeta = rmeta->superClass(); if (method_index < 0) break; - res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index); + res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index, 0); method_found = true; } while ((rmeta = rmeta->superClass())); } @@ -2881,7 +2881,7 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, return false; } - if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index)) + if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index, 0)) return false; const_cast(sender)->disconnectNotify(method.mobj ? signalSignature.constData() : 0); @@ -3072,7 +3072,7 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, { signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); return QMetaObjectPrivate::disconnect(sender, signal_index, - receiver, method_index); + receiver, method_index, 0); } /*!\internal @@ -3086,7 +3086,7 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index, { signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); return QMetaObjectPrivate::disconnect(sender, signal_index, - receiver, method_index, + receiver, method_index, 0, QMetaObjectPrivate::DisconnectOne); } @@ -3094,14 +3094,15 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index, Helper function to remove the connection from the senders list and setting the receivers to 0 */ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, - const QObject *receiver, int method_index, + const QObject *receiver, int method_index, void **slot, QMutex *senderMutex, DisconnectType disconnectType) { bool success = false; while (c) { if (c->receiver && (receiver == 0 || (c->receiver == receiver - && (method_index < 0 || c->method() == method_index)))) { + && (method_index < 0 || c->method() == method_index) + && (slot == 0 || (c->isSlotObject && c->slotObj->compare(slot)))))) { bool needToUnlock = false; QMutex *receiverMutex = 0; if (!receiver) { @@ -3134,7 +3135,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex */ bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index, - const QObject *receiver, int method_index, + const QObject *receiver, int method_index, void **slot, DisconnectType disconnectType) { if (!sender) @@ -3159,7 +3160,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index, for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) { QObjectPrivate::Connection *c = (*connectionLists)[signal_index].first; - if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) { + if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) { success = true; connectionLists->dirty = true; } @@ -3167,7 +3168,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index, } else if (signal_index < connectionLists->count()) { QObjectPrivate::Connection *c = (*connectionLists)[signal_index].first; - if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) { + if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) { success = true; connectionLists->dirty = true; } @@ -4212,6 +4213,88 @@ bool QObject::disconnect(const QMetaObject::Connection &connection) return true; } +/*! \fn bool QObject::disconnect(const QObject *sender, (T::*signal)(...), const Qbject *receiver, (T::*method)(...)) + \threadsafe + \overload + + Disconnects \a signal in object \a sender from \a method in object + \a receiver. Returns true if the connection is successfully broken; + otherwise returns false. + + A signal-slot connection is removed when either of the objects + involved are destroyed. + + disconnect() is typically used in three ways, as the following + examples demonstrate. + \list 1 + \i Disconnect everything connected to an object's signals: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 26 + + \i Disconnect everything connected to a specific signal: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 47 + + \i Disconnect a specific receiver: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 30 + + \i Disconnect a connection from one specific signal to a specific slot: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 48 + + + \endlist + + 0 may be used as a wildcard, meaning "any signal", "any receiving + object", or "any slot in the receiving object", respectively. + + The \a sender may never be 0. (You cannot disconnect signals from + more than one object in a single call.) + + If \a signal is 0, it disconnects \a receiver and \a method from + any signal. If not, only the specified signal is disconnected. + + If \a receiver is 0, it disconnects anything connected to \a + signal. If not, slots in objects other than \a receiver are not + disconnected. + + If \a method is 0, it disconnects anything that is connected to \a + receiver. If not, only slots named \a method will be disconnected, + and all other slots are left alone. The \a method must be 0 if \a + receiver is left out, so you cannot disconnect a + specifically-named slot on all objects. + + \note It is not possible to use this overload to diconnect signals + connected to functors or lambda expressions. That is because it is not + possible to compare them. Instead, use the olverload that take a + QMetaObject::Connection + + \sa connect() +*/ +bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot, const QMetaObject *senderMetaObject) +{ + if (sender == 0 || (receiver == 0 && slot != 0)) { + qWarning("Object::disconnect: Unexpected null parameter"); + return false; + } + + int signal_index = -1; + if (signal) { + void *args[] = { &signal_index, signal }; + senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); + if (signal_index < 0 || signal_index >= QMetaObjectPrivate::get(senderMetaObject)->signalCount) { + qWarning("QObject::disconnect: signal not found in %s", senderMetaObject->className()); + return false; + } + int signalOffset, methodOffset; + computeOffsets(senderMetaObject, &signalOffset, &methodOffset); + signal_index += signalOffset; + } + + return QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1, slot); +} + /*! \class QMetaObject::Connection Represents a handle to a signal-slot connection. It can be used to disconnect that connection, or check if @@ -4263,6 +4346,12 @@ QObject::QSlotObjectBase::~QSlotObjectBase() { } +bool QObject::QSlotObjectBase::compare(void** ) +{ + return false; +} + + QT_END_NAMESPACE #include "moc_qobject.cpp" diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 27165cdcc3..3b8803c1a6 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -273,6 +273,33 @@ public: { return disconnect(this, 0, receiver, member); } static bool disconnect(const QMetaObject::Connection &); + template + static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object *receiver, Func2 slot) + { + typedef QtPrivate::FunctionPointer SignalType; + typedef QtPrivate::FunctionPointer SlotType; + reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(0)); + + //compilation error if the arguments does not match. + typedef typename QtPrivate::CheckCompatibleArguments::IncompatibleSignalSlotArguments EnsureCompatibleArguments; + return disconnectImpl(sender, reinterpret_cast(&signal), receiver, reinterpret_cast(&slot), + &SignalType::Object::staticMetaObject); + } + template + static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, + const QObject *receiver, void **zero) + { + // This is the overload for when one wish to disconnect a signal from any slot. (slot=0) + // Since the function template parametter cannot be deduced from '0', we use a + // dummy void ** parametter that must be equal to 0 + Q_ASSERT(!zero); + typedef QtPrivate::FunctionPointer SignalType; + return disconnectImpl(sender, reinterpret_cast(&signal), receiver, zero, + &SignalType::Object::staticMetaObject); + } + + void dumpObjectTree(); void dumpObjectInfo(); @@ -340,6 +367,7 @@ private: QSlotObjectBase() : ref(1) {} virtual ~QSlotObjectBase(); virtual void call(QObject *receiver, void **a) = 0; + virtual bool compare(void **); }; // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. @@ -351,6 +379,9 @@ private: virtual void call(QObject *receiver, void **a) { FuncType::template call(function, static_cast(receiver), a); } + virtual bool compare(void **f) { + return *reinterpret_cast(f) == function; + } }; // implementation of QSlotObjectBase for which the slot is a static function // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. @@ -378,6 +409,10 @@ private: static QMetaObject::Connection connectImpl(const QObject *sender, void **signal, const QObject *receiver, QSlotObjectBase *slot, Qt::ConnectionType type, const int *types, const QMetaObject *senderMetaObject); + + static bool disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot, + const QMetaObject *senderMetaObject); + }; inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal, diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 77e9a9e42d..f5d06d27ee 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -122,6 +122,7 @@ private slots: void autoConnectionBehavior(); void baseDestroyed(); void pointerConnect(); + void pointerDisconnect(); void emitInDefinedOrderPointer(); void customTypesPointer(); void connectCxx0x(); @@ -4104,6 +4105,110 @@ void tst_QObject::pointerConnect() delete r2; } +void tst_QObject::pointerDisconnect() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + + connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 ); + + connect( s, &SenderObject::signal2, r1, &ReceiverObject::slot2 ); + connect( s, &SenderObject::signal3, r1, &ReceiverObject::slot3 ); + connect( s, &SenderObject::signal4, r1, &ReceiverObject::slot4 ); + + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->called(1), TRUE ); + QCOMPARE( r1->called(2), TRUE ); + QCOMPARE( r1->called(3), TRUE ); + QCOMPARE( r1->called(4), TRUE ); + r1->reset(); + + // usual disconnect with all parameters given + bool ret = QObject::disconnect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 ); + + s->emitSignal1(); + + QCOMPARE( r1->called(1), FALSE ); + r1->reset(); + + QCOMPARE( ret, TRUE ); + ret = QObject::disconnect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 ); + QCOMPARE( ret, FALSE ); + + // disconnect all signals from s from all slots from r1 + QObject::disconnect( s, 0, r1, 0 ); + + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->called(2), FALSE ); + QCOMPARE( r1->called(3), FALSE ); + QCOMPARE( r1->called(4), FALSE ); + r1->reset(); + + connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 ); + connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot2 ); + connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot3 ); + connect( s, &SenderObject::signal2, r1, &ReceiverObject::slot4 ); + + // disconnect s's signal1() from all slots of r1 + QObject::disconnect( s, &SenderObject::signal1, r1, 0 ); + + s->emitSignal1(); + s->emitSignal2(); + + QCOMPARE( r1->called(1), FALSE ); + QCOMPARE( r1->called(2), FALSE ); + QCOMPARE( r1->called(3), FALSE ); + QCOMPARE( r1->called(4), TRUE ); + r1->reset(); + // make sure all is disconnected again + QObject::disconnect( s, 0, r1, 0 ); + + connect( s, &SenderObject::signal1, r1, &ReceiverObject::slot1 ); + connect( s, &SenderObject::signal1, r2, &ReceiverObject::slot1 ); + connect( s, &SenderObject::signal2, r1, &ReceiverObject::slot2 ); + connect( s, &SenderObject::signal2, r2, &ReceiverObject::slot2 ); + connect( s, &SenderObject::signal3, r1, &ReceiverObject::slot3 ); + connect( s, &SenderObject::signal3, r2, &ReceiverObject::slot3 ); + + // disconnect signal1() from all receivers + QObject::disconnect( s, &SenderObject::signal1, 0, 0 ); + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + + QCOMPARE( r1->called(1), FALSE ); + QCOMPARE( r2->called(1), FALSE ); + QCOMPARE( r1->called(2), TRUE ); + QCOMPARE( r2->called(2), TRUE ); + QCOMPARE( r1->called(2), TRUE ); + QCOMPARE( r2->called(2), TRUE ); + + r1->reset(); + r2->reset(); + + // disconnect all signals of s from all receivers + QObject::disconnect( s, 0, 0, 0 ); + + QCOMPARE( r1->called(2), FALSE ); + QCOMPARE( r2->called(2), FALSE ); + QCOMPARE( r1->called(2), FALSE ); + QCOMPARE( r2->called(2), FALSE ); + + delete r2; + delete r1; + delete s; + +} + + void tst_QObject::emitInDefinedOrderPointer() { SenderObject sender; -- cgit v1.2.3