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
|
||||
------
|
||||
|
||||
- QSignalBlocker: (new) RAII wrapper around QObject::blockSignals()
|
||||
|
||||
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
|
||||
\inmodule QtCore
|
||||
|
@ -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; }
|
||||
|
@ -102,6 +102,7 @@ private slots:
|
||||
#ifndef QT_NO_PROCESS
|
||||
void recursiveSignalEmission();
|
||||
#endif
|
||||
void signalBlocking();
|
||||
void blockingQueuedConnection();
|
||||
void childEvents();
|
||||
void installEventFilter();
|
||||
@ -2979,6 +2980,54 @@ void tst_QObject::recursiveSignalEmission()
|
||||
}
|
||||
#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()
|
||||
{
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user