I'm trying to create a task to process some geometry intersections in a QGIS plugin I've created. I'm following 15.2.2 Task from function from the PyQgis Developer cook book exactly as its shown. I can get the tasks to run as expected but the completed function is never called unless an exception is raised from the doSomething function(see code from the cook book below).
When I run the cook book code from the Python Console in Qgis it works properly, showing the Task Name and iterations in the Log Messages after the task has finished. I've tried adding self to the start of each task and including them in the class that I'm calling the tasks from as suggested in this post.
I have used my own task classe before in the same plugin which have worked well but I'm at the point were I've invested a fair bit of time trying to get this way to work that I want to see it through. Is there any way I can get the specified function to run once a task has successfully completed within my plugin?
Edit: I actually looked through the code for my custom task class and I'm using signals to pass information back to the main thread as the finished method in the class was not being called upon task completion. The workaround is ok though it would be good to understand why it is not working as intended.
import random
from time import sleep
MESSAGE_CATEGORY = 'TaskFromFunction'
def doSomething(task, wait_time):
"""
Raises an exception to abort the task.
Returns a result if success.
The result will be passed, together with the exception (None in
the case of success), to the on_finished method.
If there is an exception, there will be no result.
"""
QgsMessageLog.logMessage('Started task {}'.format(task.description()),
MESSAGE_CATEGORY, Qgis.Info)
wait_time = wait_time / 100
total = 0
iterations = 0
for i in range(100):
sleep(wait_time)
# use task.setProgress to report progress
task.setProgress(i)
arandominteger = random.randint(0, 500)
total += arandominteger
iterations += 1
# check task.isCanceled() to handle cancellation
if task.isCanceled():
stopped(task)
return None
# raise an exception to abort the task
if arandominteger == 42:
raise Exception('bad value!')
return {'total': total, 'iterations': iterations,
'task': task.description()}
def stopped(task):
QgsMessageLog.logMessage(
'Task "{name}" was canceled'.format(
name=task.description()),
MESSAGE_CATEGORY, Qgis.Info)
def completed(exception, result=None):
"""This is called when doSomething is finished.
Exception is not None if doSomething raises an exception.
result is the return value of doSomething."""
if exception is None:
if result is None:
QgsMessageLog.logMessage(
'Completed with no exception and no result '\
'(probably manually canceled by the user)',
MESSAGE_CATEGORY, Qgis.Warning)
else:
QgsMessageLog.logMessage(
'Task {name} completed\n'
'Total: {total} ( with {iterations} '
'iterations)'.format(
name=result['task'],
total=result['total'],
iterations=result['iterations']),
MESSAGE_CATEGORY, Qgis.Info)
else:
QgsMessageLog.logMessage("Exception: {}".format(exception),
MESSAGE_CATEGORY, Qgis.Critical)
raise exception
# Create a few tasks
task1 = QgsTask.fromFunction('Waste cpu 1', doSomething,
on_finished=completed, wait_time=4)
task2 = QgsTask.fromFunction('Waste cpu 2', doSomething,
on_finished=completed, wait_time=3)
QgsApplication.taskManager().addTask(task1)
QgsApplication.taskManager().addTask(task2)