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