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
qsplashscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant reason:default
4
6
7#include "qapplication.h"
8#include "qpainter.h"
9#include "qpixmap.h"
10#include "qtextdocument.h"
11#include "qtextcursor.h"
12#include <QtGui/qscreen.h>
13#include <QtGui/qwindow.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qelapsedtimer.h>
16#include <private/qwidget_p.h>
17
18#ifdef Q_OS_WIN
19# include <QtCore/qt_windows.h>
20#else
21# include <time.h>
22#endif
23
24QT_BEGIN_NAMESPACE
25
26class QSplashScreenPrivate : public QWidgetPrivate
27{
28 Q_DECLARE_PUBLIC(QSplashScreen)
29public:
30 QPixmap pixmap;
31 QString currStatus;
32 QColor currColor;
33 int currAlign;
34
35 inline QSplashScreenPrivate();
36
37 void handlePaintEvent();
38};
39
40/*!
41 \class QSplashScreen
42 \brief The QSplashScreen widget provides a splash screen that can
43 be shown during application startup.
44
45 \inmodule QtWidgets
46
47 A splash screen is a widget that is usually displayed when an
48 application is being started. Splash screens are often used for
49 applications that have long start up times (e.g. database or
50 networking applications that take time to establish connections) to
51 provide the user with feedback that the application is loading.
52
53 The splash screen appears in the center of the screen. It may be
54 useful to add the Qt::WindowStaysOnTopHint to the splash widget's
55 window flags if you want to keep it above all the other windows on
56 the desktop.
57
58 Some X11 window managers do not support the "stays on top" flag. A
59 solution is to set up a timer that periodically calls raise() on
60 the splash screen to simulate the "stays on top" effect.
61
62 The most common usage is to show a splash screen before the main
63 widget is displayed on the screen. This is illustrated in the
64 following code snippet in which a splash screen is displayed and
65 some initialization tasks are performed before the application's
66 main window is shown:
67
68 \snippet qsplashscreen/main.cpp 0
69
70 The user can hide the splash screen by clicking on it with the
71 mouse. For mouse handling to work, call QApplication::processEvents()
72 periodically during startup.
73
74
75 It is sometimes useful to update the splash screen with messages,
76 for example, announcing connections established or modules loaded
77 as the application starts up:
78
79 \snippet code/src_gui_widgets_qsplashscreen.cpp 0
80
81 QSplashScreen supports this with the showMessage() function. If you
82 wish to do your own drawing you can get a pointer to the pixmap
83 used in the splash screen with pixmap(). Alternatively, you can
84 subclass QSplashScreen and reimplement drawContents().
85
86 In case of having multiple screens, it is also possible to show the
87 splash screen on a different screen than the primary one. For example:
88
89 \snippet qsplashscreen/main.cpp 2
90*/
91
92/*!
93 Construct a splash screen that will display the \a pixmap.
94
95 There should be no need to set the widget flags, \a f, except
96 perhaps Qt::WindowStaysOnTopHint.
97*/
98QSplashScreen::QSplashScreen(const QPixmap &pixmap, Qt::WindowFlags f)
99 : QWidget(*(new QSplashScreenPrivate()), nullptr, Qt::SplashScreen | Qt::FramelessWindowHint | f)
100{
101 setPixmap(pixmap); // Does an implicit repaint
102}
103
104/*!
105 \overload
106 \since 5.15
107
108 This function allows you to specify the screen for your splashscreen. The
109 typical use for this constructor is if you have multiple screens and
110 prefer to have the splash screen on a different screen than your primary
111 one. In that case pass the proper \a screen.
112*/
113QSplashScreen::QSplashScreen(QScreen *screen, const QPixmap &pixmap, Qt::WindowFlags f)
114 : QWidget(*(new QSplashScreenPrivate()), nullptr, Qt::SplashScreen | Qt::FramelessWindowHint | f)
115{
116 Q_D(QSplashScreen);
117 d->setScreen(screen);
118 setPixmap(pixmap);
119}
120
121/*!
122 Destructor.
123*/
124QSplashScreen::~QSplashScreen()
125{
126}
127
128/*!
129 \reimp
130*/
131void QSplashScreen::mousePressEvent(QMouseEvent *)
132{
133 hide();
134}
135
136/*!
137 This overrides QWidget::repaint(). It differs from the standard repaint
138 function in that it also calls QCoreApplication::processEvents() to ensure
139 the updates are displayed, even when there is no event loop present.
140*/
141void QSplashScreen::repaint()
142{
143 QWidget::repaint();
144 QCoreApplication::processEvents();
145}
146
147/*!
148 \fn QSplashScreen::messageChanged(const QString &message)
149
150 This signal is emitted when the message on the splash screen
151 changes. \a message is the new message and is a null-string
152 when the message has been removed.
153
154 \sa showMessage(), clearMessage()
155*/
156
157
158
159/*!
160 Draws the \a message text onto the splash screen with color \a
161 color and aligns the text according to the flags in \a alignment.
162 This function calls repaint() to make sure the splash screen is
163 repainted immediately. As a result the message is kept up
164 to date with what your application is doing (e.g. loading files).
165
166 \sa Qt::Alignment, clearMessage(), message()
167*/
168void QSplashScreen::showMessage(const QString &message, int alignment,
169 const QColor &color)
170{
171 Q_D(QSplashScreen);
172 d->currStatus = message;
173 d->currAlign = alignment;
174 d->currColor = color;
175 emit messageChanged(d->currStatus);
176 repaint();
177}
178
179/*!
180 \since 5.2
181
182 Returns the message that is currently displayed on the splash screen.
183
184 \sa showMessage(), clearMessage()
185*/
186
187QString QSplashScreen::message() const
188{
189 Q_D(const QSplashScreen);
190 return d->currStatus;
191}
192
193/*!
194 Removes the message being displayed on the splash screen
195
196 \sa showMessage()
197 */
198void QSplashScreen::clearMessage()
199{
200 d_func()->currStatus.clear();
201 emit messageChanged(d_func()->currStatus);
202 repaint();
203}
204
205static bool waitForWidgetMapped(QWidget *widget, int timeout = 1000)
206{
207 enum { TimeOutMs = 10 };
208 auto isMapped = [widget](){
209 return widget->windowHandle() && widget->windowHandle()->isVisible();
210 };
211
212 QElapsedTimer timer;
213 timer.start();
214 while (!isMapped()) {
215 const int remaining = timeout - int(timer.elapsed());
216 if (remaining <= 0)
217 break;
218 QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
219 QCoreApplication::sendPostedEvents();
220#if defined(Q_OS_WIN)
221 Sleep(uint(TimeOutMs));
222#else
223 struct timespec ts = { TimeOutMs / 1000, (TimeOutMs % 1000) * 1000 * 1000 };
224 nanosleep(&ts, nullptr);
225#endif
226 }
227 return isMapped();
228}
229
230/*!
231 Makes the splash screen wait until the widget \a mainWin is displayed
232 before calling close() on itself.
233*/
234
235void QSplashScreen::finish(QWidget *mainWin)
236{
237 if (mainWin) {
238 if (!mainWin->windowHandle())
239 mainWin->createWinId();
240 waitForWidgetMapped(mainWin);
241 }
242 close();
243}
244
245/*!
246 Sets the pixmap that will be used as the splash screen's image to
247 \a pixmap.
248*/
249void QSplashScreen::setPixmap(const QPixmap &pixmap)
250{
251 Q_D(QSplashScreen);
252 d->pixmap = pixmap;
253 setAttribute(Qt::WA_TranslucentBackground, pixmap.hasAlpha());
254
255 const QRect r(QPoint(), pixmap.deviceIndependentSize().toSize());
256 resize(r.size());
257
258 move(screen()->geometry().center() - r.center());
259 if (isVisible())
260 repaint();
261}
262
263/*!
264 Returns the pixmap that is used in the splash screen. The image
265 does not have any of the text drawn by showMessage() calls.
266*/
267const QPixmap QSplashScreen::pixmap() const
268{
269 return d_func()->pixmap;
270}
271
272/*!
273 \internal
274*/
275inline QSplashScreenPrivate::QSplashScreenPrivate() : currAlign(Qt::AlignLeft)
276{
277}
278
279/*!
280 Draw the contents of the splash screen using painter \a painter.
281 The default implementation draws the message passed by showMessage().
282 Reimplement this function if you want to do your own drawing on
283 the splash screen.
284*/
285void QSplashScreen::drawContents(QPainter *painter)
286{
287 Q_D(QSplashScreen);
288 painter->setPen(d->currColor);
289 QRect r = rect().adjusted(5, 5, -5, -5);
290 if (Qt::mightBeRichText(d->currStatus)) {
291 QTextDocument doc;
292#ifdef QT_NO_TEXTHTMLPARSER
293 doc.setPlainText(d->currStatus);
294#else
295 doc.setHtml(d->currStatus);
296#endif
297 doc.setTextWidth(r.width());
298 QTextCursor cursor(&doc);
299 cursor.select(QTextCursor::Document);
300 QTextBlockFormat fmt;
301 fmt.setAlignment(Qt::Alignment(d->currAlign));
302 fmt.setLayoutDirection(layoutDirection());
303 cursor.mergeBlockFormat(fmt);
304 const QSizeF txtSize = doc.size();
305 if (d->currAlign & Qt::AlignBottom)
306 r.setTop(r.height() - txtSize.height());
307 else if (d->currAlign & Qt::AlignVCenter)
308 r.setTop(r.height() / 2 - txtSize.height() / 2);
309 painter->save();
310 painter->translate(r.topLeft());
311 doc.drawContents(painter);
312 painter->restore();
313 } else {
314 painter->drawText(r, d->currAlign, d->currStatus);
315 }
316}
317
318void QSplashScreenPrivate::handlePaintEvent()
319{
320 Q_Q(QSplashScreen);
321 QPainter painter(q);
322 painter.setRenderHints(QPainter::SmoothPixmapTransform);
323 painter.setLayoutDirection(q->layoutDirection());
324 if (!pixmap.isNull())
325 painter.drawPixmap(QPoint(), pixmap);
326 q->drawContents(&painter);
327}
328
329/*! \reimp */
330bool QSplashScreen::event(QEvent *e)
331{
332 Q_D(QSplashScreen);
333
334 switch (e->type()) {
335 case QEvent::Paint:
336 d->handlePaintEvent();
337 break;
338 case QEvent::Show:
339 waitForWidgetMapped(this);
340 break;
341 default:
342 break;
343 }
344
345 return QWidget::event(e);
346}
347
348QT_END_NAMESPACE
349
350#include "moc_qsplashscreen.cpp"
friend class QWidget
Definition qpainter.h:431
static bool waitForWidgetMapped(QWidget *widget, int timeout=1000)