Krita Source Code Documentation
Loading...
Searching...
No Matches
KoProgressUpdater.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2006-2007 Thomas Zander <zander@kde.org>
3 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7#include "KoProgressUpdater.h"
8
9#include <QApplication>
10#include <QString>
11#include <QThread>
12#include <QMutex>
13#include <QMutexLocker>
14
15#include "KoUpdaterPrivate_p.h"
16#include "KoUpdater.h"
17#include "KoProgressProxy.h"
18
20
21#include <kis_debug.h>
22
23class Q_DECL_HIDDEN KoProgressUpdater::Private
24{
25public:
26
28 : q(_q)
29 , parentProgressProxy(proxy)
30 , parentUpdater(parentUpdater)
31 , mode(_mode)
32 , updateCompressor(new KisSignalCompressor(250, KisSignalCompressor::FIRST_ACTIVE, q))
33 , canceled(false)
34 {
35 }
36
38
39private:
42
43public:
45 int currentProgress = 0;
46 bool isUndefinedState = false;
50 int updateInterval = 250; // ms, 4 updates per second should be enough
51 bool autoNestNames = false;
52 QString taskName;
53 int taskMax = 99;
54 bool isStarted = false;
55
56 QMutex mutex;
57
59 void clearState();
60
62 return parentUpdater ? parentUpdater : parentProgressProxy;
63 }
64};
65
66// NOTE: do not make the KoProgressUpdater object part of the QObject
67// hierarchy. Do not make KoProgressProxy its parent (note that KoProgressProxy
68// is not necessarily castable to QObject ). This prevents proper functioning
69// of progress reporting in multi-threaded environments.
71 : d (new Private(this, progressProxy, 0, mode))
72{
74 connect(d->updateCompressor, SIGNAL(timeout()), SLOT(updateUi()));
75 connect(this, SIGNAL(triggerUpdateAsynchronously()), d->updateCompressor, SLOT(start()));
77}
78
80 : d (new Private(this, 0, updater, Unthreaded))
81{
83 connect(d->updateCompressor, SIGNAL(timeout()), SLOT(updateUi()));
84 connect(this, SIGNAL(triggerUpdateAsynchronously()), d->updateCompressor, SLOT(start()));
86}
87
89{
90 if (d->progressProxy()) {
91 d->progressProxy()->setRange(0, d->taskMax);
92 d->progressProxy()->setValue(d->progressProxy()->maximum());
93 }
94
95 // make sure to stop the timer to avoid accessing
96 // the data we are going to delete right now
97 d->updateCompressor->stop();
98
99 qDeleteAll(d->subtasks);
100 d->subtasks.clear();
101
102 delete d;
103}
104
105void KoProgressUpdater::start(int range, const QString &text)
106{
107 {
108 QMutexLocker l(&d->mutex);
109 d->clearState();
110 d->taskName = text;
111 d->taskMax = range - 1;
112 d->isStarted = true;
113 d->currentProgress = 0;
114 }
115
117}
118
120 const QString &name,
121 bool isPersistent)
122{
123 if (!d->isStarted) {
124 // lazy initialization for intermediate proxies
125 start();
126 }
127
128 KoUpdaterPrivate *p = new KoUpdaterPrivate(weight, name, isPersistent);
129
130 {
131 QMutexLocker l(&d->mutex);
132 d->subtasks.append(p);
133 }
134 connect(p, SIGNAL(sigUpdated()), SLOT(update()));
135 connect(p, SIGNAL(sigCancelled()), SLOT(cancel()));
136
137 QPointer<KoUpdater> updater = p->connectedUpdater();
138
140 return updater;
141}
142
144{
145 {
146 QMutexLocker l(&d->mutex);
147
148 for (auto it = d->subtasks.begin(); it != d->subtasks.end();) {
149 if ((*it)->connectedUpdater() != updater) {
150 ++it;
151 } else {
152 KIS_SAFE_ASSERT_RECOVER_NOOP((*it)->isPersistent());
153 (*it)->deleteLater();
154 it = d->subtasks.erase(it);
155 break;
156 }
157 }
158 }
159
161}
162
164{
165 KIS_SAFE_ASSERT_RECOVER_RETURN(QThread::currentThread() == this->thread());
166
168
169 {
170 QMutexLocker l(&d->mutex);
171 subtasks = d->subtasks;
172 }
173
174 Q_FOREACH (QPointer<KoUpdaterPrivate> updater, subtasks) {
175 if (!updater) continue;
176
177 updater->setProgress(100);
178 updater->setInterrupted(true);
179 }
180 d->canceled = true;
181
183}
184
186{
187 KIS_SAFE_ASSERT_RECOVER_RETURN(QThread::currentThread() == this->thread());
188
189 if (d->mode == Unthreaded) {
190 qApp->processEvents();
191 }
192
193 d->updateCompressor->start();
194}
195
197{
198 KIS_SAFE_ASSERT_RECOVER_RETURN(QThread::currentThread() == this->thread());
199
200 // This function runs in the app main thread. All the progress
201 // updates arrive at the KoUpdaterPrivate instances through
202 // queued connections, so until we relinquish control to the
203 // event loop, the progress values cannot change, and that
204 // won't happen until we return from this function (which is
205 // triggered by a timer)
206
207 {
208 QMutexLocker l(&d->mutex);
209
210 if (!d->subtasks.isEmpty()) {
211 int totalProgress = 0;
212 int totalWeight = 0;
213 d->isUndefinedState = false;
214
215 Q_FOREACH (QPointer<KoUpdaterPrivate> updater, d->subtasks) {
216 if (updater->interrupted()) {
217 d->currentProgress = -1;
218 break;
219 }
220
221 if (!updater->hasValidRange()) {
222 totalWeight = 0;
223 totalProgress = 0;
224 d->isUndefinedState = true;
225 break;
226 }
227
228 if (updater->isPersistent() && updater->isCompleted()) {
229 continue;
230 }
231
232 const int progress = qBound(0, updater->progress(), 100);
233 totalProgress += progress * updater->weight();
234 totalWeight += updater->weight();
235 }
236
237 const int progressPercent = totalWeight > 0 ? totalProgress / totalWeight : -1;
238
239 d->currentProgress =
240 d->taskMax == 99 ?
241 progressPercent :
242 qRound(qreal(progressPercent) * d->taskMax / 99.0);
243 }
244
245 }
246
247 if (d->progressProxy()) {
248 if (!d->isUndefinedState) {
249 d->progressProxy()->setRange(0, d->taskMax);
250
251 if (d->currentProgress == -1) {
252 d->currentProgress = d->progressProxy()->maximum();
253 }
254
255 if (d->currentProgress >= d->progressProxy()->maximum()) {
256 {
257 QMutexLocker l(&d->mutex);
258 d->clearState();
259 }
260 d->progressProxy()->setRange(0, d->taskMax);
261 d->progressProxy()->setValue(d->progressProxy()->maximum());
262 } else {
263 d->progressProxy()->setValue(d->currentProgress);
264 }
265 } else {
266 d->progressProxy()->setRange(0,0);
267 d->progressProxy()->setValue(0);
268 }
269
270 d->updateParentText();
271 }
272}
273
274void KoProgressUpdater::Private::updateParentText()
275{
276 if (!progressProxy()) return;
277
278 QString actionName = taskName;
279
280 if (autoNestNames) {
281 Q_FOREACH (QPointer<KoUpdaterPrivate> updater, subtasks) {
282
283 if (updater->isPersistent() && updater->isCompleted()) {
284 continue;
285 }
286
287 if (updater->progress() < 100) {
288 const QString subTaskName = updater->mergedSubTaskName();
289
290 if (!subTaskName.isEmpty()) {
291 if (actionName.isEmpty()) {
292 actionName = subTaskName;
293 } else {
294 actionName = QString("%1: %2").arg(actionName).arg(subTaskName);
295 }
296 }
297 break;
298 }
299 }
300 progressProxy()->setAutoNestedName(actionName);
301 } else {
302 progressProxy()->setFormat(actionName);
303 }
304
305}
306
307void KoProgressUpdater::Private::clearState()
308{
309 for (auto it = subtasks.begin(); it != subtasks.end();) {
310 if (!(*it)->isPersistent()) {
311 (*it)->deleteLater();
312 it = subtasks.erase(it);
313 } else {
314 if ((*it)->interrupted()) {
315 (*it)->setInterrupted(false);
316 }
317 ++it;
318 }
319 }
320
321 canceled = false;
322}
323
325{
326 return d->canceled;
327}
328
330{
331 d->updateCompressor->setDelay(ms);
332}
333
335{
336 return d->updateCompressor->delay();
337}
338
340{
341 d->autoNestNames = value;
342}
343
345{
346 return d->autoNestNames;
347}
float value(const T *src, size_t ch)
const Params2D p
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void setAutoNestNames(bool value)
~KoProgressUpdater() override
destructor
KoProgressProxy * progressProxy()
void triggerUpdateAsynchronously()
KoProgressProxy * parentProgressProxy
KisSignalCompressor * updateCompressor
KoProgressUpdater * q
QPointer< KoUpdater > parentUpdater
void removePersistentSubtask(QPointer< KoUpdater > updater)
void start(int range=100, const QString &text="")
void setUpdateInterval(int ms)
QList< QPointer< KoUpdaterPrivate > > subtasks
QPointer< KoUpdater > startSubtask(int weight=1, const QString &name=QString(), bool isPersistent=false)
Private(KoProgressUpdater *_q, KoProgressProxy *proxy, QPointer< KoUpdater > parentUpdater, Mode _mode)
KoProgressUpdater(KoProgressProxy *progressProxy, Mode mode=Threaded)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130