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