2

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)
2
  • Having the same problem. I resorted to: task.taskCompleted.connect(self.preprocessed) which I guess you also used, but I would also prefer to use the method in the Cookbook. Any luck?
    – wingnut
    Mar 26, 2021 at 5:42
  • @wingnut No luck. I think it must be a bug with Tasks and plugins.
    – Tim
    Mar 27, 2021 at 14:53

1 Answer 1

1

Elsewhere, use of a global variable for the task has been suggested. I tried it, and it worked.

globals()['task1'] = QgsTask.fromFunction(etc)

Here is the link to the suggestion from @Subgenius :

Issues with QgsTask and task manager

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.