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