mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Now all evaluate/prepare/etc methods must be called using QgsExpressionContexts Also remove most remaining traces of special variables. This brings some user facing changes, such that existing expressions may need to be updated if they used these old special variables (eg $scale, $feature). These changes are noted in doc/qgis3_user_changes.dox so that we can include them in the release notes.
176 lines
5.6 KiB
Python
176 lines
5.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. *
|
|
* *
|
|
***************************************************************************
|
|
"""
|
|
|
|
__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, usesgeometry, referencedColumns)
|
|
self.function = func
|
|
self.expandargs = expandargs
|
|
|
|
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
|
|
|
|
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:
|
|
|
|
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
|