diff options
author | Marc Mutz <marc.mutz@kdab.com> | 2012-03-01 18:52:32 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-10-30 12:56:44 +0100 |
commit | ed827acc27530a97b84685920615359010d74f48 (patch) | |
tree | f603df3e597531a5bb6a6a21d4a565ba8262d614 /src | |
parent | bf6a345baa21818010242566bef4f2c25cb72437 (diff) |
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>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 73 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.h | 46 |
2 files changed, 119 insertions, 0 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index a52fd25e08..72ce941b6e 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -483,6 +483,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 \inmodule QtCore \brief The QObject class is the base class of all Qt objects. diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index e2000afc82..f9239bf575 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -549,6 +549,52 @@ template <class T> inline const char * qobject_interface_iid() Q_CORE_EXPORT QDebug operator<<(QDebug, const QObject *); #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 { inline QObject & deref_for_methodcall(QObject &o) { return o; } inline QObject & deref_for_methodcall(QObject *o) { return *o; } |