QGIS API Documentation  3.2.0-Bonn (bc43194)
qgstaskmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstaskmanager.cpp
3  ------------------
4  begin : April 2016
5  copyright : (C) 2016 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgstaskmanager.h"
19 #include "qgsproject.h"
20 #include "qgsmaplayerlistutils.h"
21 #include <QtConcurrentRun>
22 
23 
24 //
25 // QgsTask
26 //
27 
28 QgsTask::QgsTask( const QString &name, Flags flags )
29  : mFlags( flags )
30  , mDescription( name )
31 {
32 }
33 
35 {
36  Q_ASSERT_X( mStatus != Running, "delete", QString( "status was %1" ).arg( mStatus ).toLatin1() );
37 
38  Q_FOREACH ( const SubTask &subTask, mSubTasks )
39  {
40  delete subTask.task;
41  }
42 }
43 
44 void QgsTask::start()
45 {
46  mNotFinishedMutex.lock();
47  mStartCount++;
48  Q_ASSERT( mStartCount == 1 );
49 
50  if ( mStatus != Queued )
51  return;
52 
53  mStatus = Running;
54  mOverallStatus = Running;
55  emit statusChanged( Running );
56  emit begun();
57 
58  // force initial emission of progressChanged, but respect if task has had initial progress manually set
59  setProgress( mProgress );
60 
61  if ( run() )
62  {
63  completed();
64  }
65  else
66  {
67  terminated();
68  }
69 }
70 
72 {
73  if ( mOverallStatus == Complete || mOverallStatus == Terminated )
74  return;
75 
76  mShouldTerminate = true;
77 
78 #if QT_VERSION >= 0x050500
79  //can't cancel queued tasks with qt < 5.5
80  if ( mStatus == Queued || mStatus == OnHold )
81  {
82  // immediately terminate unstarted jobs
83  terminated();
84  }
85 #endif
86 
87  if ( mStatus == Terminated )
88  {
89  processSubTasksForTermination();
90  }
91 
92  Q_FOREACH ( const SubTask &subTask, mSubTasks )
93  {
94  subTask.task->cancel();
95  }
96 }
97 
99 {
100  if ( mStatus == Queued )
101  {
102  mStatus = OnHold;
103  processSubTasksForHold();
104  }
105 
106  Q_FOREACH ( const SubTask &subTask, mSubTasks )
107  {
108  subTask.task->hold();
109  }
110 }
111 
113 {
114  if ( mStatus == OnHold )
115  {
116  mStatus = Queued;
117  mOverallStatus = Queued;
118  emit statusChanged( Queued );
119  }
120 
121  Q_FOREACH ( const SubTask &subTask, mSubTasks )
122  {
123  subTask.task->unhold();
124  }
125 }
126 
127 void QgsTask::addSubTask( QgsTask *subTask, const QgsTaskList &dependencies,
128  SubTaskDependency subTaskDependency )
129 {
130  mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
131  connect( subTask, &QgsTask::progressChanged, this, [ = ] { setProgress( mProgress ); } );
132  connect( subTask, &QgsTask::statusChanged, this, &QgsTask::subTaskStatusChanged );
133 }
134 
135 QList<QgsMapLayer *> QgsTask::dependentLayers() const
136 {
137  return _qgis_listQPointerToRaw( mDependentLayers );
138 }
139 
140 bool QgsTask::waitForFinished( unsigned long timeout )
141 {
142  bool rv = true;
143  if ( mOverallStatus == Complete || mOverallStatus == Terminated )
144  {
145  rv = true;
146  }
147  else
148  {
149  if ( timeout == 0 )
150  timeout = ULONG_MAX;
151  rv = mTaskFinished.wait( &mNotFinishedMutex, timeout );
152  }
153  return rv;
154 }
155 
156 void QgsTask::setDependentLayers( const QList< QgsMapLayer * > &dependentLayers )
157 {
158  mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
159 }
160 
161 void QgsTask::subTaskStatusChanged( int status )
162 {
163  QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
164  if ( !subTask )
165  return;
166 
167  if ( status == Running && mStatus == Queued )
168  {
169  mOverallStatus = Running;
170  }
171  else if ( status == Complete && mStatus == Complete )
172  {
173  //check again if all subtasks are complete
174  processSubTasksForCompletion();
175  }
176  else if ( ( status == Complete || status == Terminated ) && mStatus == Terminated )
177  {
178  //check again if all subtasks are terminated
179  processSubTasksForTermination();
180  }
181  else if ( ( status == Complete || status == Terminated || status == OnHold ) && mStatus == OnHold )
182  {
183  processSubTasksForHold();
184  }
185  else if ( status == Terminated )
186  {
187  //uh oh...
188  cancel();
189  }
190 }
191 
193 {
194  mProgress = progress;
195 
196  if ( !mSubTasks.isEmpty() )
197  {
198  // calculate total progress including subtasks
199 
200  double totalProgress = 0.0;
201  Q_FOREACH ( const SubTask &subTask, mSubTasks )
202  {
203  if ( subTask.task->status() == QgsTask::Complete )
204  {
205  totalProgress += 100.0;
206  }
207  else
208  {
209  totalProgress += subTask.task->progress();
210  }
211  }
212  progress = ( progress + totalProgress ) / ( mSubTasks.count() + 1 );
213  }
214 
215  // avoid flooding with too many events
216  if ( static_cast< int >( mTotalProgress * 10 ) != static_cast< int >( progress * 10 ) )
217  emit progressChanged( progress );
218 
219  mTotalProgress = progress;
220 }
221 
222 void QgsTask::completed()
223 {
224  mStatus = Complete;
225  processSubTasksForCompletion();
226 }
227 
228 void QgsTask::processSubTasksForCompletion()
229 {
230  bool subTasksCompleted = true;
231  Q_FOREACH ( const SubTask &subTask, mSubTasks )
232  {
233  if ( subTask.task->status() != Complete )
234  {
235  subTasksCompleted = false;
236  break;
237  }
238  }
239 
240  if ( mStatus == Complete && subTasksCompleted )
241  {
242  mOverallStatus = Complete;
243 
244  setProgress( 100.0 );
245  emit statusChanged( Complete );
246  emit taskCompleted();
247  mTaskFinished.wakeAll();
248  mNotFinishedMutex.unlock();
249  mTaskFinished.wakeAll();
250  }
251  else if ( mStatus == Complete )
252  {
253  // defer completion until all subtasks are complete
254  mOverallStatus = Running;
255  }
256 }
257 
258 void QgsTask::processSubTasksForTermination()
259 {
260  bool subTasksTerminated = true;
261  Q_FOREACH ( const SubTask &subTask, mSubTasks )
262  {
263  if ( subTask.task->status() != Terminated && subTask.task->status() != Complete )
264  {
265  subTasksTerminated = false;
266  break;
267  }
268  }
269 
270  if ( mStatus == Terminated && subTasksTerminated && mOverallStatus != Terminated )
271  {
272  mOverallStatus = Terminated;
273 
274  emit statusChanged( Terminated );
275  emit taskTerminated();
276  mTaskFinished.wakeAll();
277  mNotFinishedMutex.unlock();
278  mTaskFinished.wakeAll();
279  }
280  else if ( mStatus == Terminated && !subTasksTerminated )
281  {
282  // defer termination until all subtasks are terminated (or complete)
283  mOverallStatus = Running;
284  }
285 }
286 
287 void QgsTask::processSubTasksForHold()
288 {
289  bool subTasksRunning = false;
290  Q_FOREACH ( const SubTask &subTask, mSubTasks )
291  {
292  if ( subTask.task->status() == Running )
293  {
294  subTasksRunning = true;
295  break;
296  }
297  }
298 
299  if ( mStatus == OnHold && !subTasksRunning && mOverallStatus != OnHold )
300  {
301  mOverallStatus = OnHold;
302  emit statusChanged( OnHold );
303  }
304  else if ( mStatus == OnHold && subTasksRunning )
305  {
306  // defer hold until all subtasks finish running
307  mOverallStatus = Running;
308  }
309 }
310 
311 void QgsTask::terminated()
312 {
313  mStatus = Terminated;
314  processSubTasksForTermination();
315 }
316 
317 
319 
320 class QgsTaskRunnableWrapper : public QRunnable
321 {
322  public:
323 
324  explicit QgsTaskRunnableWrapper( QgsTask *task )
325  : mTask( task )
326  {
327  setAutoDelete( true );
328  }
329 
330  void run() override
331  {
332  Q_ASSERT( mTask );
333  mTask->start();
334  }
335 
336  private:
337 
338  QgsTask *mTask = nullptr;
339 
340 };
341 
343 
344 
345 
346 //
347 // QgsTaskManager
348 //
349 
351  : QObject( parent )
352  , mTaskMutex( new QMutex( QMutex::Recursive ) )
353 {
354  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList< QgsMapLayer * >& ) > ( &QgsProject::layersWillBeRemoved ),
355  this, &QgsTaskManager::layersWillBeRemoved );
356 }
357 
359 {
360  //first tell all tasks to cancel
361  cancelAll();
362 
363  //then clean them up, including waiting for them to terminate
364  mTaskMutex->lock();
365  QMap< long, TaskInfo > tasks = mTasks;
366  mTasks.detach();
367  mTaskMutex->unlock();
368  QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
369  for ( ; it != tasks.constEnd(); ++it )
370  {
371  cleanupAndDeleteTask( it.value().task );
372  }
373 
374  delete mTaskMutex;
375 }
376 
377 long QgsTaskManager::addTask( QgsTask *task, int priority )
378 {
379  return addTaskPrivate( task, QgsTaskList(), false, priority );
380 }
381 
382 long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
383 {
384  return addTaskPrivate( definition.task,
385  definition.dependentTasks,
386  false,
387  priority );
388 }
389 
390 
391 long QgsTaskManager::addTaskPrivate( QgsTask *task, QgsTaskList dependencies, bool isSubTask, int priority )
392 {
393  if ( !task )
394  return 0;
395 
396  long taskId = mNextTaskId++;
397 
398  mTaskMutex->lock();
399  mTasks.insert( taskId, TaskInfo( task, priority ) );
400  if ( isSubTask )
401  {
402  mSubTasks << task;
403  }
404  else
405  {
406  mParentTasks << task;
407  }
408  if ( !task->dependentLayers().isEmpty() )
409  mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->dependentLayers() ) );
410  mTaskMutex->unlock();
411 
412  connect( task, &QgsTask::statusChanged, this, &QgsTaskManager::taskStatusChanged );
413  if ( !isSubTask )
414  {
415  connect( task, &QgsTask::progressChanged, this, &QgsTaskManager::taskProgressChanged );
416  }
417 
418  // add all subtasks, must be done before dependency resolution
419  Q_FOREACH ( const QgsTask::SubTask &subTask, task->mSubTasks )
420  {
421  switch ( subTask.dependency )
422  {
424  dependencies << subTask.task;
425  break;
426 
428  //nothing
429  break;
430  }
431  //recursively add sub tasks
432  addTaskPrivate( subTask.task, subTask.dependencies, true, priority );
433  }
434 
435  if ( !dependencies.isEmpty() )
436  {
437  mTaskDependencies.insert( taskId, dependencies );
438  }
439 
440  if ( hasCircularDependencies( taskId ) )
441  {
442  task->cancel();
443  }
444 
445  if ( !isSubTask )
446  {
447  emit taskAdded( taskId );
448  processQueue();
449  }
450 
451  return taskId;
452 }
453 
454 QgsTask *QgsTaskManager::task( long id ) const
455 {
456  QMutexLocker ml( mTaskMutex );
457  QgsTask *t = nullptr;
458  if ( mTasks.contains( id ) )
459  t = mTasks.value( id ).task;
460  return t;
461 }
462 
463 QList<QgsTask *> QgsTaskManager::tasks() const
464 {
465  QMutexLocker ml( mTaskMutex );
466  return mParentTasks.toList();
467 }
468 
470 {
471  QMutexLocker ml( mTaskMutex );
472  return mParentTasks.count();
473 }
474 
475 long QgsTaskManager::taskId( QgsTask *task ) const
476 {
477  if ( !task )
478  return -1;
479 
480  QMutexLocker ml( mTaskMutex );
481  QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
482  for ( ; it != mTasks.constEnd(); ++it )
483  {
484  if ( it.value().task == task )
485  {
486  return it.key();
487  }
488  }
489  return -1;
490 }
491 
493 {
494  mTaskMutex->lock();
495  QSet< QgsTask * > parents = mParentTasks;
496  parents.detach();
497  mTaskMutex->unlock();
498 
499  Q_FOREACH ( QgsTask *task, parents )
500  {
501  task->cancel();
502  }
503 }
504 
506 {
507  mTaskMutex->lock();
508  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
509  dependencies.detach();
510  mTaskMutex->unlock();
511 
512  if ( !dependencies.contains( taskId ) )
513  return true;
514 
515  Q_FOREACH ( QgsTask *task, dependencies.value( taskId ) )
516  {
517  if ( task->status() != QgsTask::Complete )
518  return false;
519  }
520 
521  return true;
522 }
523 
524 QSet<long> QgsTaskManager::dependencies( long taskId ) const
525 {
526  QSet<long> results;
527  if ( resolveDependencies( taskId, taskId, results ) )
528  return results;
529  else
530  return QSet<long>();
531 }
532 
533 bool QgsTaskManager::resolveDependencies( long firstTaskId, long currentTaskId, QSet<long> &results ) const
534 {
535  mTaskMutex->lock();
536  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
537  dependencies.detach();
538  mTaskMutex->unlock();
539 
540  if ( !dependencies.contains( currentTaskId ) )
541  return true;
542 
543  Q_FOREACH ( QgsTask *task, dependencies.value( currentTaskId ) )
544  {
545  long dependentTaskId = taskId( task );
546  if ( dependentTaskId >= 0 )
547  {
548  if ( dependentTaskId == firstTaskId )
549  // circular
550  return false;
551 
552  //add task as dependent
553  results.insert( dependentTaskId );
554  //plus all its other dependencies
555  QSet< long > newTaskDeps;
556  if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
557  return false;
558 
559  if ( newTaskDeps.contains( firstTaskId ) )
560  {
561  // circular
562  return false;
563  }
564 
565  results.unite( newTaskDeps );
566  }
567  }
568 
569  return true;
570 }
571 
572 bool QgsTaskManager::hasCircularDependencies( long taskId ) const
573 {
574  QSet< long > d;
575  return !resolveDependencies( taskId, taskId, d );
576 }
577 
578 QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
579 {
580  QMutexLocker ml( mTaskMutex );
581  return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
582 }
583 
584 QList<QgsTask *> QgsTaskManager::tasksDependentOnLayer( QgsMapLayer *layer ) const
585 {
586  QMutexLocker ml( mTaskMutex );
587  QList< QgsTask * > tasks;
588  QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
589  for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
590  {
591  if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
592  {
593  QgsTask *layerTask = task( layerIt.key() );
594  if ( layerTask )
595  tasks << layerTask;
596  }
597  }
598  return tasks;
599 }
600 
601 QList<QgsTask *> QgsTaskManager::activeTasks() const
602 {
603  QMutexLocker ml( mTaskMutex );
604  QSet< QgsTask * > activeTasks = mActiveTasks;
605  activeTasks.intersect( mParentTasks );
606  return activeTasks.toList();
607 }
608 
610 {
611  QMutexLocker ml( mTaskMutex );
612  QSet< QgsTask * > tasks = mActiveTasks;
613  return tasks.intersect( mParentTasks ).count();
614 }
615 
617 {
618  if ( task )
619  emit taskTriggered( task );
620 }
621 
622 void QgsTaskManager::taskProgressChanged( double progress )
623 {
624  QgsTask *task = qobject_cast< QgsTask * >( sender() );
625 
626  //find ID of task
627  long id = taskId( task );
628  if ( id < 0 )
629  return;
630 
631  emit progressChanged( id, progress );
632 
633  if ( countActiveTasks() == 1 )
634  {
635  emit finalTaskProgressChanged( progress );
636  }
637 }
638 
639 void QgsTaskManager::taskStatusChanged( int status )
640 {
641  QgsTask *task = qobject_cast< QgsTask * >( sender() );
642 
643  //find ID of task
644  long id = taskId( task );
645  if ( id < 0 )
646  return;
647 
648 
649 #if QT_VERSION >= 0x050500
650  mTaskMutex->lock();
651  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
652  mTaskMutex->unlock();
653  if ( runnable )
654  QThreadPool::globalInstance()->cancel( runnable );
655 #endif
656 
657  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
658  {
659  bool result = status == QgsTask::Complete;
660  task->finished( result );
661  }
662 
663  if ( status == QgsTask::Terminated )
664  {
665  //recursively cancel dependent tasks
666  cancelDependentTasks( id );
667  }
668 
669  mTaskMutex->lock();
670  bool isParent = mParentTasks.contains( task );
671  mTaskMutex->unlock();
672  if ( isParent )
673  {
674  // don't emit status changed for subtasks
675  emit statusChanged( id, status );
676  }
677 
678  processQueue();
679 
680  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
681  {
682  cleanupAndDeleteTask( task );
683  }
684 
685 }
686 
687 void QgsTaskManager::layersWillBeRemoved( const QList< QgsMapLayer * > &layers )
688 {
689  mTaskMutex->lock();
690  // scan through layers to be removed
691  QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
692  layerDependencies.detach();
693  mTaskMutex->unlock();
694 
695  Q_FOREACH ( QgsMapLayer *layer, layers )
696  {
697  // scan through tasks with layer dependencies
698  for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
699  it != layerDependencies.constEnd(); ++it )
700  {
701  if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
702  {
703  //task not dependent on this layer
704  continue;
705  }
706 
707  QgsTask *dependentTask = task( it.key() );
708  if ( dependentTask && ( dependentTask->status() != QgsTask::Complete && dependentTask->status() != QgsTask::Terminated ) )
709  {
710  // incomplete task is dependent on this layer!
711  dependentTask->cancel();
712  }
713  }
714  }
715 }
716 
717 
718 bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
719 {
720  if ( !task )
721  return false;
722 
723  long id = taskId( task );
724  if ( id < 0 )
725  return false;
726 
727  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
728 
729  task->disconnect( this );
730 
731  mTaskMutex->lock();
732  if ( mTaskDependencies.contains( id ) )
733  mTaskDependencies.remove( id );
734  mTaskMutex->unlock();
735 
736  emit taskAboutToBeDeleted( id );
737 
738  mTaskMutex->lock();
739  bool isParent = mParentTasks.contains( task );
740  mParentTasks.remove( task );
741  mSubTasks.remove( task );
742  mTasks.remove( id );
743  mLayerDependencies.remove( id );
744 
745  if ( task->status() != QgsTask::Complete && task->status() != QgsTask::Terminated )
746  {
747  if ( isParent )
748  {
749  // delete task when it's terminated
750  connect( task, &QgsTask::taskCompleted, task, &QgsTask::deleteLater );
751  connect( task, &QgsTask::taskTerminated, task, &QgsTask::deleteLater );
752  }
753  task->cancel();
754  }
755  else
756  {
757 #if QT_VERSION >= 0x050500
758  if ( runnable )
759  QThreadPool::globalInstance()->cancel( runnable );
760 #endif
761  if ( isParent )
762  {
763  //task already finished, kill it
764  task->deleteLater();
765  }
766  }
767 
768  // at this stage (hopefully) dependent tasks have been canceled or queued
769  for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
770  {
771  if ( it.value().contains( task ) )
772  {
773  it.value().removeAll( task );
774  }
775  }
776  mTaskMutex->unlock();
777 
778  return true;
779 }
780 
781 void QgsTaskManager::processQueue()
782 {
783  int prevActiveCount = countActiveTasks();
784  mTaskMutex->lock();
785  mActiveTasks.clear();
786  for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
787  {
788  QgsTask *task = it.value().task;
789  if ( task && task->mStatus == QgsTask::Queued && dependenciesSatisfied( it.key() ) && it.value().added.testAndSetRelaxed( 0, 1 ) )
790  {
791  it.value().createRunnable();
792  QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
793  }
794 
795  if ( task && ( task->mStatus != QgsTask::Complete && task->mStatus != QgsTask::Terminated ) )
796  {
797  mActiveTasks << task;
798  }
799  }
800 
801  bool allFinished = mActiveTasks.isEmpty();
802  mTaskMutex->unlock();
803 
804  if ( allFinished )
805  {
806  emit allTasksFinished();
807  }
808 
809  int newActiveCount = countActiveTasks();
810  if ( prevActiveCount != newActiveCount )
811  {
812  emit countActiveTasksChanged( newActiveCount );
813  }
814 }
815 
816 void QgsTaskManager::cancelDependentTasks( long taskId )
817 {
818  QgsTask *canceledTask = task( taskId );
819 
820  //deep copy
821  mTaskMutex->lock();
822  QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
823  taskDependencies.detach();
824  mTaskMutex->unlock();
825 
826  QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
827  for ( ; it != taskDependencies.constEnd(); ++it )
828  {
829  if ( it.value().contains( canceledTask ) )
830  {
831  // found task with this dependency
832 
833  // cancel it - note that this will be recursive, so any tasks dependent
834  // on this one will also be canceled
835  QgsTask *dependentTask = task( it.key() );
836  if ( dependentTask )
837  dependentTask->cancel();
838  }
839  }
840 }
841 
842 QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
843  : task( task )
844  , added( 0 )
845  , priority( priority )
846 {}
847 
848 void QgsTaskManager::TaskInfo::createRunnable()
849 {
850  Q_ASSERT( !runnable );
851  runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
852 }
bool dependenciesSatisfied(long taskId) const
Returns true if all dependencies for the specified task are satisfied.
int countActiveTasks() const
Returns the number of active (queued or running) tasks.
void taskTerminated()
Will be emitted by task if it has terminated for any reason other then completion (e...
void setProgress(double progress)
Sets the task&#39;s current progress.
Base class for all map layer types.
Definition: qgsmaplayer.h:61
void hold()
Places the task on hold.
void taskCompleted()
Will be emitted by task to indicate its successful completion.
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
QList< QgsMapLayer *> dependentLayers() const
Returns the list of layers on which the task depends.
QgsTask(const QString &description=QString(), QgsTask::Flags flags=AllFlags)
Constructor for QgsTask.
void statusChanged(int status)
Will be emitted by task when its status changes.
QList< QgsTask *> QgsTaskList
List of QgsTask objects.
int count() const
Returns the number of tasks tracked by the manager.
Subtask must complete before parent can begin.
QgsTaskManager(QObject *parent=nullptr)
Constructor for QgsTaskManager.
void setDependentLayers(const QList< QgsMapLayer *> &dependentLayers)
Sets a list of layers on which the task depends.
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
void begun()
Will be emitted by task to indicate its commencement.
SubTaskDependency
Controls how subtasks relate to their parent task.
void cancelAll()
Instructs all tasks tracked by the manager to terminate.
QgsTaskList dependentTasks
List of dependent tasks which must be completed before task can run.
void progressChanged(double progress)
Will be emitted by task when its progress changes.
QSet< long > dependencies(long taskId) const
Returns the set of task IDs on which a task is dependent.
void taskAboutToBeDeleted(long taskId)
Emitted when a task is about to be deleted.
Task was terminated or errored.
void countActiveTasksChanged(int count)
Emitted when the number of active tasks changes.
void triggerTask(QgsTask *task)
Triggers a task, e.g.
QList< QgsTask *> tasksDependentOnLayer(QgsMapLayer *layer) const
Returns a list of tasks which depend on a layer.
QgsTask * task(long id) const
Returns the task with matching ID.
~QgsTask() override
void progressChanged(long taskId, double progress)
Will be emitted when a task reports a progress change.
Definition of a task for inclusion in the manager.
Task is queued but on hold and will not be started.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
Abstract base class for long running background tasks.
Reads and writes project states.
Definition: qgsproject.h:85
friend class QgsTaskRunnableWrapper
void statusChanged(long taskId, int status)
Will be emitted when a task reports a status change.
Task successfully completed.
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
virtual void cancel()
Notifies the task that it should terminate.
void allTasksFinished()
Emitted when all tasks are complete.
Task is queued and has not begun.
double progress() const
Returns the task&#39;s progress (between 0.0 and 100.0)
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QList< QgsTask *> activeTasks() const
Returns a list of the active (queued or running) tasks.
Task is currently running.
~QgsTaskManager() override
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
Definition: qgsmaplayer.h:1392
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:391
long taskId(QgsTask *task) const
Returns the unique task ID corresponding to a task managed by the class.
virtual void finished(bool result)
If the task is managed by a QgsTaskManager, this will be called after the task has finished (whether ...
virtual bool run()=0
Performs the task&#39;s operation.
void finalTaskProgressChanged(double progress)
Will be emitted when only a single task remains to complete and that task has reported a progress cha...
Subtask is independent of the parent, and can run before, after or at the same time as the parent...
void addSubTask(QgsTask *subTask, const QgsTaskList &dependencies=QgsTaskList(), SubTaskDependency subTaskDependency=SubTaskIndependent)
Adds a subtask to this task.
void unhold()
Releases the task from being held.
bool waitForFinished(unsigned long timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
TaskStatus status() const
Returns the current task status.
QList< QgsMapLayer *> dependentLayers(long taskId) const
Returns a list of layers on which as task is dependent.