1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the dialog module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qprogressdialog.h"
31
#ifndef QT_NO_PROGRESSDIALOG
33
#include "qshortcut.h"
35
#include "qdrawutil.h"
36
#include "qdatetime.h"
38
#include "qprogressbar.h"
39
#include "qapplication.h"
41
#include "qpushbutton.h"
44
#include <private/qdialog_p.h>
47
// If the operation is expected to take this long (as predicted by
48
// progress time), show the progress dialog.
49
static const int defaultShowTime = 4000;
50
// Wait at least this long before attempting to make a prediction.
51
static const int minWaitTime = 50;
53
// Various layout values
54
static const int margin_lr = 10;
55
static const int margin_tb = 10;
56
static const int spacing = 4;
59
class QProgressDialogPrivate : public QDialogPrivate
61
Q_DECLARE_PUBLIC(QProgressDialog)
63
QProgressDialogPrivate() : label(0), cancel(0), bar(0),
65
cancellation_flag(false),
66
showTime(defaultShowTime)
70
void init(const QString &labelText, const QString &cancelText, int min, int max);
78
bool cancellation_flag;
89
void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText,
93
label = new QLabel(labelText, q);
94
int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, q);
95
label->setAlignment(Qt::Alignment(align));
96
bar = new QProgressBar(q);
97
bar->setRange(min, max);
101
q->setCancelButtonText(cancelText);
102
QObject::connect(q, SIGNAL(canceled()), q, SLOT(cancel()));
103
forceTimer = new QTimer(q);
104
QObject::connect(forceTimer, SIGNAL(timeout()), q, SLOT(forceShow()));
107
void QProgressDialogPrivate::layout()
109
Q_Q(QProgressDialog);
112
int mlr = qMin(q->width() / 10, margin_lr);
113
const bool centered =
114
bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, q));
116
QSize cs = cancel ? cancel->sizeHint() : QSize(0,0);
117
QSize bh = bar->sizeHint();
121
// Find spacing and sizes that fit. It is important that a progress
122
// dialog can be made very small if the user demands it so.
123
for (int attempt=5; attempt--;) {
124
cspc = cancel ? cs.height() + sp : 0;
125
lh = qMax(0, q->height() - mtb - bh.height() - sp - cspc);
127
if (lh < q->height()/4) {
132
cs.setHeight(qMax(4,cs.height()-sp-2));
134
bh.setHeight(qMax(4,bh.height()-sp-1));
142
centered ? q->width()/2 - cs.width()/2 : q->width() - mlr - cs.width(),
143
q->height() - mtb - cs.height() + sp,
144
cs.width(), cs.height());
147
label->setGeometry(mlr, 0, q->width()-mlr*2, lh);
148
bar->setGeometry(mlr, lh+sp, q->width()-mlr*2, bh.height());
153
\class QProgressDialog
154
\brief The QProgressDialog class provides feedback on the progress of a slow operation.
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.
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).
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.
179
The dialog automatically resets and hides itself at the end of the
180
operation. Use setAutoReset() and setAutoClose() to change this
183
There are two ways of using QProgressDialog: modal and modeless.
185
Using a modal QProgressDialog is simpler for the programmer, but you
186
must call QApplication::processEvents() or
187
QEventLoop::processEvents(ExcludeUserInput) to keep the event loop
188
running to ensure that the application doesn't freeze. Do the
189
operation in a loop, call \l setValue() at intervals, and check
190
for cancelation with wasCanceled(). For example:
192
\quotefromfile snippets/dialogs/dialogs.cpp
193
\skipto QProgressDialog progress("Copying files...", "Abort Copy", 0, numFiles, this);
194
\printuntil progress.setValue(numFiles);
196
A modeless progress dialog is suitable for operations that take
197
place in the background, where the user is able to interact with the
198
application. Such operations are typically based on QTimer (or
199
QObject::timerEvent()), QSocketNotifier, or QUrlOperator; or performed
200
in a separate thread. A QProgressBar in the status bar of your main window
201
is often an alternative to a modeless progress dialog.
203
You need to have an event loop to be running, connect the
204
canceled() signal to a slot that stops the operation, and call \l
205
setValue() at intervals. For example:
207
\skipto Operation constructor
212
In both modes the progress dialog may be customized by
213
replacing the child widgets with custom widgets by using setLabel(),
214
setBar(), and setCancelButton().
215
The functions setLabelText() and setCancelButtonText()
218
The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
219
how to use QProgressDialog as well as other built-in Qt dialogs.
221
\inlineimage qprogdlg-m.png Screenshot in Motif style
222
\inlineimage qprogdlg-w.png Screenshot in Windows style
224
\sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator}
229
Constructs a progress dialog.
233
\i The label text is empty.
234
\i The cancel button text is (translated) "Cancel".
239
The \a parent argument is dialog's parent widget. The widget flags, \a f, are
240
passed to the QDialog::QDialog() constructor.
242
\sa setLabelText(), setCancelButtonText(), setCancelButton(),
243
setMinimum(), setMaximum()
246
QProgressDialog::QProgressDialog(QWidget *parent, Qt::WFlags f)
247
: QDialog(*(new QProgressDialogPrivate), parent, f)
249
Q_D(QProgressDialog);
250
d->init(QString::fromLatin1(""), tr("Cancel"), 0, 100);
254
Constructs a progress dialog.
256
The \a labelText is the text used to remind the user what is progressing.
258
The \a cancelButtonText is the text to display on the cancel button,
259
or 0 if no cancel button is to be shown.
261
The \a minimum and \a maximum is the number of steps in the operation for
262
which this progress dialog shows progress. For example, if the
263
operation is to examine 50 files, this value minimum value would be 0,
264
and the maximum would be 50. Before examining the first file, call
265
setValue(0). As each file is processed call setValue(1), setValue(2),
266
etc., finally calling setValue(50) after examining the last file.
268
The \a parent argument is the dialog's parent widget. The and widget flags,
269
\a f, are passed to the QDialog::QDialog() constructor.
271
\sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
272
setMinimum(), setMaximum()
275
QProgressDialog::QProgressDialog(const QString &labelText,
276
const QString &cancelButtonText,
277
int minimum, int maximum,
278
QWidget *parent, Qt::WFlags f)
279
: QDialog(*(new QProgressDialogPrivate), parent, f)
281
Q_D(QProgressDialog);
282
d->init(labelText, cancelButtonText, minimum, maximum);
287
Destroys the progress dialog.
290
QProgressDialog::~QProgressDialog()
295
\fn void QProgressDialog::canceled()
297
This signal is emitted when the cancel button is clicked.
298
It is connected to the cancel() slot by default.
305
Sets the label to \a label. The progress dialog resizes to fit. The
306
label becomes owned by the progress dialog and will be deleted when
307
necessary, so do not pass the address of an object on the stack.
312
void QProgressDialog::setLabel(QLabel *label)
314
Q_D(QProgressDialog);
318
if (label->parentWidget() == this) {
319
label->hide(); // until we resize
321
label->setParent(this, 0);
324
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
325
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
333
\property QProgressDialog::labelText
334
\brief the label's text
336
The default text is an empty string.
339
QString QProgressDialog::labelText() const
341
Q_D(const QProgressDialog);
343
return d->label->text();
347
void QProgressDialog::setLabelText(const QString &text)
349
Q_D(QProgressDialog);
351
d->label->setText(text);
352
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
353
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
360
Sets the cancel button to the push button, \a cancelButton. The
361
progress dialog takes ownership of this button which will be deleted
362
when necessary, so do not pass the address of an object that is on
363
the stack, i.e. use new() to create the button.
365
\sa setCancelButtonText()
368
void QProgressDialog::setCancelButton(QPushButton *cancelButton)
370
Q_D(QProgressDialog);
372
d->cancel = cancelButton;
374
if (cancelButton->parentWidget() == this) {
375
cancelButton->hide(); // until we resize
377
cancelButton->setParent(this, 0);
379
connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled()));
380
new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled()));
382
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
383
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
386
cancelButton->show();
390
Sets the cancel button's text to \a cancelButtonText.
391
\sa setCancelButton()
394
void QProgressDialog::setCancelButtonText(const QString &cancelButtonText)
396
Q_D(QProgressDialog);
397
if (!cancelButtonText.isNull()) {
399
d->cancel->setText(cancelButtonText);
401
setCancelButton(new QPushButton(cancelButtonText, this));
405
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
406
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
412
Sets the progress bar widget to \a bar. The progress dialog resizes to
413
fit. The progress dialog takes ownership of the progress \a bar which
414
will be deleted when necessary, so do not use a progress bar
415
allocated on the stack.
418
void QProgressDialog::setBar(QProgressBar *bar)
420
Q_D(QProgressDialog);
423
qWarning("QProgressDialog::setBar: Cannot set a new progress bar "
424
"while the old one is active");
428
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
429
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
435
\property QProgressDialog::wasCanceled
436
\brief whether the dialog was canceled
439
bool QProgressDialog::wasCanceled() const
441
Q_D(const QProgressDialog);
442
return d->cancellation_flag;
447
\property QProgressDialog::maximum
448
\brief the highest value represented by the progress bar
452
\sa minimum, setRange()
455
int QProgressDialog::maximum() const
457
Q_D(const QProgressDialog);
458
return d->bar->maximum();
461
void QProgressDialog::setMaximum(int maximum)
463
Q_D(QProgressDialog);
464
d->bar->setMaximum(maximum);
468
\property QProgressDialog::minimum
469
\brief the lowest value represented by the progress bar
473
\sa maximum, setRange()
476
int QProgressDialog::minimum() const
478
Q_D(const QProgressDialog);
479
return d->bar->minimum();
482
void QProgressDialog::setMinimum(int minimum)
484
Q_D(QProgressDialog);
485
d->bar->setMinimum(minimum);
489
Sets the progress dialog's minimum and maximum values
490
to \a minimum and \a maximum, respectively.
492
If \a maximum is smaller than \a minimum, \a minimum becomes the only
495
If the current value falls outside the new range, the progress
496
dialog is reset with reset().
500
void QProgressDialog::setRange(int minimum, int maximum)
502
Q_D(QProgressDialog);
503
d->bar->setRange(minimum, maximum);
508
Resets the progress dialog.
509
The progress dialog becomes hidden if autoClose() is true.
511
\sa setAutoClose(), setAutoReset()
514
void QProgressDialog::reset()
516
Q_D(QProgressDialog);
520
parentWidget()->setCursor(d->parentCursor);
523
if (d->autoClose || d->forceHide)
526
d->cancellation_flag = false;
527
d->shown_once = false;
528
d->forceTimer->stop();
532
Resets the progress dialog. wasCanceled() becomes true until
533
the progress dialog is reset.
534
The progress dialog becomes hidden.
537
void QProgressDialog::cancel()
539
Q_D(QProgressDialog);
542
d->forceHide = false;
543
d->cancellation_flag = true;
547
int QProgressDialog::value() const
549
Q_D(const QProgressDialog);
550
return d->bar->value();
554
\property QProgressDialog::value
555
\brief the current amount of progress made.
557
For the progress dialog to work as expected, you should initially set
558
this property to 0 and finally set it to
559
QProgressDialog::totalSteps(); you can call setProgress() any number of times
562
\warning If the progress dialog is modal
563
(see QProgressDialog::QProgressDialog()),
564
this function calls QApplication::processEvents(), so take care that
565
this does not cause undesirable re-entrancy in your code. For example,
566
don't use a QProgressDialog inside a paintEvent()!
570
void QProgressDialog::setValue(int progress)
572
Q_D(QProgressDialog);
573
if (progress == d->bar->value() ||
574
d->bar->value() == -1 && progress == d->bar->maximum())
577
d->bar->setValue(progress);
580
if (testAttribute(Qt::WA_ShowModal))
581
qApp->processEvents();
584
d->starttime.start();
585
d->forceTimer->start(d->showTime);
589
int elapsed = d->starttime.elapsed();
590
if (elapsed >= d->showTime) {
593
if (elapsed > minWaitTime) {
595
int totalSteps = maximum() - minimum();
596
int myprogress = progress - minimum();
597
if ((totalSteps - myprogress) >= INT_MAX / elapsed)
598
estimate = (totalSteps - myprogress) / myprogress * elapsed;
600
estimate = elapsed * (totalSteps - myprogress) / myprogress;
601
need_show = estimate >= d->showTime;
607
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
608
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
611
d->shown_once = true;
615
QApplication::flush();
619
if (progress == d->bar->maximum() && d->autoReset)
624
Returns a size that fits the contents of the progress dialog.
625
The progress dialog resizes itself as required, so you should not
626
need to call this yourself.
629
QSize QProgressDialog::sizeHint() const
631
Q_D(const QProgressDialog);
632
QSize sh = d->label->sizeHint();
633
QSize bh = d->bar->sizeHint();
634
int h = margin_tb*2 + bh.height() + sh.height() + spacing;
636
h += d->cancel->sizeHint().height() + spacing;
637
return QSize(qMax(200, sh.width() + 2*margin_lr), h);
642
void QProgressDialog::resizeEvent(QResizeEvent *)
644
Q_D(QProgressDialog);
651
void QProgressDialog::changeEvent(QEvent *ev)
653
Q_D(QProgressDialog);
654
if(ev->type() == QEvent::StyleChange)
656
QDialog::changeEvent(ev);
660
\property QProgressDialog::minimumDuration
661
\brief the time that must pass before the dialog appears
663
If the expected duration of the task is less than the
664
minimumDuration, the dialog will not appear at all. This prevents
665
the dialog popping up for tasks that are quickly over. For tasks
666
that are expected to exceed the minimumDuration, the dialog will
667
pop up after the minimumDuration time or as soon as any progress
670
If set to 0, the dialog is always shown as soon as any progress is
671
set. The default is 4000 milliseconds.
673
void QProgressDialog::setMinimumDuration(int ms)
675
Q_D(QProgressDialog);
677
if (d->bar->value() == 0) {
678
d->forceTimer->stop();
679
d->forceTimer->start(ms);
683
int QProgressDialog::minimumDuration() const
685
Q_D(const QProgressDialog);
694
void QProgressDialog::closeEvent(QCloseEvent *e)
697
QDialog::closeEvent(e);
701
\property QProgressDialog::autoReset
702
\brief whether the progress dialog calls reset() as soon as progress() equals totalSteps()
709
void QProgressDialog::setAutoReset(bool b)
711
Q_D(QProgressDialog);
715
bool QProgressDialog::autoReset() const
717
Q_D(const QProgressDialog);
722
\property QProgressDialog::autoClose
723
\brief whether the dialog gets hidden by reset()
730
void QProgressDialog::setAutoClose(bool b)
732
Q_D(QProgressDialog);
736
bool QProgressDialog::autoClose() const
738
Q_D(const QProgressDialog);
746
void QProgressDialog::showEvent(QShowEvent *e)
748
Q_D(QProgressDialog);
749
QDialog::showEvent(e);
750
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
751
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
753
d->forceTimer->stop();
757
Shows the dialog if it is still hidden after the algorithm has been started
758
and minimumDuration milliseconds have passed.
760
\sa setMinimumDuration()
763
void QProgressDialog::forceShow()
765
Q_D(QProgressDialog);
766
if (d->shown_once || d->cancellation_flag)
770
d->shown_once = true;