1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWidgets module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qprogressdialog.h" |
41 | |
42 | #include "qshortcut.h" |
43 | #include "qpainter.h" |
44 | #include "qdrawutil.h" |
45 | #include "qlabel.h" |
46 | #include "qprogressbar.h" |
47 | #include "qapplication.h" |
48 | #include "qstyle.h" |
49 | #include "qpushbutton.h" |
50 | #include "qtimer.h" |
51 | #include "qelapsedtimer.h" |
52 | #include "qscopedvaluerollback.h" |
53 | #include <private/qdialog_p.h> |
54 | #include <limits.h> |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | // If the operation is expected to take this long (as predicted by |
59 | // progress time), show the progress dialog. |
60 | static const int defaultShowTime = 4000; |
61 | // Wait at least this long before attempting to make a prediction. |
62 | static const int minWaitTime = 50; |
63 | |
64 | class QProgressDialogPrivate : public QDialogPrivate |
65 | { |
66 | Q_DECLARE_PUBLIC(QProgressDialog) |
67 | |
68 | public: |
69 | QProgressDialogPrivate() : label(nullptr), cancel(nullptr), bar(nullptr), |
70 | shown_once(false), |
71 | cancellation_flag(false), |
72 | setValue_called(false), |
73 | processingEvents(false), |
74 | showTime(defaultShowTime), |
75 | #ifndef QT_NO_SHORTCUT |
76 | escapeShortcut(nullptr), |
77 | #endif |
78 | useDefaultCancelText(false) |
79 | { |
80 | } |
81 | |
82 | void init(const QString &labelText, const QString &cancelText, int min, int max); |
83 | void layout(); |
84 | void retranslateStrings(); |
85 | void setCancelButtonText(const QString &cancelButtonText); |
86 | void adoptChildWidget(QWidget *c); |
87 | void ensureSizeIsAtLeastSizeHint(); |
88 | void _q_disconnectOnClose(); |
89 | |
90 | QLabel *label; |
91 | QPushButton *cancel; |
92 | QProgressBar *bar; |
93 | QTimer *forceTimer; |
94 | bool shown_once; |
95 | bool cancellation_flag; |
96 | bool setValue_called; |
97 | bool processingEvents; |
98 | QElapsedTimer starttime; |
99 | int showTime; |
100 | bool autoClose; |
101 | bool autoReset; |
102 | bool forceHide; |
103 | #ifndef QT_NO_SHORTCUT |
104 | QShortcut *escapeShortcut; |
105 | #endif |
106 | bool useDefaultCancelText; |
107 | QPointer<QObject> receiverToDisconnectOnClose; |
108 | QByteArray memberToDisconnectOnClose; |
109 | }; |
110 | |
111 | void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText, |
112 | int min, int max) |
113 | { |
114 | Q_Q(QProgressDialog); |
115 | label = new QLabel(labelText, q); |
116 | bar = new QProgressBar(q); |
117 | bar->setRange(minimum: min, maximum: max); |
118 | int align = q->style()->styleHint(stylehint: QStyle::SH_ProgressDialog_TextLabelAlignment, opt: nullptr, widget: q); |
119 | label->setAlignment(Qt::Alignment(align)); |
120 | autoClose = true; |
121 | autoReset = true; |
122 | forceHide = false; |
123 | QObject::connect(sender: q, SIGNAL(canceled()), receiver: q, SLOT(cancel())); |
124 | forceTimer = new QTimer(q); |
125 | QObject::connect(sender: forceTimer, SIGNAL(timeout()), receiver: q, SLOT(forceShow())); |
126 | if (useDefaultCancelText) { |
127 | retranslateStrings(); |
128 | } else { |
129 | q->setCancelButtonText(cancelText); |
130 | } |
131 | starttime.start(); |
132 | forceTimer->start(msec: showTime); |
133 | } |
134 | |
135 | void QProgressDialogPrivate::layout() |
136 | { |
137 | Q_Q(QProgressDialog); |
138 | int sp = q->style()->pixelMetric(metric: QStyle::PM_LayoutVerticalSpacing, option: nullptr, widget: q); |
139 | int mb = q->style()->pixelMetric(metric: QStyle::PM_LayoutBottomMargin, option: nullptr, widget: q); |
140 | int ml = qMin(a: q->width() / 10, b: q->style()->pixelMetric(metric: QStyle::PM_LayoutLeftMargin, option: nullptr, widget: q)); |
141 | int mr = qMin(a: q->width() / 10, b: q->style()->pixelMetric(metric: QStyle::PM_LayoutRightMargin, option: nullptr, widget: q)); |
142 | const bool centered = |
143 | bool(q->style()->styleHint(stylehint: QStyle::SH_ProgressDialog_CenterCancelButton, opt: nullptr, widget: q)); |
144 | |
145 | int additionalSpacing = 0; |
146 | QSize cs = cancel ? cancel->sizeHint() : QSize(0,0); |
147 | QSize bh = bar->sizeHint(); |
148 | int cspc; |
149 | int lh = 0; |
150 | |
151 | // Find spacing and sizes that fit. It is important that a progress |
152 | // dialog can be made very small if the user demands it so. |
153 | for (int attempt=5; attempt--;) { |
154 | cspc = cancel ? cs.height() + sp : 0; |
155 | lh = qMax(a: 0, b: q->height() - mb - bh.height() - sp - cspc); |
156 | |
157 | if (lh < q->height()/4) { |
158 | // Getting cramped |
159 | sp /= 2; |
160 | mb /= 2; |
161 | if (cancel) { |
162 | cs.setHeight(qMax(a: 4,b: cs.height()-sp-2)); |
163 | } |
164 | bh.setHeight(qMax(a: 4,b: bh.height()-sp-1)); |
165 | } else { |
166 | break; |
167 | } |
168 | } |
169 | |
170 | if (cancel) { |
171 | cancel->setGeometry( |
172 | ax: centered ? q->width()/2 - cs.width()/2 : q->width() - mr - cs.width(), |
173 | ay: q->height() - mb - cs.height(), |
174 | aw: cs.width(), ah: cs.height()); |
175 | } |
176 | |
177 | if (label) |
178 | label->setGeometry(ax: ml, ay: additionalSpacing, aw: q->width() - ml - mr, ah: lh); |
179 | bar->setGeometry(ax: ml, ay: lh + sp + additionalSpacing, aw: q->width() - ml - mr, ah: bh.height()); |
180 | } |
181 | |
182 | void QProgressDialogPrivate::retranslateStrings() |
183 | { |
184 | if (useDefaultCancelText) |
185 | setCancelButtonText(QProgressDialog::tr(s: "Cancel" )); |
186 | } |
187 | |
188 | void QProgressDialogPrivate::_q_disconnectOnClose() |
189 | { |
190 | Q_Q(QProgressDialog); |
191 | if (receiverToDisconnectOnClose) { |
192 | QObject::disconnect(sender: q, SIGNAL(canceled()), receiver: receiverToDisconnectOnClose, |
193 | member: memberToDisconnectOnClose); |
194 | receiverToDisconnectOnClose = nullptr; |
195 | } |
196 | memberToDisconnectOnClose.clear(); |
197 | } |
198 | |
199 | /*! |
200 | \class QProgressDialog |
201 | \brief The QProgressDialog class provides feedback on the progress of a slow operation. |
202 | \ingroup standard-dialogs |
203 | \inmodule QtWidgets |
204 | |
205 | |
206 | A progress dialog is used to give the user an indication of how long |
207 | an operation is going to take, and to demonstrate that the |
208 | application has not frozen. It can also give the user an opportunity |
209 | to abort the operation. |
210 | |
211 | A common problem with progress dialogs is that it is difficult to know |
212 | when to use them; operations take different amounts of time on different |
213 | hardware. QProgressDialog offers a solution to this problem: |
214 | it estimates the time the operation will take (based on time for |
215 | steps), and only shows itself if that estimate is beyond minimumDuration() |
216 | (4 seconds by default). |
217 | |
218 | Use setMinimum() and setMaximum() or the constructor to set the number of |
219 | "steps" in the operation and call setValue() as the operation |
220 | progresses. The number of steps can be chosen arbitrarily. It can be the |
221 | number of files copied, the number of bytes received, the number of |
222 | iterations through the main loop of your algorithm, or some other |
223 | suitable unit. Progress starts at the value set by setMinimum(), |
224 | and the progress dialog shows that the operation has finished when |
225 | you call setValue() with the value set by setMaximum() as its argument. |
226 | |
227 | The dialog automatically resets and hides itself at the end of the |
228 | operation. Use setAutoReset() and setAutoClose() to change this |
229 | behavior. Note that if you set a new maximum (using setMaximum() or |
230 | setRange()) that equals your current value(), the dialog will not |
231 | close regardless. |
232 | |
233 | There are two ways of using QProgressDialog: modal and modeless. |
234 | |
235 | Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler |
236 | to use for the programmer. Do the operation in a loop, call \l setValue() at |
237 | intervals, and check for cancellation with wasCanceled(). For example: |
238 | |
239 | \snippet dialogs/dialogs.cpp 3 |
240 | |
241 | A modeless progress dialog is suitable for operations that take |
242 | place in the background, where the user is able to interact with the |
243 | application. Such operations are typically based on QTimer (or |
244 | QObject::timerEvent()) or QSocketNotifier; or performed |
245 | in a separate thread. A QProgressBar in the status bar of your main window |
246 | is often an alternative to a modeless progress dialog. |
247 | |
248 | You need to have an event loop to be running, connect the |
249 | canceled() signal to a slot that stops the operation, and call \l |
250 | setValue() at intervals. For example: |
251 | |
252 | \snippet dialogs/dialogs.cpp 4 |
253 | \codeline |
254 | \snippet dialogs/dialogs.cpp 5 |
255 | \codeline |
256 | \snippet dialogs/dialogs.cpp 6 |
257 | |
258 | In both modes the progress dialog may be customized by |
259 | replacing the child widgets with custom widgets by using setLabel(), |
260 | setBar(), and setCancelButton(). |
261 | The functions setLabelText() and setCancelButtonText() |
262 | set the texts shown. |
263 | |
264 | \image fusion-progressdialog.png A progress dialog shown in the Fusion widget style. |
265 | |
266 | \sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator}, |
267 | {Find Files Example}, {Pixelator Example} |
268 | */ |
269 | |
270 | |
271 | /*! |
272 | Constructs a progress dialog. |
273 | |
274 | Default settings: |
275 | \list |
276 | \li The label text is empty. |
277 | \li The cancel button text is (translated) "Cancel". |
278 | \li minimum is 0; |
279 | \li maximum is 100 |
280 | \endlist |
281 | |
282 | The \a parent argument is dialog's parent widget. The widget flags, \a f, are |
283 | passed to the QDialog::QDialog() constructor. |
284 | |
285 | \sa setLabelText(), setCancelButtonText(), setCancelButton(), |
286 | setMinimum(), setMaximum() |
287 | */ |
288 | |
289 | QProgressDialog::QProgressDialog(QWidget *parent, Qt::WindowFlags f) |
290 | : QDialog(*(new QProgressDialogPrivate), parent, f) |
291 | { |
292 | Q_D(QProgressDialog); |
293 | d->useDefaultCancelText = true; |
294 | d->init(labelText: QString::fromLatin1(str: "" ), cancelText: QString(), min: 0, max: 100); |
295 | } |
296 | |
297 | /*! |
298 | Constructs a progress dialog. |
299 | |
300 | The \a labelText is the text used to remind the user what is progressing. |
301 | |
302 | The \a cancelButtonText is the text to display on the cancel button. If |
303 | QString() is passed then no cancel button is shown. |
304 | |
305 | The \a minimum and \a maximum is the number of steps in the operation for |
306 | which this progress dialog shows progress. For example, if the |
307 | operation is to examine 50 files, this value minimum value would be 0, |
308 | and the maximum would be 50. Before examining the first file, call |
309 | setValue(0). As each file is processed call setValue(1), setValue(2), |
310 | etc., finally calling setValue(50) after examining the last file. |
311 | |
312 | The \a parent argument is the dialog's parent widget. The parent, \a parent, and |
313 | widget flags, \a f, are passed to the QDialog::QDialog() constructor. |
314 | |
315 | \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(), |
316 | setMinimum(), setMaximum() |
317 | */ |
318 | |
319 | QProgressDialog::QProgressDialog(const QString &labelText, |
320 | const QString &cancelButtonText, |
321 | int minimum, int maximum, |
322 | QWidget *parent, Qt::WindowFlags f) |
323 | : QDialog(*(new QProgressDialogPrivate), parent, f) |
324 | { |
325 | Q_D(QProgressDialog); |
326 | d->init(labelText, cancelText: cancelButtonText, min: minimum, max: maximum); |
327 | } |
328 | |
329 | |
330 | /*! |
331 | Destroys the progress dialog. |
332 | */ |
333 | |
334 | QProgressDialog::~QProgressDialog() |
335 | { |
336 | } |
337 | |
338 | /*! |
339 | \fn void QProgressDialog::canceled() |
340 | |
341 | This signal is emitted when the cancel button is clicked. |
342 | It is connected to the cancel() slot by default. |
343 | |
344 | \sa wasCanceled() |
345 | */ |
346 | |
347 | |
348 | /*! |
349 | Sets the label to \a label. The progress dialog resizes to fit. The |
350 | label becomes owned by the progress dialog and will be deleted when |
351 | necessary, so do not pass the address of an object on the stack. |
352 | |
353 | \sa setLabelText() |
354 | */ |
355 | |
356 | void QProgressDialog::setLabel(QLabel *label) |
357 | { |
358 | Q_D(QProgressDialog); |
359 | if (label == d->label) { |
360 | if (Q_UNLIKELY(label)) |
361 | qWarning(msg: "QProgressDialog::setLabel: Attempt to set the same label again" ); |
362 | return; |
363 | } |
364 | delete d->label; |
365 | d->label = label; |
366 | d->adoptChildWidget(c: label); |
367 | } |
368 | |
369 | |
370 | /*! |
371 | \property QProgressDialog::labelText |
372 | \brief the label's text |
373 | |
374 | The default text is an empty string. |
375 | */ |
376 | |
377 | QString QProgressDialog::labelText() const |
378 | { |
379 | Q_D(const QProgressDialog); |
380 | if (d->label) |
381 | return d->label->text(); |
382 | return QString(); |
383 | } |
384 | |
385 | void QProgressDialog::setLabelText(const QString &text) |
386 | { |
387 | Q_D(QProgressDialog); |
388 | if (d->label) { |
389 | d->label->setText(text); |
390 | d->ensureSizeIsAtLeastSizeHint(); |
391 | } |
392 | } |
393 | |
394 | |
395 | /*! |
396 | Sets the cancel button to the push button, \a cancelButton. The |
397 | progress dialog takes ownership of this button which will be deleted |
398 | when necessary, so do not pass the address of an object that is on |
399 | the stack, i.e. use new() to create the button. If \nullptr is passed, |
400 | no cancel button will be shown. |
401 | |
402 | \sa setCancelButtonText() |
403 | */ |
404 | |
405 | void QProgressDialog::setCancelButton(QPushButton *cancelButton) |
406 | { |
407 | Q_D(QProgressDialog); |
408 | if (d->cancel == cancelButton) { |
409 | if (Q_UNLIKELY(cancelButton)) |
410 | qWarning(msg: "QProgressDialog::setCancelButton: Attempt to set the same button again" ); |
411 | return; |
412 | } |
413 | delete d->cancel; |
414 | d->cancel = cancelButton; |
415 | if (cancelButton) { |
416 | connect(sender: d->cancel, SIGNAL(clicked()), receiver: this, SIGNAL(canceled())); |
417 | #ifndef QT_NO_SHORTCUT |
418 | // FIXME: This only registers the primary key sequence of the cancel action |
419 | d->escapeShortcut = new QShortcut(QKeySequence::Cancel, this, SIGNAL(canceled())); |
420 | #endif |
421 | } else { |
422 | #ifndef QT_NO_SHORTCUT |
423 | delete d->escapeShortcut; |
424 | d->escapeShortcut = nullptr; |
425 | #endif |
426 | } |
427 | d->adoptChildWidget(c: cancelButton); |
428 | } |
429 | |
430 | /*! |
431 | Sets the cancel button's text to \a cancelButtonText. If the text |
432 | is set to QString() then it will cause the cancel button to be |
433 | hidden and deleted. |
434 | |
435 | \sa setCancelButton() |
436 | */ |
437 | |
438 | void QProgressDialog::setCancelButtonText(const QString &cancelButtonText) |
439 | { |
440 | Q_D(QProgressDialog); |
441 | d->useDefaultCancelText = false; |
442 | d->setCancelButtonText(cancelButtonText); |
443 | } |
444 | |
445 | void QProgressDialogPrivate::setCancelButtonText(const QString &cancelButtonText) |
446 | { |
447 | Q_Q(QProgressDialog); |
448 | |
449 | if (!cancelButtonText.isNull()) { |
450 | if (cancel) { |
451 | cancel->setText(cancelButtonText); |
452 | } else { |
453 | q->setCancelButton(new QPushButton(cancelButtonText, q)); |
454 | } |
455 | } else { |
456 | q->setCancelButton(nullptr); |
457 | } |
458 | ensureSizeIsAtLeastSizeHint(); |
459 | } |
460 | |
461 | |
462 | /*! |
463 | Sets the progress bar widget to \a bar. The progress dialog resizes to |
464 | fit. The progress dialog takes ownership of the progress \a bar which |
465 | will be deleted when necessary, so do not use a progress bar |
466 | allocated on the stack. |
467 | */ |
468 | |
469 | void QProgressDialog::setBar(QProgressBar *bar) |
470 | { |
471 | Q_D(QProgressDialog); |
472 | if (Q_UNLIKELY(!bar)) { |
473 | qWarning(msg: "QProgressDialog::setBar: Cannot set a null progress bar" ); |
474 | return; |
475 | } |
476 | #ifndef QT_NO_DEBUG |
477 | if (Q_UNLIKELY(value() > 0)) |
478 | qWarning(msg: "QProgressDialog::setBar: Cannot set a new progress bar " |
479 | "while the old one is active" ); |
480 | #endif |
481 | if (Q_UNLIKELY(bar == d->bar)) { |
482 | qWarning(msg: "QProgressDialog::setBar: Attempt to set the same progress bar again" ); |
483 | return; |
484 | } |
485 | delete d->bar; |
486 | d->bar = bar; |
487 | d->adoptChildWidget(c: bar); |
488 | } |
489 | |
490 | void QProgressDialogPrivate::adoptChildWidget(QWidget *c) |
491 | { |
492 | Q_Q(QProgressDialog); |
493 | |
494 | if (c) { |
495 | if (c->parentWidget() == q) |
496 | c->hide(); // until after ensureSizeIsAtLeastSizeHint() |
497 | else |
498 | c->setParent(parent: q, f: { }); |
499 | } |
500 | ensureSizeIsAtLeastSizeHint(); |
501 | if (c) |
502 | c->show(); |
503 | } |
504 | |
505 | void QProgressDialogPrivate::ensureSizeIsAtLeastSizeHint() |
506 | { |
507 | Q_Q(QProgressDialog); |
508 | |
509 | QSize size = q->sizeHint(); |
510 | if (q->isVisible()) |
511 | size = size.expandedTo(otherSize: q->size()); |
512 | q->resize(size); |
513 | } |
514 | |
515 | |
516 | /*! |
517 | \property QProgressDialog::wasCanceled |
518 | \brief whether the dialog was canceled |
519 | */ |
520 | |
521 | bool QProgressDialog::wasCanceled() const |
522 | { |
523 | Q_D(const QProgressDialog); |
524 | return d->cancellation_flag; |
525 | } |
526 | |
527 | |
528 | /*! |
529 | \property QProgressDialog::maximum |
530 | \brief the highest value represented by the progress bar |
531 | |
532 | The default is 100. |
533 | |
534 | \sa minimum, setRange() |
535 | */ |
536 | |
537 | int QProgressDialog::maximum() const |
538 | { |
539 | Q_D(const QProgressDialog); |
540 | return d->bar->maximum(); |
541 | } |
542 | |
543 | void QProgressDialog::setMaximum(int maximum) |
544 | { |
545 | Q_D(QProgressDialog); |
546 | d->bar->setMaximum(maximum); |
547 | } |
548 | |
549 | /*! |
550 | \property QProgressDialog::minimum |
551 | \brief the lowest value represented by the progress bar |
552 | |
553 | The default is 0. |
554 | |
555 | \sa maximum, setRange() |
556 | */ |
557 | |
558 | int QProgressDialog::minimum() const |
559 | { |
560 | Q_D(const QProgressDialog); |
561 | return d->bar->minimum(); |
562 | } |
563 | |
564 | void QProgressDialog::setMinimum(int minimum) |
565 | { |
566 | Q_D(QProgressDialog); |
567 | d->bar->setMinimum(minimum); |
568 | } |
569 | |
570 | /*! |
571 | Sets the progress dialog's minimum and maximum values |
572 | to \a minimum and \a maximum, respectively. |
573 | |
574 | If \a maximum is smaller than \a minimum, \a minimum becomes the only |
575 | legal value. |
576 | |
577 | If the current value falls outside the new range, the progress |
578 | dialog is reset with reset(). |
579 | |
580 | \sa minimum, maximum |
581 | */ |
582 | void QProgressDialog::setRange(int minimum, int maximum) |
583 | { |
584 | Q_D(QProgressDialog); |
585 | d->bar->setRange(minimum, maximum); |
586 | } |
587 | |
588 | |
589 | /*! |
590 | Resets the progress dialog. |
591 | The progress dialog becomes hidden if autoClose() is true. |
592 | |
593 | \sa setAutoClose(), setAutoReset() |
594 | */ |
595 | |
596 | void QProgressDialog::reset() |
597 | { |
598 | Q_D(QProgressDialog); |
599 | if (d->autoClose || d->forceHide) |
600 | hide(); |
601 | d->bar->reset(); |
602 | d->cancellation_flag = false; |
603 | d->shown_once = false; |
604 | d->setValue_called = false; |
605 | d->forceTimer->stop(); |
606 | |
607 | /* |
608 | I wish we could disconnect the user slot provided to open() here but |
609 | unfortunately reset() is usually called before the slot has been invoked. |
610 | (reset() is itself invoked when canceled() is emitted.) |
611 | */ |
612 | if (d->receiverToDisconnectOnClose) |
613 | QMetaObject::invokeMethod(obj: this, member: "_q_disconnectOnClose" , type: Qt::QueuedConnection); |
614 | } |
615 | |
616 | /*! |
617 | Resets the progress dialog. wasCanceled() becomes true until |
618 | the progress dialog is reset. |
619 | The progress dialog becomes hidden. |
620 | */ |
621 | |
622 | void QProgressDialog::cancel() |
623 | { |
624 | Q_D(QProgressDialog); |
625 | d->forceHide = true; |
626 | reset(); |
627 | d->forceHide = false; |
628 | d->cancellation_flag = true; |
629 | } |
630 | |
631 | |
632 | int QProgressDialog::value() const |
633 | { |
634 | Q_D(const QProgressDialog); |
635 | return d->bar->value(); |
636 | } |
637 | |
638 | /*! |
639 | \property QProgressDialog::value |
640 | \brief the current amount of progress made. |
641 | |
642 | For the progress dialog to work as expected, you should initially set |
643 | this property to QProgressDialog::minimum() and finally set it to |
644 | QProgressDialog::maximum(); you can call setValue() any number of times |
645 | in-between. |
646 | |
647 | \warning If the progress dialog is modal |
648 | (see QProgressDialog::QProgressDialog()), |
649 | setValue() calls QCoreApplication::processEvents(), so take care that |
650 | this does not cause undesirable re-entrancy in your code. For example, |
651 | don't use a QProgressDialog inside a paintEvent()! |
652 | |
653 | \sa minimum, maximum |
654 | */ |
655 | void QProgressDialog::setValue(int progress) |
656 | { |
657 | Q_D(QProgressDialog); |
658 | if (d->setValue_called && progress == d->bar->value()) |
659 | return; |
660 | |
661 | d->bar->setValue(progress); |
662 | |
663 | if (d->shown_once) { |
664 | if (isModal() && !d->processingEvents) { |
665 | const QScopedValueRollback<bool> guard(d->processingEvents, true); |
666 | QCoreApplication::processEvents(); |
667 | } |
668 | } else { |
669 | if ((!d->setValue_called && progress == 0 /* for compat with Qt < 5.4 */) || progress == minimum()) { |
670 | d->starttime.start(); |
671 | d->forceTimer->start(msec: d->showTime); |
672 | d->setValue_called = true; |
673 | return; |
674 | } else { |
675 | d->setValue_called = true; |
676 | bool need_show; |
677 | int elapsed = d->starttime.elapsed(); |
678 | if (elapsed >= d->showTime) { |
679 | need_show = true; |
680 | } else { |
681 | if (elapsed > minWaitTime) { |
682 | int estimate; |
683 | int totalSteps = maximum() - minimum(); |
684 | int myprogress = progress - minimum(); |
685 | if (myprogress == 0) myprogress = 1; |
686 | if ((totalSteps - myprogress) >= INT_MAX / elapsed) |
687 | estimate = (totalSteps - myprogress) / myprogress * elapsed; |
688 | else |
689 | estimate = elapsed * (totalSteps - myprogress) / myprogress; |
690 | need_show = estimate >= d->showTime; |
691 | } else { |
692 | need_show = false; |
693 | } |
694 | } |
695 | if (need_show) { |
696 | d->ensureSizeIsAtLeastSizeHint(); |
697 | show(); |
698 | d->shown_once = true; |
699 | } |
700 | } |
701 | } |
702 | |
703 | if (progress == d->bar->maximum() && d->autoReset) |
704 | reset(); |
705 | } |
706 | |
707 | /*! |
708 | Returns a size that fits the contents of the progress dialog. |
709 | The progress dialog resizes itself as required, so you should not |
710 | need to call this yourself. |
711 | */ |
712 | |
713 | QSize QProgressDialog::sizeHint() const |
714 | { |
715 | Q_D(const QProgressDialog); |
716 | QSize labelSize = d->label ? d->label->sizeHint() : QSize(0, 0); |
717 | QSize barSize = d->bar->sizeHint(); |
718 | int marginBottom = style()->pixelMetric(metric: QStyle::PM_LayoutBottomMargin, option: 0, widget: this); |
719 | int spacing = style()->pixelMetric(metric: QStyle::PM_LayoutVerticalSpacing, option: 0, widget: this); |
720 | int marginLeft = style()->pixelMetric(metric: QStyle::PM_LayoutLeftMargin, option: 0, widget: this); |
721 | int marginRight = style()->pixelMetric(metric: QStyle::PM_LayoutRightMargin, option: 0, widget: this); |
722 | |
723 | int height = marginBottom * 2 + barSize.height() + labelSize.height() + spacing; |
724 | if (d->cancel) |
725 | height += d->cancel->sizeHint().height() + spacing; |
726 | return QSize(qMax(a: 200, b: labelSize.width() + marginLeft + marginRight), height); |
727 | } |
728 | |
729 | /*!\reimp |
730 | */ |
731 | void QProgressDialog::resizeEvent(QResizeEvent *) |
732 | { |
733 | Q_D(QProgressDialog); |
734 | d->layout(); |
735 | } |
736 | |
737 | /*! |
738 | \reimp |
739 | */ |
740 | void QProgressDialog::changeEvent(QEvent *ev) |
741 | { |
742 | Q_D(QProgressDialog); |
743 | if (ev->type() == QEvent::StyleChange) { |
744 | d->layout(); |
745 | } else if (ev->type() == QEvent::LanguageChange) { |
746 | d->retranslateStrings(); |
747 | } |
748 | QDialog::changeEvent(ev); |
749 | } |
750 | |
751 | /*! |
752 | \property QProgressDialog::minimumDuration |
753 | \brief the time that must pass before the dialog appears |
754 | |
755 | If the expected duration of the task is less than the |
756 | minimumDuration, the dialog will not appear at all. This prevents |
757 | the dialog popping up for tasks that are quickly over. For tasks |
758 | that are expected to exceed the minimumDuration, the dialog will |
759 | pop up after the minimumDuration time or as soon as any progress |
760 | is set. |
761 | |
762 | If set to 0, the dialog is always shown as soon as any progress is |
763 | set. The default is 4000 milliseconds. |
764 | */ |
765 | void QProgressDialog::setMinimumDuration(int ms) |
766 | { |
767 | Q_D(QProgressDialog); |
768 | d->showTime = ms; |
769 | if (d->bar->value() == d->bar->minimum()) { |
770 | d->forceTimer->stop(); |
771 | d->forceTimer->start(msec: ms); |
772 | } |
773 | } |
774 | |
775 | int QProgressDialog::minimumDuration() const |
776 | { |
777 | Q_D(const QProgressDialog); |
778 | return d->showTime; |
779 | } |
780 | |
781 | |
782 | /*! |
783 | \reimp |
784 | */ |
785 | |
786 | void QProgressDialog::closeEvent(QCloseEvent *e) |
787 | { |
788 | emit canceled(); |
789 | QDialog::closeEvent(e); |
790 | } |
791 | |
792 | /*! |
793 | \property QProgressDialog::autoReset |
794 | \brief whether the progress dialog calls reset() as soon as value() equals maximum() |
795 | |
796 | The default is true. |
797 | |
798 | \sa setAutoClose() |
799 | */ |
800 | |
801 | void QProgressDialog::setAutoReset(bool b) |
802 | { |
803 | Q_D(QProgressDialog); |
804 | d->autoReset = b; |
805 | } |
806 | |
807 | bool QProgressDialog::autoReset() const |
808 | { |
809 | Q_D(const QProgressDialog); |
810 | return d->autoReset; |
811 | } |
812 | |
813 | /*! |
814 | \property QProgressDialog::autoClose |
815 | \brief whether the dialog gets hidden by reset() |
816 | |
817 | The default is true. |
818 | |
819 | \sa setAutoReset() |
820 | */ |
821 | |
822 | void QProgressDialog::setAutoClose(bool close) |
823 | { |
824 | Q_D(QProgressDialog); |
825 | d->autoClose = close; |
826 | } |
827 | |
828 | bool QProgressDialog::autoClose() const |
829 | { |
830 | Q_D(const QProgressDialog); |
831 | return d->autoClose; |
832 | } |
833 | |
834 | /*! |
835 | \reimp |
836 | */ |
837 | |
838 | void QProgressDialog::showEvent(QShowEvent *e) |
839 | { |
840 | Q_D(QProgressDialog); |
841 | QDialog::showEvent(e); |
842 | d->ensureSizeIsAtLeastSizeHint(); |
843 | d->forceTimer->stop(); |
844 | } |
845 | |
846 | /*! |
847 | Shows the dialog if it is still hidden after the algorithm has been started |
848 | and minimumDuration milliseconds have passed. |
849 | |
850 | \sa setMinimumDuration() |
851 | */ |
852 | |
853 | void QProgressDialog::forceShow() |
854 | { |
855 | Q_D(QProgressDialog); |
856 | d->forceTimer->stop(); |
857 | if (d->shown_once || d->cancellation_flag) |
858 | return; |
859 | |
860 | show(); |
861 | d->shown_once = true; |
862 | } |
863 | |
864 | /*! |
865 | \since 4.5 |
866 | |
867 | Opens the dialog and connects its canceled() signal to the slot specified |
868 | by \a receiver and \a member. |
869 | |
870 | The signal will be disconnected from the slot when the dialog is closed. |
871 | */ |
872 | void QProgressDialog::open(QObject *receiver, const char *member) |
873 | { |
874 | Q_D(QProgressDialog); |
875 | connect(sender: this, SIGNAL(canceled()), receiver, member); |
876 | d->receiverToDisconnectOnClose = receiver; |
877 | d->memberToDisconnectOnClose = member; |
878 | QDialog::open(); |
879 | } |
880 | |
881 | QT_END_NAMESPACE |
882 | |
883 | #include "moc_qprogressdialog.cpp" |
884 | |