Coverage for C:\Program Files\QGIS 3.10\apps\qgis-ltr\python\qgis\core\additions\qgsfunction.py: 12%
67 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-10 14:40 +0900
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-10 14:40 +0900
1# -*- coding: utf-8 -*-
3"""
4***************************************************************************
5 qgsfunction.py
6 ---------------------
7 Date : May 2018
8 Copyright : (C) 2018 by Denis Rouzaud
9 Email : denis@opengis.ch
10***************************************************************************
11* *
12* This program is free software; you can redistribute it and/or modify *
13* it under the terms of the GNU General Public License as published by *
14* the Free Software Foundation; either version 2 of the License, or *
15* (at your option) any later version. *
16* *
17***************************************************************************
18"""
21import inspect
22import string
23from builtins import str
24from qgis.PyQt.QtCore import QCoreApplication
25from qgis._core import QgsExpressionFunction, QgsExpression, QgsMessageLog, QgsFeatureRequest, Qgis
28def register_function(function, arg_count, group, usesgeometry=False,
29 referenced_columns=[QgsFeatureRequest.ALL_ATTRIBUTES], handlesnull=False, **kwargs):
30 """
31 Register a Python function to be used as a expression function.
33 Functions should take (values, feature, parent) as args:
35 Example:
36 def myfunc(values, feature, parent):
37 pass
39 They can also shortcut naming feature and parent args by using *args
40 if they are not needed in the function.
42 Example:
43 def myfunc(values, *args):
44 pass
46 Functions should return a value compatible with QVariant
48 Eval errors can be raised using parent.setEvalErrorString("Error message")
50 :param function:
51 :param arg_count:
52 :param group:
53 :param usesgeometry:
54 :param handlesnull: Needs to be set to True if this function does not always return NULL if any parameter is NULL. Default False.
55 :return:
56 """
58 class QgsPyExpressionFunction(QgsExpressionFunction):
60 def __init__(self, func, name, args, group, helptext='', usesGeometry=True,
61 referencedColumns=QgsFeatureRequest.ALL_ATTRIBUTES, expandargs=False, handlesNull=False):
62 QgsExpressionFunction.__init__(self, name, args, group, helptext)
63 self.function = func
64 self.expandargs = expandargs
65 self.uses_geometry = usesGeometry
66 self.referenced_columns = referencedColumns
67 self.handles_null = handlesNull
69 def func(self, values, context, parent, node):
70 feature = None
71 if context:
72 feature = context.feature()
74 try:
75 if self.expandargs:
76 values.append(feature)
77 values.append(parent)
78 if inspect.getfullargspec(self.function).args[-1] == 'context':
79 values.append(context)
80 return self.function(*values)
81 else:
82 if inspect.getfullargspec(self.function).args[-1] == 'context':
83 self.function(values, feature, parent, context)
84 return self.function(values, feature, parent)
85 except Exception as ex:
86 parent.setEvalErrorString(str(ex))
87 return None
89 def usesGeometry(self, node):
90 return self.uses_geometry
92 def referencedColumns(self, node):
93 return self.referenced_columns
95 def handlesNull(self):
96 return self.handles_null
98 helptemplate = string.Template("""<h3>$name function</h3><br>$doc""")
99 name = kwargs.get('name', function.__name__)
100 helptext = kwargs.get('helpText') or function.__doc__ or ''
101 helptext = helptext.strip()
102 expandargs = False
104 if arg_count == "auto":
105 # Work out the number of args we need.
106 # Number of function args - 2. The last two args are always feature, parent.
107 args = inspect.getfullargspec(function).args
108 number = len(args)
109 arg_count = number - 2
110 if args[-1] == 'context':
111 arg_count -= 1
112 expandargs = True
114 register = kwargs.get('register', True)
115 if register and QgsExpression.isFunctionName(name):
116 if not QgsExpression.unregisterFunction(name):
117 msgtitle = QCoreApplication.translate("UserExpressions", "User expressions")
118 msg = QCoreApplication.translate("UserExpressions",
119 "The user expression {0} already exists and could not be unregistered.").format(
120 name)
121 QgsMessageLog.logMessage(msg + "\n", msgtitle, Qgis.Warning)
122 return None
124 function.__name__ = name
125 helptext = helptemplate.safe_substitute(name=name, doc=helptext)
126 f = QgsPyExpressionFunction(function, name, arg_count, group, helptext, usesgeometry, referenced_columns,
127 expandargs, handlesnull)
129 # This doesn't really make any sense here but does when used from a decorator context
130 # so it can stay.
131 if register:
132 QgsExpression.registerFunction(f)
133 return f
136def qgsfunction(args='auto', group='custom', **kwargs):
137 """
138 Decorator function used to define a user expression function.
140 :param args: Number of parameters, set to 'auto' to accept a variable length of parameters.
141 :param group: The expression group to which this expression should be added.
142 :param \**kwargs:
143 See below
145 :Keyword Arguments:
146 * *referenced_columns* (``list``) --
147 An array of field names on which this expression works. Can be set to ``[QgsFeatureRequest.ALL_ATTRIBUTES]``. By default empty.
148 * *usesgeometry* (``bool``) --
149 Defines if this expression requires the geometry. By default False.
150 * *handlesnull* (``bool``) --
151 Defines if this expression has custom handling for NULL values. If False, the result will always be NULL as soon as any parameter is NULL. False by default.
153 Example:
154 @qgsfunction(2, 'test'):
155 def add(values, feature, parent):
156 pass
158 Will create and register a function in QgsExpression called 'add' in the
159 'test' group that takes two arguments.
161 or not using feature and parent:
163 Example:
164 @qgsfunction(2, 'test'):
165 def add(values, *args):
166 pass
167 """
169 def wrapper(func):
170 return register_function(func, args, group, **kwargs)
172 return wrapper