mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-23 00:05:43 -04:00
QgsTask subclasses can now return a result (success or fail) directly from QgsTask::run. If they do so then there's no need for them to manually call completed() or stopped() to report their completion. Alternatively, tasks can also return the ResultPending value to indicate that the task is still operating and will manually report its completion by calling completed() or stopped(). This may be useful for tasks which rely on external events for completion, eg downloading a file. In this case Qt slots could be created which are connected to the download completion or termination and which call completed() or stopped() to indicate the task has finished operations.
213 lines
6.6 KiB
Python
213 lines
6.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
***************************************************************************
|
|
__init__.py
|
|
---------------------
|
|
Date : May 2014
|
|
Copyright : (C) 2014 by Nathan Woodrow
|
|
Email : woodrow dot nathan at gmail dot com
|
|
***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************
|
|
"""
|
|
from builtins import str
|
|
from builtins import object
|
|
|
|
__author__ = 'Nathan Woodrow'
|
|
__date__ = 'May 2014'
|
|
__copyright__ = '(C) 2014, Nathan Woodrow'
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
__revision__ = '$Format:%H$'
|
|
|
|
from qgis.PyQt.QtCore import QCoreApplication, NULL
|
|
|
|
import inspect
|
|
import string
|
|
from qgis._core import *
|
|
|
|
# Boolean evaluation of QgsGeometry
|
|
|
|
|
|
def _geometryNonZero(self):
|
|
return not self.isEmpty()
|
|
QgsGeometry.__nonzero__ = _geometryNonZero
|
|
QgsGeometry.__bool__ = _geometryNonZero
|
|
|
|
|
|
def register_function(function, arg_count, group, usesgeometry=False, referenced_columns=[QgsFeatureRequest.AllAttributes], **kwargs):
|
|
"""
|
|
Register a Python function to be used as a expression function.
|
|
|
|
Functions should take (values, feature, parent) as args:
|
|
|
|
Example:
|
|
def myfunc(values, feature, parent):
|
|
pass
|
|
|
|
They can also shortcut naming feature and parent args by using *args
|
|
if they are not needed in the function.
|
|
|
|
Example:
|
|
def myfunc(values, *args):
|
|
pass
|
|
|
|
Functions should return a value compatible with QVariant
|
|
|
|
Eval errors can be raised using parent.setEvalErrorString("Error message")
|
|
|
|
:param function:
|
|
:param arg_count:
|
|
:param group:
|
|
:param usesgeometry:
|
|
:return:
|
|
"""
|
|
class QgsExpressionFunction(QgsExpression.Function):
|
|
|
|
def __init__(self, func, name, args, group, helptext='', usesGeometry=True, referencedColumns=QgsFeatureRequest.AllAttributes, expandargs=False):
|
|
QgsExpression.Function.__init__(self, name, args, group, helptext)
|
|
self.function = func
|
|
self.expandargs = expandargs
|
|
self.uses_geometry = usesGeometry
|
|
self.referenced_columns = referencedColumns
|
|
|
|
def func(self, values, context, parent):
|
|
feature = None
|
|
if context:
|
|
feature = context.feature()
|
|
try:
|
|
if self.expandargs:
|
|
values.append(feature)
|
|
values.append(parent)
|
|
return self.function(*values)
|
|
else:
|
|
return self.function(values, feature, parent)
|
|
except Exception as ex:
|
|
parent.setEvalErrorString(str(ex))
|
|
return None
|
|
|
|
def usesGeometry(self, node):
|
|
return self.uses_geometry
|
|
|
|
def referencedColumns(self, node):
|
|
return self.referenced_columns
|
|
|
|
helptemplate = string.Template("""<h3>$name function</h3><br>$doc""")
|
|
name = kwargs.get('name', function.__name__)
|
|
helptext = function.__doc__ or ''
|
|
helptext = helptext.strip()
|
|
expandargs = False
|
|
|
|
if arg_count == "auto":
|
|
# Work out the number of args we need.
|
|
# Number of function args - 2. The last two args are always feature, parent.
|
|
args = inspect.getargspec(function).args
|
|
number = len(args)
|
|
arg_count = number - 2
|
|
expandargs = True
|
|
|
|
register = kwargs.get('register', True)
|
|
if register and QgsExpression.isFunctionName(name):
|
|
if not QgsExpression.unregisterFunction(name):
|
|
msgtitle = QCoreApplication.translate("UserExpressions", "User expressions")
|
|
msg = QCoreApplication.translate("UserExpressions", "The user expression {0} already exists and could not be unregistered.").format(name)
|
|
QgsMessageLog.logMessage(msg + "\n", msgtitle, QgsMessageLog.WARNING)
|
|
return None
|
|
|
|
function.__name__ = name
|
|
helptext = helptemplate.safe_substitute(name=name, doc=helptext)
|
|
f = QgsExpressionFunction(function, name, arg_count, group, helptext, usesgeometry, referenced_columns, expandargs)
|
|
|
|
# This doesn't really make any sense here but does when used from a decorator context
|
|
# so it can stay.
|
|
if register:
|
|
QgsExpression.registerFunction(f)
|
|
return f
|
|
|
|
|
|
def qgsfunction(args='auto', group='custom', **kwargs):
|
|
"""
|
|
Decorator function used to define a user expression function.
|
|
|
|
Example:
|
|
@qgsfunction(2, 'test'):
|
|
def add(values, feature, parent):
|
|
pass
|
|
|
|
Will create and register a function in QgsExpression called 'add' in the
|
|
'test' group that takes two arguments.
|
|
|
|
or not using feature and parent:
|
|
|
|
Example:
|
|
@qgsfunction(2, 'test'):
|
|
def add(values, *args):
|
|
pass
|
|
"""
|
|
|
|
def wrapper(func):
|
|
return register_function(func, args, group, **kwargs)
|
|
return wrapper
|
|
|
|
|
|
class QgsEditError(Exception):
|
|
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __str__(self):
|
|
return repr(self.value)
|
|
|
|
# Define a `with edit(layer)` statement
|
|
|
|
|
|
class edit(object):
|
|
|
|
def __init__(self, layer):
|
|
self.layer = layer
|
|
|
|
def __enter__(self):
|
|
assert self.layer.startEditing()
|
|
return self.layer
|
|
|
|
def __exit__(self, ex_type, ex_value, traceback):
|
|
if ex_type is None:
|
|
if not self.layer.commitChanges():
|
|
raise QgsEditError(self.layer.commitErrors())
|
|
return True
|
|
else:
|
|
self.layer.rollBack()
|
|
return False
|
|
|
|
|
|
class QgsTaskWrapper(QgsTask):
|
|
|
|
def __init__(self, description, function, *args, **kwargs):
|
|
QgsTask.__init__(self, description)
|
|
self.args = args
|
|
self.kwargs = kwargs
|
|
self.function = function
|
|
self.result = None
|
|
self.exception = None
|
|
|
|
def run(self):
|
|
try:
|
|
self.result = self.function(self, *self.args, **self.kwargs)
|
|
except Exception as ex:
|
|
# report error
|
|
self.exception = ex
|
|
return QgsTask.ResultFail
|
|
|
|
return QgsTask.ResultSuccess
|
|
|
|
|
|
def fromFunction(cls, description, function, *args, **kwargs):
|
|
return QgsTaskWrapper(description, function, *args, **kwargs)
|
|
|
|
QgsTask.fromFunction = classmethod(fromFunction)
|