Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qprogressbar.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5#include "qprogressbar.h"
6
7#include <qlocale.h>
8#include <qevent.h>
9#include <qpainter.h>
10#include <qstylepainter.h>
11#include <qstyleoption.h>
12#include <private/qwidget_p.h>
13#if QT_CONFIG(accessibility)
14#include <qaccessible.h>
15#endif
16#include <limits.h>
17
19
20using namespace Qt::StringLiterals;
21
23{
24 Q_DECLARE_PUBLIC(QProgressBar)
25
26public:
28
29 void init();
32
35 int value;
44 inline int bound(int val) const { return qMax(minimum-1, qMin(maximum, val)); }
45 bool repaintRequired() const;
46};
47
48QProgressBarPrivate::QProgressBarPrivate()
49 : minimum(0), maximum(100), value(-1), alignment(Qt::AlignLeft), textVisible(true),
50 defaultFormat(true), lastPaintedValue(-1), orientation(Qt::Horizontal), invertedAppearance(false),
51 textDirection(QProgressBar::TopToBottom)
52{
54}
55
57{
58 if (defaultFormat) {
59 //: %p is the percent value, % is the percent sign. When translated
60 //: to the identical %p%, then the second % symbol will be replaced
61 //: by the percentage-symbol from the user's locale.
62 format = QProgressBar::tr("%p%");
63 }
64
65 if (format == "%p%"_L1)
66 format = "%p"_L1 + locale.percent();
67}
68
70{
71 Q_Q(QProgressBar);
72 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed);
73 if (orientation == Qt::Vertical)
74 sp.transpose();
75 q->setSizePolicy(sp);
76 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
78}
79
81{
82 Q_Q(QProgressBar);
83 QStyleOptionProgressBar option;
84 // ### It seems like this can be called directly from the constructor which should be avoided
85 // ### if possible, since we will not call a possible re-implemented version
86 q->initStyleOption(&option);
87 setLayoutItemMargins(QStyle::SE_ProgressBarLayoutItem, &option);
88}
89
90/*!
91 Initialize \a option with the values from this QProgressBar. This method is useful
92 for subclasses when they need a QStyleOptionProgressBar,
93 but don't want to fill in all the information themselves.
94
95 \sa QStyleOption::initFrom()
96*/
97void QProgressBar::initStyleOption(QStyleOptionProgressBar *option) const
98{
99 if (!option)
100 return;
101 Q_D(const QProgressBar);
102 option->initFrom(this);
103
104 if (d->orientation == Qt::Horizontal)
105 option->state |= QStyle::State_Horizontal;
106 option->minimum = d->minimum;
107 option->maximum = d->maximum;
108 option->progress = d->value;
109 option->textAlignment = d->alignment;
110 option->textVisible = d->textVisible;
111 option->text = text();
112 option->invertedAppearance = d->invertedAppearance;
113 option->bottomToTop = d->textDirection == QProgressBar::BottomToTop;
114}
115
117{
118 Q_Q(const QProgressBar);
119 if (value == lastPaintedValue)
120 return false;
121
122 const qint64 valueDifference = qAbs(qint64(value) - qint64(lastPaintedValue));
123
124 if (value == minimum || value == maximum)
125 return true;
126
127 const int totalSteps = maximum - minimum;
128
129 const int currentPercentage = totalSteps ? (value - minimum) * 100 / totalSteps : 0;
130 const int lastPaintedPercentage = totalSteps ? (lastPaintedValue - minimum) * 100 / totalSteps
131 : 0;
132
133 const int percentageChangeConstant = 1;
134 const bool percentageChanged = (qAbs(currentPercentage - lastPaintedPercentage) >= percentageChangeConstant);
135
136 if (textVisible) {
137 if (format.contains("%v"_L1))
138 return true;
139 if (format.contains("%p"_L1) && percentageChanged)
140 return true;
141 }
142
143 // Check if the bar needs to be repainted based on pixel-level differences
144 QStyleOptionProgressBar opt;
145 q->initStyleOption(&opt);
146 QRect groove = q->style()->subElementRect(QStyle::SE_ProgressBarGroove, &opt, q);
147 int grooveBlock = (q->orientation() == Qt::Horizontal) ? groove.width() : groove.height();
148 const double pixelSize = static_cast<double>(totalSteps) / grooveBlock;
149 return valueDifference > pixelSize;
150}
151
152/*!
153 \class QProgressBar
154 \brief The QProgressBar widget provides a horizontal or vertical progress bar.
155
156 \ingroup basicwidgets
157 \inmodule QtWidgets
158
159 \image fusion-progressbar.png {Progress bar showing 42%}
160
161 A progress bar is used to give the user an indication of the
162 progress of an operation and to reassure them that the application
163 is still running.
164
165 The progress bar uses the concept of \e steps. You set it up by
166 specifying the minimum and maximum possible step values, and it
167 will display the percentage of steps that have been completed
168 when you later give it the current step value. The percentage is
169 calculated by dividing the progress (value() - minimum()) divided
170 by maximum() - minimum().
171
172 You can specify the minimum and maximum number of steps with
173 setMinimum() and setMaximum. The current number of steps is set
174 with setValue(). The progress bar can be rewound to the
175 beginning with reset().
176
177 If minimum and maximum both are set to 0, the bar shows a busy
178 indicator instead of a percentage of steps. This is useful, for
179 example, when using QNetworkAccessManager to download items when
180 they are unable to determine the size of the item being downloaded.
181
182 \sa QProgressDialog
183*/
184
185/*!
186 \since 4.1
187 \enum QProgressBar::Direction
188 \brief Specifies the reading direction of the \l text for vertical progress bars.
189
190 \value TopToBottom The text is rotated 90 degrees clockwise.
191 \value BottomToTop The text is rotated 90 degrees counter-clockwise.
192
193 Note that whether or not the text is drawn is dependent on the style.
194 Currently CleanLooks and Plastique draw the text. Mac, Windows
195 and WindowsVista style do not.
196
197 \sa textDirection
198*/
199
200/*!
201 \fn void QProgressBar::valueChanged(int value)
202
203 This signal is emitted when the value shown in the progress bar changes.
204 \a value is the new value shown by the progress bar.
205*/
206
207/*!
208 Constructs a progress bar with the given \a parent.
209
210 By default, the minimum step value is set to 0, and the maximum to 100.
211
212 \sa setRange()
213*/
214
215QProgressBar::QProgressBar(QWidget *parent)
216 : QWidget(*(new QProgressBarPrivate), parent, { })
217{
218 d_func()->init();
219}
220
221/*!
222 Destructor.
223*/
224QProgressBar::~QProgressBar()
225{
226}
227
228/*!
229 Reset the progress bar. The progress bar "rewinds" and shows no
230 progress.
231*/
232
233void QProgressBar::reset()
234{
235 Q_D(QProgressBar);
236 if (d->minimum == INT_MIN)
237 d->value = INT_MIN;
238 else
239 d->value = d->minimum - 1;
240 repaint();
241}
242
243/*!
244 \property QProgressBar::minimum
245 \brief the progress bar's minimum value
246
247 When setting this property, the \l maximum is adjusted if
248 necessary to ensure that the range remains valid. If the
249 current value falls outside the new range, the progress bar is reset
250 with reset().
251*/
252void QProgressBar::setMinimum(int minimum)
253{
254 setRange(minimum, qMax(d_func()->maximum, minimum));
255}
256
257int QProgressBar::minimum() const
258{
259 return d_func()->minimum;
260}
261
262
263/*!
264 \property QProgressBar::maximum
265 \brief the progress bar's maximum value
266
267 When setting this property, the \l minimum is adjusted if
268 necessary to ensure that the range remains valid. If the
269 current value falls outside the new range, the progress bar is reset
270 with reset().
271*/
272
273void QProgressBar::setMaximum(int maximum)
274{
275 setRange(qMin(d_func()->minimum, maximum), maximum);
276}
277
278int QProgressBar::maximum() const
279{
280 return d_func()->maximum;
281}
282
283/*!
284 \property QProgressBar::value
285 \brief the progress bar's current value
286
287 Attempting to change the current value to one outside
288 the minimum-maximum range has no effect on the current value.
289*/
290void QProgressBar::setValue(int value)
291{
292 Q_D(QProgressBar);
293 if (d->value == value
294 || ((value > d->maximum || value < d->minimum)
295 && (d->maximum != 0 || d->minimum != 0)))
296 return;
297 d->value = value;
298 emit valueChanged(value);
299#if QT_CONFIG(accessibility)
300 if (isVisible()) {
301 QAccessibleValueChangeEvent event(this, value);
302 QAccessible::updateAccessibility(&event);
303 }
304#endif
305 if (d->repaintRequired())
306 repaint();
307}
308
309int QProgressBar::value() const
310{
311 return d_func()->value;
312}
313
314/*!
315 Sets the progress bar's minimum and maximum values to \a minimum and
316 \a maximum respectively.
317
318 If \a maximum is smaller than \a minimum, \a minimum becomes the only
319 legal value.
320
321 If the current value falls outside the new range, the progress bar is reset
322 with reset().
323
324 The QProgressBar can be set to undetermined state by using setRange(0, 0).
325
326 \sa minimum, maximum
327*/
328void QProgressBar::setRange(int minimum, int maximum)
329{
330 Q_D(QProgressBar);
331 if (minimum != d->minimum || maximum != d->maximum) {
332 d->minimum = minimum;
333 d->maximum = qMax(minimum, maximum);
334
335 if (d->value < qint64(d->minimum) - 1 || d->value > d->maximum)
336 reset();
337 else
338 update();
339 }
340}
341
342/*!
343 \property QProgressBar::textVisible
344 \brief whether the current completed percentage should be displayed
345
346 This property may be ignored by the style (e.g., QMacStyle never draws the text).
347
348 \sa textDirection
349*/
350void QProgressBar::setTextVisible(bool visible)
351{
352 Q_D(QProgressBar);
353 if (d->textVisible != visible) {
354 d->textVisible = visible;
355 repaint();
356 }
357}
358
359bool QProgressBar::isTextVisible() const
360{
361 return d_func()->textVisible;
362}
363
364/*!
365 \property QProgressBar::alignment
366 \brief the alignment of the progress bar
367*/
368void QProgressBar::setAlignment(Qt::Alignment alignment)
369{
370 if (d_func()->alignment != alignment) {
371 d_func()->alignment = alignment;
372 repaint();
373 }
374}
375
376Qt::Alignment QProgressBar::alignment() const
377{
378 return d_func()->alignment;
379}
380
381/*!
382 \reimp
383*/
384void QProgressBar::paintEvent(QPaintEvent *)
385{
386 QStylePainter paint(this);
387 QStyleOptionProgressBar opt;
388 initStyleOption(&opt);
389 paint.drawControl(QStyle::CE_ProgressBar, opt);
390 d_func()->lastPaintedValue = d_func()->value;
391}
392
393/*!
394 \reimp
395*/
396QSize QProgressBar::sizeHint() const
397{
398 ensurePolished();
399 QFontMetrics fm = fontMetrics();
400 QStyleOptionProgressBar opt;
401 initStyleOption(&opt);
402 int cw = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, this);
403 QSize size = QSize(qMax(9, cw) * 7 + fm.horizontalAdvance(u'0') * 4, fm.height() + 8);
404 if (!(opt.state & QStyle::State_Horizontal))
405 size = size.transposed();
406 return style()->sizeFromContents(QStyle::CT_ProgressBar, &opt, size, this);
407}
408
409/*!
410 \reimp
411*/
412QSize QProgressBar::minimumSizeHint() const
413{
414 QSize size;
415 if (orientation() == Qt::Horizontal)
416 size = QSize(sizeHint().width(), fontMetrics().height() + 2);
417 else
418 size = QSize(fontMetrics().height() + 2, sizeHint().height());
419 return size;
420}
421
422/*!
423 \property QProgressBar::text
424 \brief the descriptive text shown with the progress bar
425
426 The text returned is the same as the text displayed in the center
427 (or in some styles, to the left) of the progress bar.
428
429 The progress shown in the text may be smaller than the minimum value,
430 indicating that the progress bar is in the "reset" state before any
431 progress is set.
432
433 In the default implementation, the text either contains a percentage
434 value that indicates the progress so far, or it is blank because the
435 progress bar is in the reset state.
436*/
437QString QProgressBar::text() const
438{
439 Q_D(const QProgressBar);
440 if ((d->maximum == 0 && d->minimum == 0) || d->value < d->minimum
441 || (d->value == INT_MIN && d->minimum == INT_MIN))
442 return QString();
443
444 qint64 totalSteps = qint64(d->maximum) - d->minimum;
445
446 QString result = d->format;
447 QLocale locale = d->locale; // Omit group separators for compatibility with previous versions that were non-localized.
448 locale.setNumberOptions(locale.numberOptions() | QLocale::OmitGroupSeparator);
449 result.replace("%m"_L1, locale.toString(totalSteps));
450 result.replace("%v"_L1, locale.toString(d->value));
451
452 // If max and min are equal and we get this far, it means that the
453 // progress bar has one step and that we are on that step. Return
454 // 100% here in order to avoid division by zero further down.
455 if (totalSteps == 0) {
456 result.replace("%p"_L1, locale.toString(100));
457 return result;
458 }
459
460 const auto progress = static_cast<int>((qint64(d->value) - d->minimum) * 100.0 / totalSteps);
461 result.replace("%p"_L1, locale.toString(progress));
462 return result;
463}
464
465/*!
466 \since 4.1
467 \property QProgressBar::orientation
468 \brief the orientation of the progress bar
469
470 The orientation must be \l Qt::Horizontal (the default) or \l
471 Qt::Vertical.
472
473 \sa invertedAppearance, textDirection
474*/
475
476void QProgressBar::setOrientation(Qt::Orientation orientation)
477{
478 Q_D(QProgressBar);
479 if (d->orientation == orientation)
480 return;
481 d->orientation = orientation;
482 if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
483 setSizePolicy(sizePolicy().transposed());
484 setAttribute(Qt::WA_WState_OwnSizePolicy, false);
485 }
486 d->resetLayoutItemMargins();
487 update();
488 updateGeometry();
489}
490
491Qt::Orientation QProgressBar::orientation() const
492{
493 Q_D(const QProgressBar);
494 return d->orientation;
495}
496
497/*!
498 \since 4.1
499 \property QProgressBar::invertedAppearance
500 \brief whether or not a progress bar shows its progress inverted
501
502 If this property is \c true, the progress bar grows in the other
503 direction (e.g. from right to left). By default, the progress bar
504 is not inverted.
505
506 \sa orientation, layoutDirection
507*/
508
509void QProgressBar::setInvertedAppearance(bool invert)
510{
511 Q_D(QProgressBar);
512 d->invertedAppearance = invert;
513 update();
514}
515
516bool QProgressBar::invertedAppearance() const
517{
518 Q_D(const QProgressBar);
519 return d->invertedAppearance;
520}
521
522/*!
523 \since 4.1
524 \property QProgressBar::textDirection
525 \brief the reading direction of the \l text for vertical progress bars
526
527 This property has no impact on horizontal progress bars.
528 By default, the reading direction is QProgressBar::TopToBottom.
529
530 \sa orientation, textVisible
531*/
532void QProgressBar::setTextDirection(QProgressBar::Direction textDirection)
533{
534 Q_D(QProgressBar);
535 d->textDirection = textDirection;
536 update();
537}
538
539QProgressBar::Direction QProgressBar::textDirection() const
540{
541 Q_D(const QProgressBar);
542 return d->textDirection;
543}
544
545/*! \reimp */
546bool QProgressBar::event(QEvent *e)
547{
548 Q_D(QProgressBar);
549 switch (e->type()) {
550 case QEvent::StyleChange:
551#ifdef Q_OS_MAC
552 case QEvent::MacSizeChange:
553#endif
554 d->resetLayoutItemMargins();
555 break;
556 case QEvent::LocaleChange:
557 d->initDefaultFormat();
558 break;
559 default:
560 break;
561 }
562 return QWidget::event(e);
563}
564
565/*!
566 \since 4.2
567 \property QProgressBar::format
568 \brief the string used to generate the current text
569
570 %p - is replaced by the percentage completed.
571 %v - is replaced by the current value.
572 %m - is replaced by the total number of steps.
573
574 The default value is "%p%".
575
576 \sa text()
577*/
578void QProgressBar::setFormat(const QString &format)
579{
580 Q_D(QProgressBar);
581 if (d->format == format)
582 return;
583 d->format = format;
584 d->defaultFormat = false;
585 update();
586}
587
588void QProgressBar::resetFormat()
589{
590 Q_D(QProgressBar);
591 d->defaultFormat = true;
592 d->initDefaultFormat();
593 update();
594}
595
596QString QProgressBar::format() const
597{
598 Q_D(const QProgressBar);
599 return d->format;
600}
601
602QT_END_NAMESPACE
603
604#include "moc_qprogressbar.cpp"
bool repaintRequired() const
Qt::Alignment alignment
int bound(int val) const
Qt::Orientation orientation