1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtGui module of the Qt Toolkit.
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qprogressdialog.h"
44
#ifndef QT_NO_PROGRESSDIALOG
46
#include "qshortcut.h"
48
#include "qdrawutil.h"
50
#include "qprogressbar.h"
51
#include "qapplication.h"
53
#include "qpushbutton.h"
56
#include "qelapsedtimer.h"
57
#include <private/qdialog_p.h>
62
// If the operation is expected to take this long (as predicted by
63
// progress time), show the progress dialog.
64
static const int defaultShowTime = 4000;
65
// Wait at least this long before attempting to make a prediction.
66
static const int minWaitTime = 50;
68
class QProgressDialogPrivate : public QDialogPrivate
70
Q_DECLARE_PUBLIC(QProgressDialog)
73
QProgressDialogPrivate() : label(0), cancel(0), bar(0),
75
cancellation_flag(false),
76
showTime(defaultShowTime),
77
#ifndef QT_NO_SHORTCUT
80
useDefaultCancelText(false)
84
void init(const QString &labelText, const QString &cancelText, int min, int max);
86
void retranslateStrings();
87
void _q_disconnectOnClose();
94
bool cancellation_flag;
95
QElapsedTimer starttime;
103
#ifndef QT_NO_SHORTCUT
104
QShortcut *escapeShortcut;
106
bool useDefaultCancelText;
107
QPointer<QObject> receiverToDisconnectOnClose;
108
QByteArray memberToDisconnectOnClose;
111
void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText,
114
Q_Q(QProgressDialog);
115
label = new QLabel(labelText, q);
116
int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, q);
117
label->setAlignment(Qt::Alignment(align));
118
bar = new QProgressBar(q);
119
bar->setRange(min, max);
123
QObject::connect(q, SIGNAL(canceled()), q, SLOT(cancel()));
124
forceTimer = new QTimer(q);
125
QObject::connect(forceTimer, SIGNAL(timeout()), q, SLOT(forceShow()));
126
if (useDefaultCancelText) {
127
retranslateStrings();
129
q->setCancelButtonText(cancelText);
133
void QProgressDialogPrivate::layout()
135
Q_Q(QProgressDialog);
136
int sp = q->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
137
int mtb = q->style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin);
138
int mlr = qMin(q->width() / 10, mtb);
139
const bool centered =
140
bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, q));
142
int additionalSpacing = 0;
143
QSize cs = cancel ? cancel->sizeHint() : QSize(0,0);
144
QSize bh = bar->sizeHint();
148
// Find spacing and sizes that fit. It is important that a progress
149
// dialog can be made very small if the user demands it so.
150
for (int attempt=5; attempt--;) {
151
cspc = cancel ? cs.height() + sp : 0;
152
lh = qMax(0, q->height() - mtb - bh.height() - sp - cspc);
154
if (lh < q->height()/4) {
159
cs.setHeight(qMax(4,cs.height()-sp-2));
161
bh.setHeight(qMax(4,bh.height()-sp-1));
169
centered ? q->width()/2 - cs.width()/2 : q->width() - mlr - cs.width(),
170
q->height() - mtb - cs.height(),
171
cs.width(), cs.height());
175
label->setGeometry(mlr, additionalSpacing, q->width() - mlr * 2, lh);
176
bar->setGeometry(mlr, lh + sp + additionalSpacing, q->width() - mlr * 2, bh.height());
179
void QProgressDialogPrivate::retranslateStrings()
181
Q_Q(QProgressDialog);
182
if (useDefaultCancelText)
183
q->setCancelButtonText(QProgressDialog::tr("Cancel"));
186
void QProgressDialogPrivate::_q_disconnectOnClose()
188
Q_Q(QProgressDialog);
189
if (receiverToDisconnectOnClose) {
190
QObject::disconnect(q, SIGNAL(canceled()), receiverToDisconnectOnClose,
191
memberToDisconnectOnClose);
192
receiverToDisconnectOnClose = 0;
194
memberToDisconnectOnClose.clear();
198
\class QProgressDialog
199
\brief The QProgressDialog class provides feedback on the progress of a slow operation.
200
\ingroup standard-dialogs
204
A progress dialog is used to give the user an indication of how long
205
an operation is going to take, and to demonstrate that the
206
application has not frozen. It can also give the user an opportunity
207
to abort the operation.
209
A common problem with progress dialogs is that it is difficult to know
210
when to use them; operations take different amounts of time on different
211
hardware. QProgressDialog offers a solution to this problem:
212
it estimates the time the operation will take (based on time for
213
steps), and only shows itself if that estimate is beyond minimumDuration()
214
(4 seconds by default).
216
Use setMinimum() and setMaximum() or the constructor to set the number of
217
"steps" in the operation and call setValue() as the operation
218
progresses. The number of steps can be chosen arbitrarily. It can be the
219
number of files copied, the number of bytes received, the number of
220
iterations through the main loop of your algorithm, or some other
221
suitable unit. Progress starts at the value set by setMinimum(),
222
and the progress dialog shows that the operation has finished when
223
you call setValue() with the value set by setMaximum() as its argument.
225
The dialog automatically resets and hides itself at the end of the
226
operation. Use setAutoReset() and setAutoClose() to change this
227
behavior. Note that if you set a new maximum (using setMaximum() or
228
setRange()) that equals your current value(), the dialog will not
231
There are two ways of using QProgressDialog: modal and modeless.
233
Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler
234
to use for the programmer. Do the operation in a loop, call \l setValue() at
235
intervals, and check for cancellation with wasCanceled(). For example:
237
\snippet dialogs/dialogs.cpp 3
239
A modeless progress dialog is suitable for operations that take
240
place in the background, where the user is able to interact with the
241
application. Such operations are typically based on QTimer (or
242
QObject::timerEvent()), QSocketNotifier, or QUrlOperator; or performed
243
in a separate thread. A QProgressBar in the status bar of your main window
244
is often an alternative to a modeless progress dialog.
246
You need to have an event loop to be running, connect the
247
canceled() signal to a slot that stops the operation, and call \l
248
setValue() at intervals. For example:
250
\snippet dialogs/dialogs.cpp 4
252
\snippet dialogs/dialogs.cpp 5
254
\snippet dialogs/dialogs.cpp 6
256
In both modes the progress dialog may be customized by
257
replacing the child widgets with custom widgets by using setLabel(),
258
setBar(), and setCancelButton().
259
The functions setLabelText() and setCancelButtonText()
262
\image fusion-progressdialog.png A progress dialog shown in the Fusion widget style.
264
\sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator},
265
{Find Files Example}, {Pixelator Example}
270
Constructs a progress dialog.
274
\li The label text is empty.
275
\li The cancel button text is (translated) "Cancel".
280
The \a parent argument is dialog's parent widget. The widget flags, \a f, are
281
passed to the QDialog::QDialog() constructor.
283
\sa setLabelText(), setCancelButtonText(), setCancelButton(),
284
setMinimum(), setMaximum()
287
QProgressDialog::QProgressDialog(QWidget *parent, Qt::WindowFlags f)
288
: QDialog(*(new QProgressDialogPrivate), parent, f)
290
Q_D(QProgressDialog);
291
d->useDefaultCancelText = true;
292
d->init(QString::fromLatin1(""), QString(), 0, 100);
296
Constructs a progress dialog.
298
The \a labelText is the text used to remind the user what is progressing.
300
The \a cancelButtonText is the text to display on the cancel button. If
301
QString() is passed then no cancel button is shown.
303
The \a minimum and \a maximum is the number of steps in the operation for
304
which this progress dialog shows progress. For example, if the
305
operation is to examine 50 files, this value minimum value would be 0,
306
and the maximum would be 50. Before examining the first file, call
307
setValue(0). As each file is processed call setValue(1), setValue(2),
308
etc., finally calling setValue(50) after examining the last file.
310
The \a parent argument is the dialog's parent widget. The parent, \a parent, and
311
widget flags, \a f, are passed to the QDialog::QDialog() constructor.
313
\sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
314
setMinimum(), setMaximum()
317
QProgressDialog::QProgressDialog(const QString &labelText,
318
const QString &cancelButtonText,
319
int minimum, int maximum,
320
QWidget *parent, Qt::WindowFlags f)
321
: QDialog(*(new QProgressDialogPrivate), parent, f)
323
Q_D(QProgressDialog);
324
d->init(labelText, cancelButtonText, minimum, maximum);
329
Destroys the progress dialog.
332
QProgressDialog::~QProgressDialog()
337
\fn void QProgressDialog::canceled()
339
This signal is emitted when the cancel button is clicked.
340
It is connected to the cancel() slot by default.
347
Sets the label to \a label. The progress dialog resizes to fit. The
348
label becomes owned by the progress dialog and will be deleted when
349
necessary, so do not pass the address of an object on the stack.
354
void QProgressDialog::setLabel(QLabel *label)
356
Q_D(QProgressDialog);
360
if (label->parentWidget() == this) {
361
label->hide(); // until we resize
363
label->setParent(this, 0);
366
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
367
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
375
\property QProgressDialog::labelText
376
\brief the label's text
378
The default text is an empty string.
381
QString QProgressDialog::labelText() const
383
Q_D(const QProgressDialog);
385
return d->label->text();
389
void QProgressDialog::setLabelText(const QString &text)
391
Q_D(QProgressDialog);
393
d->label->setText(text);
394
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
395
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
402
Sets the cancel button to the push button, \a cancelButton. The
403
progress dialog takes ownership of this button which will be deleted
404
when necessary, so do not pass the address of an object that is on
405
the stack, i.e. use new() to create the button. If 0 is passed then
406
no cancel button will be shown.
408
\sa setCancelButtonText()
411
void QProgressDialog::setCancelButton(QPushButton *cancelButton)
413
Q_D(QProgressDialog);
415
d->cancel = cancelButton;
417
if (cancelButton->parentWidget() == this) {
418
cancelButton->hide(); // until we resize
420
cancelButton->setParent(this, 0);
422
connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled()));
423
#ifndef QT_NO_SHORTCUT
424
d->escapeShortcut = new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled()));
427
#ifndef QT_NO_SHORTCUT
428
delete d->escapeShortcut;
429
d->escapeShortcut = 0;
432
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
433
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
436
cancelButton->show();
440
Sets the cancel button's text to \a cancelButtonText. If the text
441
is set to QString() then it will cause the cancel button to be
444
\sa setCancelButton()
447
void QProgressDialog::setCancelButtonText(const QString &cancelButtonText)
449
Q_D(QProgressDialog);
450
d->useDefaultCancelText = false;
452
if (!cancelButtonText.isNull()) {
454
d->cancel->setText(cancelButtonText);
456
setCancelButton(new QPushButton(cancelButtonText, this));
461
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
462
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
468
Sets the progress bar widget to \a bar. The progress dialog resizes to
469
fit. The progress dialog takes ownership of the progress \a bar which
470
will be deleted when necessary, so do not use a progress bar
471
allocated on the stack.
474
void QProgressDialog::setBar(QProgressBar *bar)
476
Q_D(QProgressDialog);
478
qWarning("QProgressDialog::setBar: Cannot set a null progress bar");
483
qWarning("QProgressDialog::setBar: Cannot set a new progress bar "
484
"while the old one is active");
488
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
489
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
495
\property QProgressDialog::wasCanceled
496
\brief whether the dialog was canceled
499
bool QProgressDialog::wasCanceled() const
501
Q_D(const QProgressDialog);
502
return d->cancellation_flag;
507
\property QProgressDialog::maximum
508
\brief the highest value represented by the progress bar
512
\sa minimum, setRange()
515
int QProgressDialog::maximum() const
517
Q_D(const QProgressDialog);
518
return d->bar->maximum();
521
void QProgressDialog::setMaximum(int maximum)
523
Q_D(QProgressDialog);
524
d->bar->setMaximum(maximum);
528
\property QProgressDialog::minimum
529
\brief the lowest value represented by the progress bar
533
\sa maximum, setRange()
536
int QProgressDialog::minimum() const
538
Q_D(const QProgressDialog);
539
return d->bar->minimum();
542
void QProgressDialog::setMinimum(int minimum)
544
Q_D(QProgressDialog);
545
d->bar->setMinimum(minimum);
549
Sets the progress dialog's minimum and maximum values
550
to \a minimum and \a maximum, respectively.
552
If \a maximum is smaller than \a minimum, \a minimum becomes the only
555
If the current value falls outside the new range, the progress
556
dialog is reset with reset().
560
void QProgressDialog::setRange(int minimum, int maximum)
562
Q_D(QProgressDialog);
563
d->bar->setRange(minimum, maximum);
568
Resets the progress dialog.
569
The progress dialog becomes hidden if autoClose() is true.
571
\sa setAutoClose(), setAutoReset()
574
void QProgressDialog::reset()
576
Q_D(QProgressDialog);
580
parentWidget()->setCursor(d->parentCursor);
583
if (d->autoClose || d->forceHide)
586
d->cancellation_flag = false;
587
d->shown_once = false;
588
d->forceTimer->stop();
591
I wish we could disconnect the user slot provided to open() here but
592
unfortunately reset() is usually called before the slot has been invoked.
593
(reset() is itself invoked when canceled() is emitted.)
595
if (d->receiverToDisconnectOnClose)
596
QMetaObject::invokeMethod(this, "_q_disconnectOnClose", Qt::QueuedConnection);
600
Resets the progress dialog. wasCanceled() becomes true until
601
the progress dialog is reset.
602
The progress dialog becomes hidden.
605
void QProgressDialog::cancel()
607
Q_D(QProgressDialog);
610
d->forceHide = false;
611
d->cancellation_flag = true;
615
int QProgressDialog::value() const
617
Q_D(const QProgressDialog);
618
return d->bar->value();
622
\property QProgressDialog::value
623
\brief the current amount of progress made.
625
For the progress dialog to work as expected, you should initially set
626
this property to 0 and finally set it to
627
QProgressDialog::maximum(); you can call setValue() any number of times
630
\warning If the progress dialog is modal
631
(see QProgressDialog::QProgressDialog()),
632
setValue() calls QApplication::processEvents(), so take care that
633
this does not cause undesirable re-entrancy in your code. For example,
634
don't use a QProgressDialog inside a paintEvent()!
638
void QProgressDialog::setValue(int progress)
640
Q_D(QProgressDialog);
641
if (progress == d->bar->value()
642
|| (d->bar->value() == -1 && progress == d->bar->maximum()))
645
d->bar->setValue(progress);
649
QApplication::processEvents();
652
d->starttime.start();
653
d->forceTimer->start(d->showTime);
657
int elapsed = d->starttime.elapsed();
658
if (elapsed >= d->showTime) {
661
if (elapsed > minWaitTime) {
663
int totalSteps = maximum() - minimum();
664
int myprogress = progress - minimum();
665
if ((totalSteps - myprogress) >= INT_MAX / elapsed)
666
estimate = (totalSteps - myprogress) / myprogress * elapsed;
668
estimate = elapsed * (totalSteps - myprogress) / myprogress;
669
need_show = estimate >= d->showTime;
675
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
676
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
679
d->shown_once = true;
683
QApplication::flush();
687
if (progress == d->bar->maximum() && d->autoReset)
692
Returns a size that fits the contents of the progress dialog.
693
The progress dialog resizes itself as required, so you should not
694
need to call this yourself.
697
QSize QProgressDialog::sizeHint() const
699
Q_D(const QProgressDialog);
700
QSize sh = d->label ? d->label->sizeHint() : QSize(0, 0);
701
QSize bh = d->bar->sizeHint();
702
int margin = style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin);
703
int spacing = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
704
int h = margin * 2 + bh.height() + sh.height() + spacing;
706
h += d->cancel->sizeHint().height() + spacing;
707
return QSize(qMax(200, sh.width() + 2 * margin), h);
712
void QProgressDialog::resizeEvent(QResizeEvent *)
714
Q_D(QProgressDialog);
721
void QProgressDialog::changeEvent(QEvent *ev)
723
Q_D(QProgressDialog);
724
if (ev->type() == QEvent::StyleChange) {
726
} else if (ev->type() == QEvent::LanguageChange) {
727
d->retranslateStrings();
729
QDialog::changeEvent(ev);
733
\property QProgressDialog::minimumDuration
734
\brief the time that must pass before the dialog appears
736
If the expected duration of the task is less than the
737
minimumDuration, the dialog will not appear at all. This prevents
738
the dialog popping up for tasks that are quickly over. For tasks
739
that are expected to exceed the minimumDuration, the dialog will
740
pop up after the minimumDuration time or as soon as any progress
743
If set to 0, the dialog is always shown as soon as any progress is
744
set. The default is 4000 milliseconds.
746
void QProgressDialog::setMinimumDuration(int ms)
748
Q_D(QProgressDialog);
750
if (d->bar->value() == 0) {
751
d->forceTimer->stop();
752
d->forceTimer->start(ms);
756
int QProgressDialog::minimumDuration() const
758
Q_D(const QProgressDialog);
767
void QProgressDialog::closeEvent(QCloseEvent *e)
770
QDialog::closeEvent(e);
774
\property QProgressDialog::autoReset
775
\brief whether the progress dialog calls reset() as soon as value() equals maximum()
782
void QProgressDialog::setAutoReset(bool b)
784
Q_D(QProgressDialog);
788
bool QProgressDialog::autoReset() const
790
Q_D(const QProgressDialog);
795
\property QProgressDialog::autoClose
796
\brief whether the dialog gets hidden by reset()
803
void QProgressDialog::setAutoClose(bool close)
805
Q_D(QProgressDialog);
806
d->autoClose = close;
809
bool QProgressDialog::autoClose() const
811
Q_D(const QProgressDialog);
819
void QProgressDialog::showEvent(QShowEvent *e)
821
Q_D(QProgressDialog);
822
QDialog::showEvent(e);
823
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
824
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
826
d->forceTimer->stop();
830
Shows the dialog if it is still hidden after the algorithm has been started
831
and minimumDuration milliseconds have passed.
833
\sa setMinimumDuration()
836
void QProgressDialog::forceShow()
838
Q_D(QProgressDialog);
839
d->forceTimer->stop();
840
if (d->shown_once || d->cancellation_flag)
844
d->shown_once = true;
851
Opens the dialog and connects its accepted() signal to the slot specified
852
by \a receiver and \a member.
854
The signal will be disconnected from the slot when the dialog is closed.
856
void QProgressDialog::open(QObject *receiver, const char *member)
858
Q_D(QProgressDialog);
859
connect(this, SIGNAL(canceled()), receiver, member);
860
d->receiverToDisconnectOnClose = receiver;
861
d->memberToDisconnectOnClose = member;
867
#include "moc_qprogressdialog.cpp"
869
#endif // QT_NO_PROGRESSDIALOG