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