QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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  , mNotStartedMutex( 1 )
32 {
33  mNotStartedMutex.acquire();
34 }
35 
37 {
38  Q_ASSERT_X( mStatus != Running, "delete", QStringLiteral( "status was %1" ).arg( mStatus ).toLatin1() );
39  mNotFinishedMutex.tryLock(); // we're not guaranteed to already have the lock in place here
40  const auto constMSubTasks = mSubTasks;
41  for ( const SubTask &subTask : constMSubTasks )
42  {
43  delete subTask.task;
44  }
45  mNotFinishedMutex.unlock();
46  mNotStartedMutex.release();
47 }
48 
49 void QgsTask::setDescription( const QString &description )
50 {
51  mDescription = description;
52 }
53 
54 qint64 QgsTask::elapsedTime() const
55 {
56  return mElapsedTime.elapsed();
57 }
58 
59 void QgsTask::start()
60 {
61  mNotFinishedMutex.lock();
62  mNotStartedMutex.release();
63  mStartCount++;
64  Q_ASSERT( mStartCount == 1 );
65 
66  if ( mStatus != Queued )
67  return;
68 
69  mStatus = Running;
70  mOverallStatus = Running;
71  mElapsedTime.start();
72 
73  emit statusChanged( Running );
74  emit begun();
75 
76  // force initial emission of progressChanged, but respect if task has had initial progress manually set
77  setProgress( mProgress );
78 
79  if ( run() )
80  {
81  completed();
82  }
83  else
84  {
85  terminated();
86  }
87 }
88 
90 {
91  if ( mOverallStatus == Complete || mOverallStatus == Terminated )
92  return;
93 
94  mShouldTerminate = true;
95  if ( mStatus == Queued || mStatus == OnHold )
96  {
97  // immediately terminate unstarted jobs
98  terminated();
99  mNotStartedMutex.release();
100  }
101 
102  if ( mStatus == Terminated )
103  {
104  processSubTasksForTermination();
105  }
106 
107  const auto constMSubTasks = mSubTasks;
108  for ( const SubTask &subTask : constMSubTasks )
109  {
110  subTask.task->cancel();
111  }
112 }
113 
115 {
116  if ( mStatus == Queued )
117  {
118  mStatus = OnHold;
119  processSubTasksForHold();
120  }
121 
122  const auto constMSubTasks = mSubTasks;
123  for ( const SubTask &subTask : constMSubTasks )
124  {
125  subTask.task->hold();
126  }
127 }
128 
130 {
131  if ( mStatus == OnHold )
132  {
133  mStatus = Queued;
134  mOverallStatus = Queued;
135  emit statusChanged( Queued );
136  }
137 
138  const auto constMSubTasks = mSubTasks;
139  for ( const SubTask &subTask : constMSubTasks )
140  {
141  subTask.task->unhold();
142  }
143 }
144 
145 void QgsTask::addSubTask( QgsTask *subTask, const QgsTaskList &dependencies,
146  SubTaskDependency subTaskDependency )
147 {
148  mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
149  connect( subTask, &QgsTask::progressChanged, this, [ = ] { setProgress( mProgress ); } );
150  connect( subTask, &QgsTask::statusChanged, this, &QgsTask::subTaskStatusChanged );
151 }
152 
153 QList<QgsMapLayer *> QgsTask::dependentLayers() const
154 {
155  return _qgis_listQPointerToRaw( mDependentLayers );
156 }
157 
158 bool QgsTask::waitForFinished( int timeout )
159 {
160  // We wait the task to be started
161  mNotStartedMutex.acquire();
162 
163  bool rv = true;
164  if ( mOverallStatus == Complete || mOverallStatus == Terminated )
165  {
166  rv = true;
167  }
168  else
169  {
170  if ( timeout == 0 )
171  timeout = std::numeric_limits< int >::max();
172  if ( mNotFinishedMutex.tryLock( timeout ) )
173  {
174  mNotFinishedMutex.unlock();
175  rv = true;
176  }
177  else
178  {
179  rv = false;
180  }
181  }
182  return rv;
183 }
184 
185 void QgsTask::setDependentLayers( const QList< QgsMapLayer * > &dependentLayers )
186 {
187  mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
188 }
189 
190 void QgsTask::subTaskStatusChanged( int status )
191 {
192  QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
193  if ( !subTask )
194  return;
195 
196  if ( status == Running && mStatus == Queued )
197  {
198  mOverallStatus = Running;
199  }
200  else if ( status == Complete && mStatus == Complete )
201  {
202  //check again if all subtasks are complete
203  processSubTasksForCompletion();
204  }
205  else if ( ( status == Complete || status == Terminated ) && mStatus == Terminated )
206  {
207  //check again if all subtasks are terminated
208  processSubTasksForTermination();
209  }
210  else if ( ( status == Complete || status == Terminated || status == OnHold ) && mStatus == OnHold )
211  {
212  processSubTasksForHold();
213  }
214  else if ( status == Terminated )
215  {
216  //uh oh...
217  cancel();
218  }
219 }
220 
222 {
223  mProgress = progress;
224 
225  if ( !mSubTasks.isEmpty() )
226  {
227  // calculate total progress including subtasks
228 
229  double totalProgress = 0.0;
230  const auto constMSubTasks = mSubTasks;
231  for ( const SubTask &subTask : constMSubTasks )
232  {
233  if ( subTask.task->status() == QgsTask::Complete )
234  {
235  totalProgress += 100.0;
236  }
237  else
238  {
239  totalProgress += subTask.task->progress();
240  }
241  }
242  progress = ( progress + totalProgress ) / ( mSubTasks.count() + 1 );
243  }
244 
245  // avoid flooding with too many events
246  double prevProgress = mTotalProgress;
247  mTotalProgress = progress;
248 
249  // avoid spamming with too many progressChanged reports
250  if ( static_cast< int >( prevProgress * 10 ) != static_cast< int >( mTotalProgress * 10 ) )
251  emit progressChanged( progress );
252 }
253 
254 void QgsTask::completed()
255 {
256  mStatus = Complete;
257  processSubTasksForCompletion();
258 }
259 
260 void QgsTask::processSubTasksForCompletion()
261 {
262  bool subTasksCompleted = true;
263  const auto constMSubTasks = mSubTasks;
264  for ( const SubTask &subTask : constMSubTasks )
265  {
266  if ( subTask.task->status() != Complete )
267  {
268  subTasksCompleted = false;
269  break;
270  }
271  }
272 
273  if ( mStatus == Complete && subTasksCompleted )
274  {
275  mOverallStatus = Complete;
276 
277  setProgress( 100.0 );
278  emit statusChanged( Complete );
279  emit taskCompleted();
280  mNotFinishedMutex.tryLock(); // we're not guaranteed to already have the lock in place here
281  mNotFinishedMutex.unlock();
282  }
283  else if ( mStatus == Complete )
284  {
285  // defer completion until all subtasks are complete
286  mOverallStatus = Running;
287  }
288 }
289 
290 void QgsTask::processSubTasksForTermination()
291 {
292  bool subTasksTerminated = true;
293  const auto constMSubTasks = mSubTasks;
294  for ( const SubTask &subTask : constMSubTasks )
295  {
296  if ( subTask.task->status() != Terminated && subTask.task->status() != Complete )
297  {
298  subTasksTerminated = false;
299  break;
300  }
301  }
302 
303  if ( mStatus == Terminated && subTasksTerminated && mOverallStatus != Terminated )
304  {
305  mOverallStatus = Terminated;
306 
307  emit statusChanged( Terminated );
308  emit taskTerminated();
309  mNotFinishedMutex.tryLock(); // we're not guaranteed to already have the lock in place here
310  mNotFinishedMutex.unlock();
311  }
312  else if ( mStatus == Terminated && !subTasksTerminated )
313  {
314  // defer termination until all subtasks are terminated (or complete)
315  mOverallStatus = Running;
316  }
317 }
318 
319 void QgsTask::processSubTasksForHold()
320 {
321  bool subTasksRunning = false;
322  const auto constMSubTasks = mSubTasks;
323  for ( const SubTask &subTask : constMSubTasks )
324  {
325  if ( subTask.task->status() == Running )
326  {
327  subTasksRunning = true;
328  break;
329  }
330  }
331 
332  if ( mStatus == OnHold && !subTasksRunning && mOverallStatus != OnHold )
333  {
334  mOverallStatus = OnHold;
335  emit statusChanged( OnHold );
336  }
337  else if ( mStatus == OnHold && subTasksRunning )
338  {
339  // defer hold until all subtasks finish running
340  mOverallStatus = Running;
341  }
342 }
343 
344 void QgsTask::terminated()
345 {
346  mStatus = Terminated;
347  processSubTasksForTermination();
348 }
349 
350 
352 
353 class QgsTaskRunnableWrapper : public QRunnable
354 {
355  public:
356 
357  explicit QgsTaskRunnableWrapper( QgsTask *task )
358  : mTask( task )
359  {
360  setAutoDelete( true );
361  }
362 
363  void run() override
364  {
365  Q_ASSERT( mTask );
366  mTask->start();
367  }
368 
369  private:
370 
371  QgsTask *mTask = nullptr;
372 
373 };
374 
376 
377 
378 
379 //
380 // QgsTaskManager
381 //
382 
384  : QObject( parent )
385  , mTaskMutex( new QMutex( QMutex::Recursive ) )
386 {
387  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList< QgsMapLayer * >& ) > ( &QgsProject::layersWillBeRemoved ),
388  this, &QgsTaskManager::layersWillBeRemoved );
389 }
390 
392 {
393  //first tell all tasks to cancel
394  cancelAll();
395 
396  //then clean them up, including waiting for them to terminate
397  mTaskMutex->lock();
398  QMap< long, TaskInfo > tasks = mTasks;
399  mTasks.detach();
400  mTaskMutex->unlock();
401  QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
402  for ( ; it != tasks.constEnd(); ++it )
403  {
404  cleanupAndDeleteTask( it.value().task );
405  }
406 
407  delete mTaskMutex;
408 }
409 
410 long QgsTaskManager::addTask( QgsTask *task, int priority )
411 {
412  return addTaskPrivate( task, QgsTaskList(), false, priority );
413 }
414 
415 long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
416 {
417  return addTaskPrivate( definition.task,
418  definition.dependentTasks,
419  false,
420  priority );
421 }
422 
423 
424 long QgsTaskManager::addTaskPrivate( QgsTask *task, QgsTaskList dependencies, bool isSubTask, int priority )
425 {
426  if ( !task )
427  return 0;
428 
429  long taskId = mNextTaskId++;
430 
431  mTaskMutex->lock();
432  mTasks.insert( taskId, TaskInfo( task, priority ) );
433  if ( isSubTask )
434  {
435  mSubTasks << task;
436  }
437  else
438  {
439  mParentTasks << task;
440  }
441  if ( !task->dependentLayers().isEmpty() )
442  mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->dependentLayers() ) );
443  mTaskMutex->unlock();
444 
445  connect( task, &QgsTask::statusChanged, this, &QgsTaskManager::taskStatusChanged );
446  if ( !isSubTask )
447  {
448  connect( task, &QgsTask::progressChanged, this, &QgsTaskManager::taskProgressChanged );
449  }
450 
451  // add all subtasks, must be done before dependency resolution
452  for ( const QgsTask::SubTask &subTask : qgis::as_const( task->mSubTasks ) )
453  {
454  switch ( subTask.dependency )
455  {
457  dependencies << subTask.task;
458  break;
459 
461  //nothing
462  break;
463  }
464  //recursively add sub tasks
465  addTaskPrivate( subTask.task, subTask.dependencies, true, priority );
466  }
467 
468  if ( !dependencies.isEmpty() )
469  {
470  mTaskDependencies.insert( taskId, dependencies );
471  }
472 
473  if ( hasCircularDependencies( taskId ) )
474  {
475  task->cancel();
476  }
477 
478  if ( !isSubTask )
479  {
480  emit taskAdded( taskId );
481  processQueue();
482  }
483 
484  return taskId;
485 }
486 
487 QgsTask *QgsTaskManager::task( long id ) const
488 {
489  QMutexLocker ml( mTaskMutex );
490  QgsTask *t = nullptr;
491  if ( mTasks.contains( id ) )
492  t = mTasks.value( id ).task;
493  return t;
494 }
495 
496 QList<QgsTask *> QgsTaskManager::tasks() const
497 {
498  QMutexLocker ml( mTaskMutex );
499  return mParentTasks.toList();
500 }
501 
503 {
504  QMutexLocker ml( mTaskMutex );
505  return mParentTasks.count();
506 }
507 
508 long QgsTaskManager::taskId( QgsTask *task ) const
509 {
510  if ( !task )
511  return -1;
512 
513  QMutexLocker ml( mTaskMutex );
514  QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
515  for ( ; it != mTasks.constEnd(); ++it )
516  {
517  if ( it.value().task == task )
518  {
519  return it.key();
520  }
521  }
522  return -1;
523 }
524 
526 {
527  mTaskMutex->lock();
528  QSet< QgsTask * > parents = mParentTasks;
529  parents.detach();
530  mTaskMutex->unlock();
531 
532  const auto constParents = parents;
533  for ( QgsTask *task : constParents )
534  {
535  task->cancel();
536  }
537 }
538 
540 {
541  mTaskMutex->lock();
542  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
543  dependencies.detach();
544  mTaskMutex->unlock();
545 
546  if ( !dependencies.contains( taskId ) )
547  return true;
548 
549  const auto constValue = dependencies.value( taskId );
550  for ( QgsTask *task : constValue )
551  {
552  if ( task->status() != QgsTask::Complete )
553  return false;
554  }
555 
556  return true;
557 }
558 
559 QSet<long> QgsTaskManager::dependencies( long taskId ) const
560 {
561  QSet<long> results;
562  if ( resolveDependencies( taskId, taskId, results ) )
563  return results;
564  else
565  return QSet<long>();
566 }
567 
568 bool QgsTaskManager::resolveDependencies( long firstTaskId, long currentTaskId, QSet<long> &results ) const
569 {
570  mTaskMutex->lock();
571  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
572  dependencies.detach();
573  mTaskMutex->unlock();
574 
575  if ( !dependencies.contains( currentTaskId ) )
576  return true;
577 
578  const auto constValue = dependencies.value( currentTaskId );
579  for ( QgsTask *task : constValue )
580  {
581  long dependentTaskId = taskId( task );
582  if ( dependentTaskId >= 0 )
583  {
584  if ( dependentTaskId == firstTaskId )
585  // circular
586  return false;
587 
588  //add task as dependent
589  results.insert( dependentTaskId );
590  //plus all its other dependencies
591  QSet< long > newTaskDeps;
592  if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
593  return false;
594 
595  if ( newTaskDeps.contains( firstTaskId ) )
596  {
597  // circular
598  return false;
599  }
600 
601  results.unite( newTaskDeps );
602  }
603  }
604 
605  return true;
606 }
607 
608 bool QgsTaskManager::hasCircularDependencies( long taskId ) const
609 {
610  QSet< long > d;
611  return !resolveDependencies( taskId, taskId, d );
612 }
613 
614 QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
615 {
616  QMutexLocker ml( mTaskMutex );
617  return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
618 }
619 
620 QList<QgsTask *> QgsTaskManager::tasksDependentOnLayer( QgsMapLayer *layer ) const
621 {
622  QMutexLocker ml( mTaskMutex );
623  QList< QgsTask * > tasks;
624  QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
625  for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
626  {
627  if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
628  {
629  QgsTask *layerTask = task( layerIt.key() );
630  if ( layerTask )
631  tasks << layerTask;
632  }
633  }
634  return tasks;
635 }
636 
637 QList<QgsTask *> QgsTaskManager::activeTasks() const
638 {
639  QMutexLocker ml( mTaskMutex );
640  QSet< QgsTask * > activeTasks = mActiveTasks;
641  activeTasks.intersect( mParentTasks );
642  return activeTasks.toList();
643 }
644 
646 {
647  QMutexLocker ml( mTaskMutex );
648  QSet< QgsTask * > tasks = mActiveTasks;
649  return tasks.intersect( mParentTasks ).count();
650 }
651 
653 {
654  if ( task )
655  emit taskTriggered( task );
656 }
657 
658 void QgsTaskManager::taskProgressChanged( double progress )
659 {
660  QgsTask *task = qobject_cast< QgsTask * >( sender() );
661 
662  //find ID of task
663  long id = taskId( task );
664  if ( id < 0 )
665  return;
666 
667  emit progressChanged( id, progress );
668 
669  if ( countActiveTasks() == 1 )
670  {
671  emit finalTaskProgressChanged( progress );
672  }
673 }
674 
675 void QgsTaskManager::taskStatusChanged( int status )
676 {
677  QgsTask *task = qobject_cast< QgsTask * >( sender() );
678 
679  //find ID of task
680  long id = taskId( task );
681  if ( id < 0 )
682  return;
683 
684  mTaskMutex->lock();
685  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
686  mTaskMutex->unlock();
687  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
688  {
689  delete runnable;
690  mTasks[ id ].runnable = nullptr;
691  }
692 
693  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
694  {
695  bool result = status == QgsTask::Complete;
696  task->finished( result );
697  }
698 
699  if ( status == QgsTask::Terminated )
700  {
701  //recursively cancel dependent tasks
702  cancelDependentTasks( id );
703  }
704 
705  mTaskMutex->lock();
706  bool isParent = mParentTasks.contains( task );
707  mTaskMutex->unlock();
708  if ( isParent )
709  {
710  // don't emit status changed for subtasks
711  emit statusChanged( id, status );
712  }
713 
714  processQueue();
715 
716  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
717  {
718  cleanupAndDeleteTask( task );
719  }
720 
721 }
722 
723 void QgsTaskManager::layersWillBeRemoved( const QList< QgsMapLayer * > &layers )
724 {
725  mTaskMutex->lock();
726  // scan through layers to be removed
727  QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
728  layerDependencies.detach();
729  mTaskMutex->unlock();
730 
731  const auto constLayers = layers;
732  for ( QgsMapLayer *layer : constLayers )
733  {
734  // scan through tasks with layer dependencies
735  for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
736  it != layerDependencies.constEnd(); ++it )
737  {
738  if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
739  {
740  //task not dependent on this layer
741  continue;
742  }
743 
744  QgsTask *dependentTask = task( it.key() );
745  if ( dependentTask && ( dependentTask->status() != QgsTask::Complete && dependentTask->status() != QgsTask::Terminated ) )
746  {
747  // incomplete task is dependent on this layer!
748  dependentTask->cancel();
749  }
750  }
751  }
752 }
753 
754 
755 bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
756 {
757  if ( !task )
758  return false;
759 
760  long id = taskId( task );
761  if ( id < 0 )
762  return false;
763 
764  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
765 
766  task->disconnect( this );
767 
768  mTaskMutex->lock();
769  if ( mTaskDependencies.contains( id ) )
770  mTaskDependencies.remove( id );
771  mTaskMutex->unlock();
772 
773  emit taskAboutToBeDeleted( id );
774 
775  mTaskMutex->lock();
776  bool isParent = mParentTasks.contains( task );
777  mParentTasks.remove( task );
778  mSubTasks.remove( task );
779  mTasks.remove( id );
780  mLayerDependencies.remove( id );
781 
782  if ( task->status() != QgsTask::Complete && task->status() != QgsTask::Terminated )
783  {
784  if ( isParent )
785  {
786  // delete task when it's terminated
787  connect( task, &QgsTask::taskCompleted, task, &QgsTask::deleteLater );
788  connect( task, &QgsTask::taskTerminated, task, &QgsTask::deleteLater );
789  }
790  task->cancel();
791  }
792  else
793  {
794  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
795  {
796  delete runnable;
797  mTasks[ id ].runnable = nullptr;
798  }
799 
800  if ( isParent )
801  {
802  //task already finished, kill it
803  task->deleteLater();
804  }
805  }
806 
807  // at this stage (hopefully) dependent tasks have been canceled or queued
808  for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
809  {
810  if ( it.value().contains( task ) )
811  {
812  it.value().removeAll( task );
813  }
814  }
815  mTaskMutex->unlock();
816 
817  return true;
818 }
819 
820 void QgsTaskManager::processQueue()
821 {
822  int prevActiveCount = countActiveTasks();
823  mTaskMutex->lock();
824  mActiveTasks.clear();
825  for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
826  {
827  QgsTask *task = it.value().task;
828  if ( task && task->mStatus == QgsTask::Queued && dependenciesSatisfied( it.key() ) && it.value().added.testAndSetRelaxed( 0, 1 ) )
829  {
830  it.value().createRunnable();
831  QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
832  }
833 
834  if ( task && ( task->mStatus != QgsTask::Complete && task->mStatus != QgsTask::Terminated ) )
835  {
836  mActiveTasks << task;
837  }
838  }
839 
840  bool allFinished = mActiveTasks.isEmpty();
841  mTaskMutex->unlock();
842 
843  if ( allFinished )
844  {
845  emit allTasksFinished();
846  }
847 
848  int newActiveCount = countActiveTasks();
849  if ( prevActiveCount != newActiveCount )
850  {
851  emit countActiveTasksChanged( newActiveCount );
852  }
853 }
854 
855 void QgsTaskManager::cancelDependentTasks( long taskId )
856 {
857  QgsTask *canceledTask = task( taskId );
858 
859  //deep copy
860  mTaskMutex->lock();
861  QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
862  taskDependencies.detach();
863  mTaskMutex->unlock();
864 
865  QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
866  for ( ; it != taskDependencies.constEnd(); ++it )
867  {
868  if ( it.value().contains( canceledTask ) )
869  {
870  // found task with this dependency
871 
872  // cancel it - note that this will be recursive, so any tasks dependent
873  // on this one will also be canceled
874  QgsTask *dependentTask = task( it.key() );
875  if ( dependentTask )
876  dependentTask->cancel();
877  }
878  }
879 }
880 
881 QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
882  : task( task )
883  , added( 0 )
884  , priority( priority )
885 {}
886 
887 void QgsTaskManager::TaskInfo::createRunnable()
888 {
889  Q_ASSERT( !runnable );
890  runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
891 }
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 setDescription(const QString &description)
Sets the task&#39;s description.
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:79
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.
qint64 elapsedTime() const
Returns the elapsed time since the task commenced, in milliseconds.
QgsTaskManager(QObject *parent=nullptr)
Constructor for QgsTaskManager.
void setDependentLayers(const QList< QgsMapLayer *> &dependentLayers)
Sets a list of layers on which the task depends.
bool waitForFinished(int timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
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.
QString description() const
Returns the task&#39;s description.
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:91
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:1657
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
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.
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.