Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qeventspy.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qeventspy.h"
5
6#include <QtCore/qobject.h>
7#include <QtCore/qpointer.h>
8
9#include <algorithm>
10#include <vector>
11
12QT_BEGIN_NAMESPACE
13
14class QEventSpyPrivate : public QObject
15{
16public:
17 QEventSpyPrivate(QObject *object, QEventSpy::Filter filter)
18 : object(object), filter(std::move(filter)), valid(object != nullptr)
19 {
20 if (object)
21 object->installEventFilter(this);
22 else
23 qWarning("QEventSpy: Cannot spy on a null object");
24 }
25
26 ~QEventSpyPrivate() override
27 {
28 if (object)
29 object->removeEventFilter(this);
30 }
31
32 bool eventFilter(QObject *receiver, QEvent *event) override
33 {
34 if (filter && !filter(receiver, event))
35 return false;
36
37 records.push_back({ receiver, std::unique_ptr<QEvent>(event->clone()) });
38 return false;
39 }
40
41 struct Record
42 {
43 QObject *receiver;
44 std::unique_ptr<QEvent> event;
45 };
46
47 QPointer<QObject> object;
48 const QEventSpy::Filter filter;
49 std::vector<Record> records;
50 const bool valid;
51};
52
53/*!
54 \class QEventSpy
55 \inmodule QtTest
56 \since 6.13
57
58 \brief The QEventSpy class enables introspection of event delivery.
59
60 QEventSpy installs itself as an event filter on a QObject and records
61 the events delivered to it, in the order they arrive. QEventSpy never
62 filters out or modifies the events it observes.
63
64 The following example verifies that showing a widget delivers a single
65 \l{QEvent::}{Show} event to it:
66
67 \code
68 QWidget widget;
69 QEventSpy spy(&widget, QEvent::Show);
70 widget.show();
71 QCOMPARE(spy.count(), 1);
72 \endcode
73
74 To record several event types, or all events, pass a list of types,
75 or an empty list:
76
77 \code
78 QEventSpy spy(&widget, { QEvent::FocusIn, QEvent::FocusOut });
79 ...
80 QCOMPARE(spy.eventList(), QList({ QEvent::FocusIn, QEvent::FocusOut }));
81 \endcode
82
83 For more fine-grained control, pass a QEventSpy::Filter. This is
84 useful when spying on the application object, which sees the events
85 of all objects:
86
87 \code
88 QEventSpy spy(qApp, [&](QObject *receiver, const QEvent *event) {
89 return receiver == &widget && !event->spontaneous();
90 });
91 \endcode
92
93 QEventSpy stores a copy of each recorded event, made with
94 QEvent::clone() at the time the event filter sees it. The copy can be
95 inspected using at():
96
97 \code
98 QEventSpy spy(&widget, QEvent::MouseButtonPress);
99 QTest::mouseClick(&widget, Qt::LeftButton, {}, QPoint(10, 10));
100 QCOMPARE(spy.at<QMouseEvent>(0)->position(), QPointF(10, 10));
101 \endcode
102
103 Event types that do not reimplement QEvent::clone() are recorded as a
104 plain QEvent of the same type.
105
106 To wait for events that are delivered asynchronously, use one of the
107 QTRY_ macros, or QTest::qWaitFor(), with a condition that states what
108 the test is waiting for:
109
110 \code
111 QEventSpy spy(&window, QEvent::Expose);
112 window.show();
113 QTRY_VERIFY(spy.contains(QEvent::Expose));
114 \endcode
115
116 QEventSpy must be created in the thread of the object it spies on.
117
118 \sa QSignalSpy, QObject::installEventFilter()
119*/
120
121/*!
122 \typedef QEventSpy::Filter
123
124 A synonym for \c{std::function<bool(QObject *receiver, const QEvent *event)>}.
125
126 A filter returns \c true for each event that the spy should record.
127*/
128
129/*!
130 Constructs a QEventSpy that records all events delivered to \a object.
131
132 If \a object is \nullptr, a warning is output using qWarning()
133 and subsequent calls to isValid() return \c false.
134*/
135QEventSpy::QEventSpy(QObject *object)
136 : QEventSpy(object, Filter{})
137{
138}
139
140/*!
141 Constructs a QEventSpy that records events of the given \a type
142 delivered to \a object.
143
144 If \a object is \nullptr, a warning is output using qWarning()
145 and subsequent calls to isValid() return \c false.
146*/
147QEventSpy::QEventSpy(QObject *object, QEvent::Type type)
148 : QEventSpy(object, [type](QObject *, const QEvent *event) {
149 return event->type() == type;
150 })
151{
152}
153
154/*!
155 Constructs a QEventSpy that records events of the given \a types
156 delivered to \a object. If \a types is empty, all events are recorded.
157
158 If \a object is \nullptr, a warning is output using qWarning()
159 and subsequent calls to isValid() return \c false.
160*/
161QEventSpy::QEventSpy(QObject *object, const QList<QEvent::Type> &types)
162 : QEventSpy(object, types.isEmpty() ? Filter{} : Filter([types](QObject *, const QEvent *event) {
163 return types.contains(event->type());
164 }))
165{
166}
167
168/*!
169 Constructs a QEventSpy that records the events delivered to \a object
170 for which \a filter returns \c true. If \a filter is empty, all events
171 are recorded.
172
173 If \a object is \nullptr, a warning is output using qWarning()
174 and subsequent calls to isValid() return \c false.
175*/
176QEventSpy::QEventSpy(QObject *object, Filter filter)
177 : d_ptr(std::make_unique<QEventSpyPrivate>(object, std::move(filter)))
178{
179}
180
181/*!
182 Destroys the QEventSpy, and removes it as an event filter
183 from the spied-on object.
184*/
185QEventSpy::~QEventSpy() = default;
186
187/*!
188 Returns \c true if the spy was constructed with a valid object,
189 otherwise \c false.
190
191 The spy remains valid if the object is later destroyed.
192*/
193bool QEventSpy::isValid() const noexcept
194{
195 return d_ptr->valid;
196}
197
198/*!
199 Returns the object the spy was installed on, or \nullptr
200 if the object has been destroyed.
201*/
202QObject *QEventSpy::object() const
203{
204 return d_ptr->object;
205}
206
207/*!
208 \fn qsizetype QEventSpy::size() const
209
210 Same as count().
211*/
212
213/*!
214 \fn bool QEventSpy::isEmpty() const
215
216 Returns \c true if the spy has not recorded any events,
217 otherwise \c false.
218*/
219
220/*!
221 Returns the number of recorded events.
222*/
223qsizetype QEventSpy::count() const noexcept
224{
225 return qsizetype(d_ptr->records.size());
226}
227
228/*!
229 \overload
230
231 Returns the number of recorded events of the given \a type.
232*/
233qsizetype QEventSpy::count(QEvent::Type type) const noexcept
234{
235 return std::count_if(d_ptr->records.cbegin(), d_ptr->records.cend(), [type](const auto &record) {
236 return record.event->type() == type;
237 });
238}
239
240/*!
241 \fn bool QEventSpy::contains(QEvent::Type type) const
242
243 Returns \c true if the spy has recorded at least one event
244 of the given \a type, otherwise \c false.
245*/
246
247/*!
248 Returns the types of the recorded events, in the order
249 the spy saw them, including repeated types.
250
251 \code
252 QCOMPARE(spy.eventList(), QList({ QEvent::ChildAdded, QEvent::ChildAdded }));
253 \endcode
254*/
255QList<QEvent::Type> QEventSpy::eventList() const
256{
257 QList<QEvent::Type> eventList;
258 eventList.reserve(count());
259 for (const auto &record : d_ptr->records)
260 eventList.append(record.event->type());
261 return eventList;
262}
263
264/*!
265 Returns a copy of the recorded event at index position \a i.
266
267 \a i must be a valid index position (0 <= \a i < count()).
268 The returned pointer is valid until the event is removed from
269 the spy, or the spy is destroyed.
270*/
271const QEvent *QEventSpy::at(qsizetype i) const
272{
273 Q_ASSERT_X(i >= 0 && i < count(), "QEventSpy::at", "index out of range");
274 return d_ptr->records[size_t(i)].event.get();
275}
276
277/*!
278 \fn template <typename EventType> const EventType *QEventSpy::at(qsizetype i) const
279 \overload
280
281 Returns a copy of the recorded event at index position \a i,
282 cast to \c EventType.
283
284 The caller must ensure that the event is of type \c EventType,
285 for example by checking QEvent::type().
286*/
287
288/*!
289 Returns the object that the event at index position \a i
290 was delivered to.
291
292 \a i must be a valid index position (0 <= \a i < count()).
293 The object may have been destroyed since the event was recorded.
294*/
295QObject *QEventSpy::receiver(qsizetype i) const
296{
297 Q_ASSERT_X(i >= 0 && i < count(), "QEventSpy::receiver", "index out of range");
298 return d_ptr->records[size_t(i)].receiver;
299}
300
301/*!
302 Removes all recorded events from the spy.
303*/
305{
306 d_ptr->records.clear();
307}
308
309QT_END_NAMESPACE
\inmodule QtTest
Definition qeventspy.h:20
std::function< bool(QObject *receiver, const QEvent *event)> Filter
A synonym for {std::function<bool(QObject *receiver, const QEvent *event)>}.
Definition qeventspy.h:22
Q_TESTLIB_EXPORT ~QEventSpy()
Destroys the QEventSpy, and removes it as an event filter from the spied-on object.
Q_TESTLIB_EXPORT bool isValid() const noexcept
Returns true if the spy was constructed with a valid object, otherwise false.
Q_TESTLIB_EXPORT void clear()
Removes all recorded events from the spy.