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