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