From 0ea19cf01e2381969a8b8ce8cdaffe9ce873d3a9 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 25 May 2009 22:47:06 +0200 Subject: Add a flag that ensure that a connection is made only one It is often desirable, when doing connection, to ensure that the same connection is only made once. This can be done with the Qt::UniqueConnection 'flag' Also documented the order the slot are called Reviewed-by: Brad --- doc/src/qnamespace.qdoc | 4 ++ doc/src/signalsandslots.qdoc | 14 ++++--- src/corelib/global/qnamespace.h | 3 +- src/corelib/kernel/qobject.cpp | 36 ++++++++++++----- tests/auto/qobject/tst_qobject.cpp | 81 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 119 insertions(+), 19 deletions(-) diff --git a/doc/src/qnamespace.qdoc b/doc/src/qnamespace.qdoc index fc4310b..069541f 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/qnamespace.qdoc @@ -528,6 +528,10 @@ Qt::DirectConnection; otherwise the signal is queued, as with Qt::QueuedConnection. + \value UniqueConnection Same as AutoConnection, but there will be a check that the signal is + not already connected to the same slot before connecting, otherwise, + the connection will fail. + \since 4.6 With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them diff --git a/doc/src/signalsandslots.qdoc b/doc/src/signalsandslots.qdoc index 5432bd4..29c8215 100644 --- a/doc/src/signalsandslots.qdoc +++ b/doc/src/signalsandslots.qdoc @@ -178,9 +178,13 @@ looping in the case of cyclic connections (e.g., if \c{b.valueChanged()} were connected to \c{a.setValue()}). - A signal is emitted for every connection you make; if you - duplicate a connection, two signals will be emitted. You can - always break a connection using QObject::disconnect(). + By default, for every connection you make, a signal is emitted; + two signals are emitted for duplicate connections. You can break + all of these connections with a single disconnect() call. + If you pass the Qt::UniqueConnection \a type, the connection will only + be made if it is not a duplicate. If there is already a duplicate + (exact same signal to the exact same slot on the same objects), + the connection will fail and connect will return false This example illustrates that objects can work together without needing to know any information about each other. To enable this, the objects only @@ -218,8 +222,8 @@ will continue immediately, and the slots will be executed later. If several slots are connected to one signal, the slots will be - executed one after the other, in an arbitrary order, when the signal - is emitted. + executed one after the other, in the order they have been connected, + when the signal is emitted. Signals are automatically generated by the \l moc and must not be implemented in the \c .cpp file. They can never have return types diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 3367581..9e53d89 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1327,7 +1327,8 @@ public: DirectConnection, QueuedConnection, AutoCompatConnection, - BlockingQueuedConnection + BlockingQueuedConnection, + UniqueConnection = 0x80 }; enum ShortcutContext { diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 65d81f1..be622d9 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -301,7 +301,6 @@ void QObjectPrivate::addConnection(int signal, Connection *c) ConnectionList &connectionList = (*connectionLists)[signal]; connectionList.append(c); - cleanConnectionLists(); } @@ -2377,7 +2376,8 @@ int QObject::receivers(const char *signal) const can be connected to one slot. If a signal is connected to several slots, the slots are activated - in an arbitrary order when the signal is emitted. + in the same order as the order the connection was made, when the + signal is emitted. The function returns true if it successfully connects the signal to the slot. It will return false if it cannot create the @@ -2385,9 +2385,13 @@ int QObject::receivers(const char *signal) const existence of either \a signal or \a method, or if their signatures aren't compatible. - For every connection you make, a signal is emitted; two signals are emitted - for duplicate connections. You can break all of these connections with a - single disconnect() call. + By default, a signal is emitted for every connection you make; + two signals are emitted for duplicate connections. You can break + all of these connections with a single disconnect() call. + If you pass the Qt::UniqueConnection \a type, the connection will only + be made if it is not a duplicate. If there is already a duplicate + (exact same signal to the exact same slot on the same objects), + the connection will fail and connect will return false The optional \a type parameter describes the type of connection to establish. In particular, it determines whether a particular @@ -2520,7 +2524,8 @@ bool QObject::connect(const QObject *sender, const char *signal, } } #endif - QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); + if (!QMetaObject::connect(sender, signal_index, receiver, method_index, type, types)) + return false; const_cast(sender)->connectNotify(signal - 1); return true; } @@ -2771,6 +2776,22 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, QObject *s = const_cast(sender); QObject *r = const_cast(receiver); + QOrderedMutexLocker locker(signalSlotLock(sender), + signalSlotLock(receiver)); + + if (type & Qt::UniqueConnection) { + QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; + if (connectionLists && connectionLists->count() > signal_index) { + QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; + for (int i = 0; i < connectionList.count(); ++i) { + QObjectPrivate::Connection *c2 = connectionList.at(i); + if (c2->receiver == receiver && c2->method == method_index) + return false; + } + } + type &= Qt::UniqueConnection - 1; + } + QObjectPrivate::Connection *c = new QObjectPrivate::Connection; c->sender = s; c->receiver = r; @@ -2778,9 +2799,6 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, c->connectionType = type; c->argumentTypes = types; - QOrderedMutexLocker locker(signalSlotLock(sender), - signalSlotLock(receiver)); - s->d_func()->addConnection(signal_index, c); r->d_func()->senders.append(c); diff --git a/tests/auto/qobject/tst_qobject.cpp b/tests/auto/qobject/tst_qobject.cpp index fb46073..399d021 100644 --- a/tests/auto/qobject/tst_qobject.cpp +++ b/tests/auto/qobject/tst_qobject.cpp @@ -116,6 +116,7 @@ private slots: void dumpObjectInfo(); void connectToSender(); void qobjectConstCast(); + void uniqConnection(); protected: }; @@ -204,12 +205,20 @@ public: sequence_slot3 = 0; sequence_slot2 = 0; sequence_slot1 = 0; + count_slot1 = 0; + count_slot2 = 0; + count_slot3 = 0; + count_slot4 = 0; } int sequence_slot1; int sequence_slot2; int sequence_slot3; int sequence_slot4; + int count_slot1; + int count_slot2; + int count_slot3; + int count_slot4; bool called(int slot) { switch (slot) { @@ -224,10 +233,10 @@ public: static int sequence; public slots: - void slot1() { sequence_slot1 = ++sequence; } - void slot2() { sequence_slot2 = ++sequence; } - void slot3() { sequence_slot3 = ++sequence; } - void slot4() { sequence_slot4 = ++sequence; } + void slot1() { sequence_slot1 = ++sequence; count_slot1++; } + void slot2() { sequence_slot2 = ++sequence; count_slot2++; } + void slot3() { sequence_slot3 = ++sequence; count_slot3++; } + void slot4() { sequence_slot4 = ++sequence; count_slot4++; } }; @@ -2783,5 +2792,69 @@ void tst_QObject::qobjectConstCast() QVERIFY(qobject_cast(cptr)); } +void tst_QObject::uniqConnection() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + QVERIFY( connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal1() ), r2, SLOT( slot1() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal1() ), r1, SLOT( slot3() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) , Qt::UniqueConnection) ); + + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 2 ); + QCOMPARE( r1->count_slot4, 0 ); + QCOMPARE( r2->count_slot1, 1 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + QCOMPARE( r2->count_slot4, 0 ); + QCOMPARE( r1->sequence_slot1, 1 ); + QCOMPARE( r2->sequence_slot1, 2 ); + QCOMPARE( r1->sequence_slot3, 4 ); + + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + QVERIFY( connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal4() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY(!connect( s, SIGNAL( signal4() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal1() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY(!connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) , Qt::UniqueConnection) ); + + s->emitSignal4(); + QCOMPARE( r1->count_slot4, 1 ); + QCOMPARE( r2->count_slot4, 1 ); + QCOMPARE( r1->sequence_slot4, 1 ); + QCOMPARE( r2->sequence_slot4, 2 ); + + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) ); + + s->emitSignal4(); + QCOMPARE( r1->count_slot4, 2 ); + QCOMPARE( r2->count_slot4, 1 ); + QCOMPARE( r1->sequence_slot4, 3 ); + QCOMPARE( r2->sequence_slot4, 2 ); + + delete s; + delete r1; + delete r2; +} + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" -- cgit v0.12