QSignalBlocker: (new) RAII class for QObject::blockSignals()
I don't think I ever worked on a project of non-trivial size that didn't at some point add a QSignalBlocker. This commit adds code, tests and documentation. Later commits will convert naked blockSignals() calls to use QSignalBlocker. The implementation is purely inline to avoid the heavy overhead of cross-dll function calls for this miniscule task. This should not be a problem because QSignalBlocker only uses public API and a pattern that we anyway need to keep working until Qt 6, at least, so even changing the implementation later will be no problem as the old implementation lurking in non-recompiled code will be acceptable, too. This implementation is an evolution from KDTools' KDSignalBlocker, with the following changes: - Implements unblock() and reblock() - Uses the return value of blockSignals() instead of a separate signalsBlocked() call. Change-Id: I1933dfd72a0f5190324be377cfca3c54cf3d6828 Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
parent
bf6a345baa
commit
ed827acc27
2
dist/changes-5.3.0
vendored
2
dist/changes-5.3.0
vendored
@ -25,5 +25,7 @@ QtWidgets
|
|||||||
QtCore
|
QtCore
|
||||||
------
|
------
|
||||||
|
|
||||||
|
- QSignalBlocker: (new) RAII wrapper around QObject::blockSignals()
|
||||||
|
|
||||||
QtGui
|
QtGui
|
||||||
-----
|
-----
|
||||||
|
@ -482,6 +482,79 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSignalBlocker
|
||||||
|
\brief Exception-safe wrapper around QObject::blockSignals()
|
||||||
|
\since 5.3
|
||||||
|
\ingroup objectmodel
|
||||||
|
|
||||||
|
\reentrant
|
||||||
|
|
||||||
|
QSignalBlocker can be used whereever you would otherwise use a
|
||||||
|
pair of calls to blockSignals(). It blocks signals in its
|
||||||
|
constructor and in the destructor it resets the state to what
|
||||||
|
it was before the constructor ran.
|
||||||
|
|
||||||
|
\code
|
||||||
|
{
|
||||||
|
const QSignalBlocker blocker(someQObject);
|
||||||
|
// no signals here
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
is thus equivalent to
|
||||||
|
\code
|
||||||
|
const bool wasBlocked = someQObject->blockSignals(true);
|
||||||
|
// no signals here
|
||||||
|
someQObject->blockSignals(false);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
except the code using QSignalBlocker is safe in the face of
|
||||||
|
exceptions.
|
||||||
|
|
||||||
|
\sa QMutexLocker, QEventLoopLocker
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QSignalBlocker::QSignalBlocker(QObject *object)
|
||||||
|
|
||||||
|
Constructor. Calls \a{object}->blockSignals(true).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QSignalBlocker::QSignalBlocker(QObject &object)
|
||||||
|
\overload
|
||||||
|
|
||||||
|
Calls \a{object}.blockSignals(true).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QSignalBlocker::~QSignalBlocker()
|
||||||
|
|
||||||
|
Destructor. Restores the QObject::signalsBlocked() state to what it
|
||||||
|
was before the constructor ran, unless unblock() has been called
|
||||||
|
without a following reblock(), in which case it does nothing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSignalBlocker::reblock()
|
||||||
|
|
||||||
|
Re-blocks signals after a previous unblock().
|
||||||
|
|
||||||
|
The numbers of reblock() and unblock() calls are not counted, so
|
||||||
|
every reblock() undoes any number of unblock() calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QSignalBlocker::unblock()
|
||||||
|
|
||||||
|
Temporarily restores the QObject::signalsBlocked() state to what
|
||||||
|
it was before this QSignaBlocker's constructor ran. To undo, use
|
||||||
|
reblock().
|
||||||
|
|
||||||
|
The numbers of reblock() and unblock() calls are not counted, so
|
||||||
|
every unblock() undoes any number of reblock() calls.
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QObject
|
\class QObject
|
||||||
\inmodule QtCore
|
\inmodule QtCore
|
||||||
|
@ -549,6 +549,52 @@ template <class T> inline const char * qobject_interface_iid()
|
|||||||
Q_CORE_EXPORT QDebug operator<<(QDebug, const QObject *);
|
Q_CORE_EXPORT QDebug operator<<(QDebug, const QObject *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Q_CORE_EXPORT QSignalBlocker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit QSignalBlocker(QObject *o);
|
||||||
|
inline explicit QSignalBlocker(QObject &o);
|
||||||
|
inline ~QSignalBlocker();
|
||||||
|
|
||||||
|
inline void reblock();
|
||||||
|
inline void unblock();
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(QSignalBlocker)
|
||||||
|
QObject * const m_o;
|
||||||
|
bool m_blocked;
|
||||||
|
bool m_inhibited;
|
||||||
|
};
|
||||||
|
|
||||||
|
QSignalBlocker::QSignalBlocker(QObject *o)
|
||||||
|
: m_o(o),
|
||||||
|
m_blocked(o && o->blockSignals(true)),
|
||||||
|
m_inhibited(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QSignalBlocker::QSignalBlocker(QObject &o)
|
||||||
|
: m_o(&o),
|
||||||
|
m_blocked(o.blockSignals(true)),
|
||||||
|
m_inhibited(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QSignalBlocker::~QSignalBlocker()
|
||||||
|
{
|
||||||
|
if (m_o && !m_inhibited)
|
||||||
|
m_o->blockSignals(m_blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QSignalBlocker::reblock()
|
||||||
|
{
|
||||||
|
if (m_o) m_o->blockSignals(true);
|
||||||
|
m_inhibited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QSignalBlocker::unblock()
|
||||||
|
{
|
||||||
|
if (m_o) m_o->blockSignals(m_blocked);
|
||||||
|
m_inhibited = true;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QtPrivate {
|
namespace QtPrivate {
|
||||||
inline QObject & deref_for_methodcall(QObject &o) { return o; }
|
inline QObject & deref_for_methodcall(QObject &o) { return o; }
|
||||||
inline QObject & deref_for_methodcall(QObject *o) { return *o; }
|
inline QObject & deref_for_methodcall(QObject *o) { return *o; }
|
||||||
|
@ -102,6 +102,7 @@ private slots:
|
|||||||
#ifndef QT_NO_PROCESS
|
#ifndef QT_NO_PROCESS
|
||||||
void recursiveSignalEmission();
|
void recursiveSignalEmission();
|
||||||
#endif
|
#endif
|
||||||
|
void signalBlocking();
|
||||||
void blockingQueuedConnection();
|
void blockingQueuedConnection();
|
||||||
void childEvents();
|
void childEvents();
|
||||||
void installEventFilter();
|
void installEventFilter();
|
||||||
@ -2979,6 +2980,54 @@ void tst_QObject::recursiveSignalEmission()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void tst_QObject::signalBlocking()
|
||||||
|
{
|
||||||
|
SenderObject sender;
|
||||||
|
ReceiverObject receiver;
|
||||||
|
|
||||||
|
receiver.connect(&sender, SIGNAL(signal1()), SLOT(slot1()));
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
|
||||||
|
{
|
||||||
|
QSignalBlocker blocker(&sender);
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(!receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
|
||||||
|
sender.blockSignals(false);
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
|
||||||
|
sender.blockSignals(true);
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(!receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
|
||||||
|
blocker.unblock();
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
|
||||||
|
blocker.reblock();
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(!receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
sender.emitSignal1();
|
||||||
|
QVERIFY(receiver.called(1));
|
||||||
|
receiver.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QObject::blockingQueuedConnection()
|
void tst_QObject::blockingQueuedConnection()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user