1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the Qt 3 compatibility classes 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 "q3progressdialog.h"
31
#ifndef QT_NO_PROGRESSDIALOG
33
#include "q3progressbar.h"
34
#include "qapplication.h"
36
#include "qdatetime.h"
39
#include "qpushbutton.h"
40
#include "qshortcut.h"
45
// If the operation is expected to take this long (as predicted by
46
// progress time), show the progress dialog.
47
static const int defaultShowTime = 4000;
48
// Wait at least this long before attempting to make a prediction.
49
static const int minWaitTime = 50;
51
// Various layout values
52
static const int margin_lr = 10;
53
static const int margin_tb = 10;
54
static const int spacing = 4;
57
class Q3ProgressDialogData
60
Q3ProgressDialogData(Q3ProgressDialog* that, QWidget* parent,
61
const QString& labelText,
64
label(new QLabel(labelText,that)),
66
bar(new Q3ProgressBar(totalSteps, that)),
68
cancellation_flag(false),
69
showTime(defaultShowTime)
71
int align = that->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, that);
72
label->setAlignment(Qt::Alignment(align));
80
bool cancellation_flag;
93
\class Q3ProgressDialog qprogressdialog.h
94
\brief The Q3ProgressDialog class provides feedback on the progress of a slow operation.
98
A progress dialog is used to give the user an indication of how long
99
an operation is going to take, and to demonstrate that the
100
application has not frozen. It can also give the user an opportunity
101
to abort the operation.
103
A common problem with progress dialogs is that it is difficult to know
104
when to use them; operations take different amounts of time on different
105
hardware. Q3ProgressDialog offers a solution to this problem:
106
it estimates the time the operation will take (based on time for
107
steps), and only shows itself if that estimate is beyond minimumDuration()
108
(4 seconds by default).
110
Use setTotalSteps() (or the constructor) to set the number of
111
"steps" in the operation and call setProgress() as the operation
112
progresses. The step value can be chosen arbitrarily. It can be the
113
number of files copied, the number of bytes received, the number of
114
iterations through the main loop of your algorithm, or some other
115
suitable unit. Progress starts at 0, and the progress dialog shows
116
that the operation has finished when you call setProgress() with
117
totalSteps() as its argument.
119
The dialog automatically resets and hides itself at the end of the
120
operation. Use setAutoReset() and setAutoClose() to change this
123
There are two ways of using Q3ProgressDialog: modal and modeless.
125
Using a modal Q3ProgressDialog is simpler for the programmer, but you
126
must call QApplication::processEvents() or
127
QEventLoop::processEvents(ExcludeUserInput) to keep the event loop
128
running to ensure that the application doesn't freeze. Do the
129
operation in a loop, call \l setProgress() at intervals, and check
130
for cancellation with wasCanceled(). For example:
132
Q3ProgressDialog progress("Copying files...", "Abort Copy", numFiles,
133
this, "progress", true);
134
for (int i = 0; i < numFiles; i++) {
135
progress.setProgress(i);
136
qApp->processEvents();
138
if (progress.wasCanceled())
142
progress.setProgress(numFiles);
145
A modeless progress dialog is suitable for operations that take
146
place in the background, where the user is able to interact with the
147
application. Such operations are typically based on QTimer (or
148
QObject::timerEvent()), QSocketNotifier, or QUrlOperator; or performed
149
in a separate thread. A Q3ProgressBar in the status bar of your main window
150
is often an alternative to a modeless progress dialog.
152
You need to have an event loop to be running, connect the
153
canceled() signal to a slot that stops the operation, and call \l
154
setProgress() at intervals. For example:
156
Operation::Operation(QObject *parent = 0)
157
: QObject(parent), steps(0)
159
pd = new Q3ProgressDialog("Operation in progress.", "Cancel", 100);
160
connect(pd, SIGNAL(canceled()), this, SLOT(cancel()));
161
t = new QTimer(this);
162
connect(t, SIGNAL(timeout()), this, SLOT(perform()));
166
void Operation::perform()
168
pd->setProgress(steps);
169
//... perform one percent of the operation
171
if (steps > pd->totalSteps())
175
void Operation::cancel()
183
In both modes the progress dialog may be customized by
184
replacing the child widgets with custom widgets by using setLabel(),
185
setBar(), and setCancelButton().
186
The functions setLabelText() and setCancelButtonText()
189
\inlineimage qprogdlg-m.png Screenshot in Motif style
190
\inlineimage qprogdlg-w.png Screenshot in Windows style
192
\sa QDialog, Q3ProgressBar, {fowler}{GUI Design Handbook: Progress Indicator}
197
Returns the QLabel currently being displayed above the progress bar.
198
This QLabel is owned by the Q3ProgressDialog.
202
QLabel *Q3ProgressDialog::label() const
208
Returns the Q3ProgressBar currently being used to display progress.
209
This Q3ProgressBar is owned by the Q3ProgressDialog.
213
Q3ProgressBar *Q3ProgressDialog::bar() const
221
Constructs a progress dialog.
225
\i The label text is empty.
226
\i The cancel button text is (translated) "Cancel".
227
\i The total number of steps is 100.
230
The \a creator argument is the widget to use as the dialog's parent.
231
The \a name, \a modal, and the widget flags, \a f, are
232
passed to the QDialog::QDialog() constructor. If \a modal is false (the
233
default), you must have an event loop proceeding for any redrawing
234
of the dialog to occur. If \a modal is true, the dialog ensures that
235
events are processed when needed.
237
\sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
241
Q3ProgressDialog::Q3ProgressDialog(QWidget *creator, const char *name,
242
bool modal, Qt::WFlags f)
243
: QDialog(creator, f)
247
init(creator, QString::fromLatin1(""), tr("Cancel"), 100);
252
Constructs a progress dialog.
254
The \a labelText is text used to remind the user what is progressing.
256
The \a cancelButtonText is the text to display on the cancel button,
257
or 0 if no cancel button is to be shown.
259
The \a totalSteps is the total number of steps in the operation for
260
which this progress dialog shows progress. For example, if the
261
operation is to examine 50 files, this value would be 50. Before
262
examining the first file, call setProgress(0). As each file is
263
processed call setProgress(1), setProgress(2), etc., finally
264
calling setProgress(50) after examining the last file.
266
The \a creator argument is the widget to use as the dialog's parent.
267
The \a name, \a modal, and widget flags, \a f, are passed to the
268
QDialog::QDialog() constructor. If \a modal is false (the default),
269
you will must have an event loop proceeding for any redrawing of
270
the dialog to occur. If \a modal is true, the dialog ensures that
271
events are processed when needed.
274
\sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
278
Q3ProgressDialog::Q3ProgressDialog(const QString &labelText,
279
const QString &cancelButtonText,
281
QWidget *creator, const char *name,
282
bool modal, Qt::WFlags f)
283
: QDialog(creator, f)
287
init(creator, labelText, cancelButtonText, totalSteps);
291
Constructs a progress dialog.
295
\i The label text is empty.
296
\i The cancel button text is (translated) "Cancel".
297
\i The total number of steps is 100.
300
The \a creator argument is the widget to use as the dialog's parent.
301
The widget flags, \a f, are passed to the QDialog::QDialog() constructor.
303
\sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
306
Q3ProgressDialog::Q3ProgressDialog(QWidget *creator, Qt::WFlags f)
307
: QDialog(creator, f)
309
init(creator, QString::fromLatin1(""), tr("Cancel"), 100);
313
Constructs a progress dialog.
315
The \a labelText is text used to remind the user what is progressing.
317
The \a cancelButtonText is the text to display on the cancel button,
318
or 0 if no cancel button is to be shown.
320
The \a totalSteps is the total number of steps in the operation for
321
which this progress dialog shows progress. For example, if the
322
operation is to examine 50 files, this value would be 50. Before
323
examining the first file, call setProgress(0). As each file is
324
processed call setProgress(1), setProgress(2), etc., finally
325
calling setProgress(50) after examining the last file.
327
The \a creator argument is the widget to use as the dialog's parent.
328
The widget flags, \a f, are passed to the
329
QDialog::QDialog() constructor.
331
\sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
334
Q3ProgressDialog::Q3ProgressDialog(const QString &labelText,
335
const QString &cancelButtonText,
336
int totalSteps, QWidget *creator, Qt::WFlags f)
337
: QDialog(creator, f)
339
init(creator, labelText, cancelButtonText, totalSteps);
343
Destroys the progress dialog.
346
Q3ProgressDialog::~Q3ProgressDialog()
350
d->creator->setCursor(d->parentCursor);
355
void Q3ProgressDialog::init(QWidget *creator,
356
const QString& lbl, const QString& canc,
359
d = new Q3ProgressDialogData(this, creator, lbl, totstps);
362
d->forceHide = false;
363
setCancelButtonText(canc);
364
connect(this, SIGNAL(canceled()), this, SLOT(cancel()));
365
forceTimer = new QTimer(this);
366
connect(forceTimer, SIGNAL(timeout()), this, SLOT(forceShow()));
371
\fn void Q3ProgressDialog::canceled()
373
This signal is emitted when the cancel button is clicked.
374
It is connected to the cancel() slot by default.
381
Sets the label to \a label. The progress dialog resizes to fit. The
382
label becomes owned by the progress dialog and will be deleted when
383
necessary, so do not pass the address of an object on the stack.
388
void Q3ProgressDialog::setLabel(QLabel *label)
393
if (label->parentWidget() == this) {
394
label->hide(); // until we resize
396
label->setParent(this, 0);
399
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
400
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
408
\property Q3ProgressDialog::labelText
409
\brief the label's text
411
The default text is an empty string.
414
QString Q3ProgressDialog::labelText() const
417
return label()->text();
421
void Q3ProgressDialog::setLabelText(const QString &text)
424
label()->setText(text);
425
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
426
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
433
Sets the cancel button to the push button, \a cancelButton. The
434
progress dialog takes ownership of this button which will be deleted
435
when necessary, so do not pass the address of an object that is on
436
the stack, i.e. use new() to create the button.
438
\sa setCancelButtonText()
441
void Q3ProgressDialog::setCancelButton(QPushButton *cancelButton)
444
d->cancel = cancelButton;
446
if (cancelButton->parentWidget() == this) {
447
cancelButton->hide(); // until we resize
449
cancelButton->setParent(this, 0);
451
connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled()));
452
new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled()));
454
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
455
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
458
cancelButton->show();
462
Sets the cancel button's text to \a cancelButtonText.
463
\sa setCancelButton()
466
void Q3ProgressDialog::setCancelButtonText(const QString &cancelButtonText)
468
if (!cancelButtonText.isNull()) {
470
d->cancel->setText(cancelButtonText);
472
setCancelButton(new QPushButton(cancelButtonText, this));
476
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
477
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
483
Sets the progress bar widget to \a bar. The progress dialog resizes to
484
fit. The progress dialog takes ownership of the progress \a bar which
485
will be deleted when necessary, so do not use a progress bar
486
allocated on the stack.
489
void Q3ProgressDialog::setBar(Q3ProgressBar *bar)
493
qWarning("Q3ProgressDialog::setBar: Cannot set a new progress bar "
494
"while the old one is active");
498
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
499
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
505
\property Q3ProgressDialog::wasCanceled
506
\brief whether the dialog was canceled
511
bool Q3ProgressDialog::wasCanceled() const
513
return d->cancellation_flag;
518
\property Q3ProgressDialog::totalSteps
519
\brief the total number of steps
524
int Q3ProgressDialog::totalSteps() const
527
return bar()->totalSteps();
531
void Q3ProgressDialog::setTotalSteps(int totalSteps)
533
bar()->setTotalSteps(totalSteps);
538
Resets the progress dialog.
539
The progress dialog becomes hidden if autoClose() is true.
541
\sa setAutoClose(), setAutoReset()
544
void Q3ProgressDialog::reset()
547
if (progress() >= 0) {
549
d->creator->setCursor(d->parentCursor);
552
if (d->autoClose || d->forceHide)
555
d->cancellation_flag = false;
556
d->shown_once = false;
561
Resets the progress dialog. wasCanceled() becomes true until
562
the progress dialog is reset.
563
The progress dialog becomes hidden.
566
void Q3ProgressDialog::cancel()
570
d->forceHide = false;
571
d->cancellation_flag = true;
575
\property Q3ProgressDialog::progress
576
\brief the current amount of progress made.
578
For the progress dialog to work as expected, you should initially set
579
this property to 0 and finally set it to
580
Q3ProgressDialog::totalSteps(); you can call setProgress() any number of times
583
\warning If the progress dialog is modal
584
(see Q3ProgressDialog::Q3ProgressDialog()),
585
this function calls QApplication::processEvents(), so take care that
586
this does not cause undesirable re-entrancy in your code. For example,
587
don't use a Q3ProgressDialog inside a paintEvent()!
592
int Q3ProgressDialog::progress() const
594
return bar()->progress();
597
void Q3ProgressDialog::setProgress(int progress)
599
if (progress == bar()->progress() ||
600
bar()->progress() == -1 && progress == bar()->totalSteps())
603
bar()->setProgress(progress);
607
qApp->processEvents();
612
d->parentCursor = d->creator->cursor();
613
d->creator->setCursor(Qt::WaitCursor);
616
d->starttime.start();
617
forceTimer->start(d->showTime);
621
int elapsed = d->starttime.elapsed();
622
if (elapsed >= d->showTime) {
625
if (elapsed > minWaitTime) {
627
if ((totalSteps() - progress) >= INT_MAX / elapsed)
628
estimate = (totalSteps() - progress) / progress * elapsed;
630
estimate = elapsed * (totalSteps() - progress) / progress;
631
need_show = estimate >= d->showTime;
637
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
638
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
641
d->shown_once = true;
645
QApplication::flush();
649
if (progress == bar()->totalSteps() && d->autoReset)
656
Sets the current amount of progress to \a progress and the total number of
657
steps to \a totalSteps.
662
void Q3ProgressDialog::setProgress(int progress, int totalSteps)
664
setTotalSteps(totalSteps);
665
setProgress(progress);
669
Returns a size that fits the contents of the progress dialog.
670
The progress dialog resizes itself as required, so you should not
671
need to call this yourself.
674
QSize Q3ProgressDialog::sizeHint() const
676
QSize sh = label()->sizeHint();
677
QSize bh = bar()->sizeHint();
678
int h = margin_tb*2 + bh.height() + sh.height() + spacing;
680
h += d->cancel->sizeHint().height() + spacing;
681
return QSize(qMax(200, sh.width() + 2*margin_lr), h);
686
void Q3ProgressDialog::resizeEvent(QResizeEvent *)
694
void Q3ProgressDialog::changeEvent(QEvent *ev)
696
if(ev->type() == QEvent::StyleChange)
698
QDialog::changeEvent(ev);
701
void Q3ProgressDialog::layout()
705
int mlr = qMin(width()/10, margin_lr);
706
const bool centered =
707
bool(style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, this));
709
QSize cs = d->cancel ? d->cancel->sizeHint() : QSize(0,0);
710
QSize bh = bar()->sizeHint();
714
// Find spacing and sizes that fit. It is important that a progress
715
// dialog can be made very small if the user demands it so.
716
for (int attempt=5; attempt--;) {
717
cspc = d->cancel ? cs.height() + sp : 0;
718
lh = qMax(0, height() - mtb - bh.height() - sp - cspc);
720
if (lh < height()/4) {
725
cs.setHeight(qMax(4,cs.height()-sp-2));
727
bh.setHeight(qMax(4,bh.height()-sp-1));
734
d->cancel->setGeometry(
735
centered ? width()/2 - cs.width()/2 : width() - mlr - cs.width(),
736
height() - mtb - cs.height() + sp,
737
cs.width(), cs.height());
740
label()->setGeometry(mlr, 0, width()-mlr*2, lh);
741
bar()->setGeometry(mlr, lh+sp, width()-mlr*2, bh.height());
745
\property Q3ProgressDialog::minimumDuration
746
\brief the time that must pass before the dialog appears
748
If the expected duration of the task is less than the
749
minimumDuration, the dialog will not appear at all. This prevents
750
the dialog popping up for tasks that are quickly over. For tasks
751
that are expected to exceed the minimumDuration, the dialog will
752
pop up after the minimumDuration time or as soon as any progress
755
If set to 0, the dialog is always shown as soon as any progress is
756
set. The default is 4000 milliseconds.
758
void Q3ProgressDialog::setMinimumDuration(int ms)
761
if (bar()->progress() == 0) {
763
forceTimer->start(ms);
767
int Q3ProgressDialog::minimumDuration() const
777
void Q3ProgressDialog::closeEvent(QCloseEvent *e)
780
QDialog::closeEvent(e);
784
\property Q3ProgressDialog::autoReset
785
\brief whether the progress dialog calls reset() as soon as progress() equals totalSteps()
792
void Q3ProgressDialog::setAutoReset(bool b)
797
bool Q3ProgressDialog::autoReset() const
803
\property Q3ProgressDialog::autoClose
804
\brief whether the dialog gets hidden by reset()
811
void Q3ProgressDialog::setAutoClose(bool b)
816
bool Q3ProgressDialog::autoClose() const
825
void Q3ProgressDialog::showEvent(QShowEvent *e)
827
QDialog::showEvent(e);
828
int w = qMax(isVisible() ? width() : 0, sizeHint().width());
829
int h = qMax(isVisible() ? height() : 0, sizeHint().height());
835
Shows the dialog if it is still hidden after the algorithm has been started
836
and minimumDuration milliseconds have passed.
838
\sa setMinimumDuration()
841
void Q3ProgressDialog::forceShow()
843
if (d->shown_once || d->cancellation_flag)
847
d->shown_once = true;