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