QGIS API Documentation  3.0.2-Girona (307d082)
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  mTotalProgress = progress;
216  emit progressChanged( mTotalProgress );
217 }
218 
219 void QgsTask::completed()
220 {
221  mStatus = Complete;
222  processSubTasksForCompletion();
223 }
224 
225 void QgsTask::processSubTasksForCompletion()
226 {
227  bool subTasksCompleted = true;
228  Q_FOREACH ( const SubTask &subTask, mSubTasks )
229  {
230  if ( subTask.task->status() != Complete )
231  {
232  subTasksCompleted = false;
233  break;
234  }
235  }
236 
237  if ( mStatus == Complete && subTasksCompleted )
238  {
239  mOverallStatus = Complete;
240 
241  setProgress( 100.0 );
242  emit statusChanged( Complete );
243  emit taskCompleted();
244  mTaskFinished.wakeAll();
245  mNotFinishedMutex.unlock();
246  mTaskFinished.wakeAll();
247  }
248  else if ( mStatus == Complete )
249  {
250  // defer completion until all subtasks are complete
251  mOverallStatus = Running;
252  }
253 }
254 
255 void QgsTask::processSubTasksForTermination()
256 {
257  bool subTasksTerminated = true;
258  Q_FOREACH ( const SubTask &subTask, mSubTasks )
259  {
260  if ( subTask.task->status() != Terminated && subTask.task->status() != Complete )
261  {
262  subTasksTerminated = false;
263  break;
264  }
265  }
266 
267  if ( mStatus == Terminated && subTasksTerminated && mOverallStatus != Terminated )
268  {
269  mOverallStatus = Terminated;
270 
271  emit statusChanged( Terminated );
272  emit taskTerminated();
273  mTaskFinished.wakeAll();
274  mNotFinishedMutex.unlock();
275  mTaskFinished.wakeAll();
276  }
277  else if ( mStatus == Terminated && !subTasksTerminated )
278  {
279  // defer termination until all subtasks are terminated (or complete)
280  mOverallStatus = Running;
281  }
282 }
283 
284 void QgsTask::processSubTasksForHold()
285 {
286  bool subTasksRunning = false;
287  Q_FOREACH ( const SubTask &subTask, mSubTasks )
288  {
289  if ( subTask.task->status() == Running )
290  {
291  subTasksRunning = true;
292  break;
293  }
294  }
295 
296  if ( mStatus == OnHold && !subTasksRunning && mOverallStatus != OnHold )
297  {
298  mOverallStatus = OnHold;
299  emit statusChanged( OnHold );
300  }
301  else if ( mStatus == OnHold && subTasksRunning )
302  {
303  // defer hold until all subtasks finish running
304  mOverallStatus = Running;
305  }
306 }
307 
308 void QgsTask::terminated()
309 {
310  mStatus = Terminated;
311  processSubTasksForTermination();
312 }
313 
314 
316 
317 class QgsTaskRunnableWrapper : public QRunnable
318 {
319  public:
320 
321  explicit QgsTaskRunnableWrapper( QgsTask *task )
322  : mTask( task )
323  {
324  setAutoDelete( true );
325  }
326 
327  void run() override
328  {
329  Q_ASSERT( mTask );
330  mTask->start();
331  }
332 
333  private:
334 
335  QgsTask *mTask = nullptr;
336 
337 };
338 
340 
341 
342 
343 //
344 // QgsTaskManager
345 //
346 
348  : QObject( parent )
349  , mTaskMutex( new QMutex( QMutex::Recursive ) )
350 {
351  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList< QgsMapLayer * >& ) > ( &QgsProject::layersWillBeRemoved ),
352  this, &QgsTaskManager::layersWillBeRemoved );
353 }
354 
356 {
357  //first tell all tasks to cancel
358  cancelAll();
359 
360  //then clean them up, including waiting for them to terminate
361  mTaskMutex->lock();
362  QMap< long, TaskInfo > tasks = mTasks;
363  mTasks.detach();
364  mTaskMutex->unlock();
365  QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
366  for ( ; it != tasks.constEnd(); ++it )
367  {
368  cleanupAndDeleteTask( it.value().task );
369  }
370 
371  delete mTaskMutex;
372 }
373 
374 long QgsTaskManager::addTask( QgsTask *task, int priority )
375 {
376  return addTaskPrivate( task, QgsTaskList(), false, priority );
377 }
378 
379 long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
380 {
381  return addTaskPrivate( definition.task,
382  definition.dependentTasks,
383  false,
384  priority );
385 }
386 
387 
388 long QgsTaskManager::addTaskPrivate( QgsTask *task, QgsTaskList dependencies, bool isSubTask, int priority )
389 {
390  long taskId = mNextTaskId++;
391 
392  mTaskMutex->lock();
393  mTasks.insert( taskId, TaskInfo( task, priority ) );
394  if ( isSubTask )
395  {
396  mSubTasks << task;
397  }
398  else
399  {
400  mParentTasks << task;
401  }
402  if ( !task->dependentLayers().isEmpty() )
403  mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->dependentLayers() ) );
404  mTaskMutex->unlock();
405 
406  connect( task, &QgsTask::statusChanged, this, &QgsTaskManager::taskStatusChanged );
407  if ( !isSubTask )
408  {
409  connect( task, &QgsTask::progressChanged, this, &QgsTaskManager::taskProgressChanged );
410  }
411 
412  // add all subtasks, must be done before dependency resolution
413  Q_FOREACH ( const QgsTask::SubTask &subTask, task->mSubTasks )
414  {
415  switch ( subTask.dependency )
416  {
418  dependencies << subTask.task;
419  break;
420 
422  //nothing
423  break;
424  }
425  //recursively add sub tasks
426  addTaskPrivate( subTask.task, subTask.dependencies, true, priority );
427  }
428 
429  if ( !dependencies.isEmpty() )
430  {
431  mTaskDependencies.insert( taskId, dependencies );
432  }
433 
434  if ( hasCircularDependencies( taskId ) )
435  {
436  task->cancel();
437  }
438 
439  if ( !isSubTask )
440  {
441  emit taskAdded( taskId );
442  processQueue();
443  }
444 
445  return taskId;
446 }
447 
448 QgsTask *QgsTaskManager::task( long id ) const
449 {
450  QMutexLocker ml( mTaskMutex );
451  QgsTask *t = nullptr;
452  if ( mTasks.contains( id ) )
453  t = mTasks.value( id ).task;
454  return t;
455 }
456 
457 QList<QgsTask *> QgsTaskManager::tasks() const
458 {
459  QMutexLocker ml( mTaskMutex );
460  return mParentTasks.toList();
461 }
462 
464 {
465  QMutexLocker ml( mTaskMutex );
466  return mParentTasks.count();
467 }
468 
469 long QgsTaskManager::taskId( QgsTask *task ) const
470 {
471  if ( !task )
472  return -1;
473 
474  QMutexLocker ml( mTaskMutex );
475  QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
476  for ( ; it != mTasks.constEnd(); ++it )
477  {
478  if ( it.value().task == task )
479  {
480  return it.key();
481  }
482  }
483  return -1;
484 }
485 
487 {
488  mTaskMutex->lock();
489  QSet< QgsTask * > parents = mParentTasks;
490  parents.detach();
491  mTaskMutex->unlock();
492 
493  Q_FOREACH ( QgsTask *task, parents )
494  {
495  task->cancel();
496  }
497 }
498 
500 {
501  mTaskMutex->lock();
502  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
503  dependencies.detach();
504  mTaskMutex->unlock();
505 
506  if ( !dependencies.contains( taskId ) )
507  return true;
508 
509  Q_FOREACH ( QgsTask *task, dependencies.value( taskId ) )
510  {
511  if ( task->status() != QgsTask::Complete )
512  return false;
513  }
514 
515  return true;
516 }
517 
518 QSet<long> QgsTaskManager::dependencies( long taskId ) const
519 {
520  QSet<long> results;
521  if ( resolveDependencies( taskId, taskId, results ) )
522  return results;
523  else
524  return QSet<long>();
525 }
526 
527 bool QgsTaskManager::resolveDependencies( long firstTaskId, long currentTaskId, QSet<long> &results ) const
528 {
529  mTaskMutex->lock();
530  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
531  dependencies.detach();
532  mTaskMutex->unlock();
533 
534  if ( !dependencies.contains( currentTaskId ) )
535  return true;
536 
537  Q_FOREACH ( QgsTask *task, dependencies.value( currentTaskId ) )
538  {
539  long dependentTaskId = taskId( task );
540  if ( dependentTaskId >= 0 )
541  {
542  if ( dependentTaskId == firstTaskId )
543  // circular
544  return false;
545 
546  //add task as dependent
547  results.insert( dependentTaskId );
548  //plus all its other dependencies
549  QSet< long > newTaskDeps;
550  if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
551  return false;
552 
553  if ( newTaskDeps.contains( firstTaskId ) )
554  {
555  // circular
556  return false;
557  }
558 
559  results.unite( newTaskDeps );
560  }
561  }
562 
563  return true;
564 }
565 
566 bool QgsTaskManager::hasCircularDependencies( long taskId ) const
567 {
568  QSet< long > d;
569  return !resolveDependencies( taskId, taskId, d );
570 }
571 
572 QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
573 {
574  QMutexLocker ml( mTaskMutex );
575  return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
576 }
577 
578 QList<QgsTask *> QgsTaskManager::tasksDependentOnLayer( QgsMapLayer *layer ) const
579 {
580  QMutexLocker ml( mTaskMutex );
581  QList< QgsTask * > tasks;
582  QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
583  for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
584  {
585  if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
586  {
587  QgsTask *layerTask = task( layerIt.key() );
588  if ( layerTask )
589  tasks << layerTask;
590  }
591  }
592  return tasks;
593 }
594 
595 QList<QgsTask *> QgsTaskManager::activeTasks() const
596 {
597  QMutexLocker ml( mTaskMutex );
598  QSet< QgsTask * > activeTasks = mActiveTasks;
599  activeTasks.intersect( mParentTasks );
600  return activeTasks.toList();
601 }
602 
604 {
605  QMutexLocker ml( mTaskMutex );
606  QSet< QgsTask * > tasks = mActiveTasks;
607  return tasks.intersect( mParentTasks ).count();
608 }
609 
611 {
612  if ( task )
613  emit taskTriggered( task );
614 }
615 
616 void QgsTaskManager::taskProgressChanged( double progress )
617 {
618  QgsTask *task = qobject_cast< QgsTask * >( sender() );
619 
620  //find ID of task
621  long id = taskId( task );
622  if ( id < 0 )
623  return;
624 
625  emit progressChanged( id, progress );
626 
627  if ( countActiveTasks() == 1 )
628  {
629  emit finalTaskProgressChanged( progress );
630  }
631 }
632 
633 void QgsTaskManager::taskStatusChanged( int status )
634 {
635  QgsTask *task = qobject_cast< QgsTask * >( sender() );
636 
637  //find ID of task
638  long id = taskId( task );
639  if ( id < 0 )
640  return;
641 
642 
643 #if QT_VERSION >= 0x050500
644  mTaskMutex->lock();
645  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
646  mTaskMutex->unlock();
647  if ( runnable )
648  QThreadPool::globalInstance()->cancel( runnable );
649 #endif
650 
651  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
652  {
653  bool result = status == QgsTask::Complete;
654  task->finished( result );
655  }
656 
657  if ( status == QgsTask::Terminated )
658  {
659  //recursively cancel dependent tasks
660  cancelDependentTasks( id );
661  }
662 
663  mTaskMutex->lock();
664  bool isParent = mParentTasks.contains( task );
665  mTaskMutex->unlock();
666  if ( isParent )
667  {
668  // don't emit status changed for subtasks
669  emit statusChanged( id, status );
670  }
671 
672  processQueue();
673 
674  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
675  {
676  cleanupAndDeleteTask( task );
677  }
678 
679 }
680 
681 void QgsTaskManager::layersWillBeRemoved( const QList< QgsMapLayer * > &layers )
682 {
683  mTaskMutex->lock();
684  // scan through layers to be removed
685  QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
686  layerDependencies.detach();
687  mTaskMutex->unlock();
688 
689  Q_FOREACH ( QgsMapLayer *layer, layers )
690  {
691  // scan through tasks with layer dependencies
692  for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
693  it != layerDependencies.constEnd(); ++it )
694  {
695  if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
696  {
697  //task not dependent on this layer
698  continue;
699  }
700 
701  QgsTask *dependentTask = task( it.key() );
702  if ( dependentTask && ( dependentTask->status() != QgsTask::Complete && dependentTask->status() != QgsTask::Terminated ) )
703  {
704  // incomplete task is dependent on this layer!
705  dependentTask->cancel();
706  }
707  }
708  }
709 }
710 
711 
712 bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
713 {
714  if ( !task )
715  return false;
716 
717  long id = taskId( task );
718  if ( id < 0 )
719  return false;
720 
721  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
722 
723  task->disconnect( this );
724 
725  mTaskMutex->lock();
726  if ( mTaskDependencies.contains( id ) )
727  mTaskDependencies.remove( id );
728  mTaskMutex->unlock();
729 
730  emit taskAboutToBeDeleted( id );
731 
732  mTaskMutex->lock();
733  bool isParent = mParentTasks.contains( task );
734  mParentTasks.remove( task );
735  mSubTasks.remove( task );
736  mTasks.remove( id );
737  mLayerDependencies.remove( id );
738 
739  if ( task->status() != QgsTask::Complete && task->status() != QgsTask::Terminated )
740  {
741  if ( isParent )
742  {
743  // delete task when it's terminated
744  connect( task, &QgsTask::taskCompleted, task, &QgsTask::deleteLater );
745  connect( task, &QgsTask::taskTerminated, task, &QgsTask::deleteLater );
746  }
747  task->cancel();
748  }
749  else
750  {
751 #if QT_VERSION >= 0x050500
752  if ( runnable )
753  QThreadPool::globalInstance()->cancel( runnable );
754 #endif
755  if ( isParent )
756  {
757  //task already finished, kill it
758  task->deleteLater();
759  }
760  }
761 
762  // at this stage (hopefully) dependent tasks have been canceled or queued
763  for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
764  {
765  if ( it.value().contains( task ) )
766  {
767  it.value().removeAll( task );
768  }
769  }
770  mTaskMutex->unlock();
771 
772  return true;
773 }
774 
775 void QgsTaskManager::processQueue()
776 {
777  int prevActiveCount = countActiveTasks();
778  mTaskMutex->lock();
779  mActiveTasks.clear();
780  for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
781  {
782  QgsTask *task = it.value().task;
783  if ( task && task->mStatus == QgsTask::Queued && dependenciesSatisfied( it.key() ) && it.value().added.testAndSetRelaxed( 0, 1 ) )
784  {
785  it.value().createRunnable();
786  QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
787  }
788 
789  if ( task && ( task->mStatus != QgsTask::Complete && task->mStatus != QgsTask::Terminated ) )
790  {
791  mActiveTasks << task;
792  }
793  }
794 
795  bool allFinished = mActiveTasks.isEmpty();
796  mTaskMutex->unlock();
797 
798  if ( allFinished )
799  {
800  emit allTasksFinished();
801  }
802 
803  int newActiveCount = countActiveTasks();
804  if ( prevActiveCount != newActiveCount )
805  {
806  emit countActiveTasksChanged( newActiveCount );
807  }
808 }
809 
810 void QgsTaskManager::cancelDependentTasks( long taskId )
811 {
812  QgsTask *canceledTask = task( taskId );
813 
814  //deep copy
815  mTaskMutex->lock();
816  QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
817  taskDependencies.detach();
818  mTaskMutex->unlock();
819 
820  QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
821  for ( ; it != taskDependencies.constEnd(); ++it )
822  {
823  if ( it.value().contains( canceledTask ) )
824  {
825  // found task with this dependency
826 
827  // cancel it - note that this will be recursive, so any tasks dependent
828  // on this one will also be canceled
829  QgsTask *dependentTask = task( it.key() );
830  if ( dependentTask )
831  dependentTask->cancel();
832  }
833  }
834 }
835 
836 QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
837  : task( task )
838  , added( 0 )
839  , priority( priority )
840 {}
841 
842 void QgsTaskManager::TaskInfo::createRunnable()
843 {
844  Q_ASSERT( !runnable );
845  runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
846 }
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:56
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:82
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:1356
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
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.