I have a Python function that I simply use as a label expression in a print layout.
Its objective is to render elements from a json object stored as a field in a vector layer.
This vector layer is coming from a rest API and is having several attributes, many if not all of them are json fields (strings).
The expression only deals with on of these fields, which contains a json object structured as:
{
"bar1": {
"foo1": "val_1_1",
"foo2": "val_1_2",
"foo3": "val_1_3",
....
"fooN": "val_1_N",
},
...
"barN": {
"foo1": "val_N_1",
"foo2": "val_N_2",
"foo3": "val_N_3",
....
"fooN": "val_N_N",
}
}
There could be one, two, or more "barX" level 1 features, their number is not fixed.
There are always 10 level 2 "fooX" features, but sometime, some of them are empty.
I need to render them all dynamically in a print layout, hence the usage of the following Python expression (this is the most advanced simplification I can make of an actually more complex situation, but it should capture its essence):
from qgis.core import *
from qgis.gui import *
import json
@qgsfunction(args='auto', group='Custom')
def get_attribute_table_bar_features(feature, parent, print_keys=True): # <-- 'print_keys' is my custom named arg
"""
Function to get a string output from a list of features
stored as 'attribute_table_bar_feature' in the attribute
table of the vector layer.
"""
# print_keys = True # <-- if that variable is hard coded
field_names = [field.name() for field in feature.fields()]
d = dict(zip(field_names, feature.attributes())) # <-- pack them all
attribute_table_bar_features = json.loads(d['attribute_table_bar_feature'])
results = ''
for k, v in "attribute_table_bar_features".items():
if print_keys: # <-- use of the custom named argument here
res_str = f"{k} {v}\n"
else:
res_str = f"{v}\n"
result += res_str
return results
I simply call it inside a label in my print layout:
[% get_attribute_table_bar_features(print_keys=True) %]
- Without the custom named arg in the function definition and without the if statement inside the function, everything goes fine, it the only prints "Hello World!" at the end of the string.
- If I set that custom named arg as a hard coded variable inside the function, and keeping the if statement, everything is also fine, but then, I am not able to use this function dynamically; i.e. I have to change the
print_keys
boolean value manually each time I need to switch. Hence, building two almost identical functions could be the way to go but it's definitely not pythonic. - And if I set up the custom named arg in the function definition as presented here, I got an error when calling it:
a) without the argument:
get_attribute_table_bar_features()
Parser Errors:
get_attribute_table_bar_features function is called with wrong number of arguments
b) with the argument:
get_attribute_table_bar_features(print_keys=True)
Eval Error: Field 'print_keys' not found
c) if arg is placed on front of get_attribute_table_bar_features(print_keys=False, feature, parent)
, it's invalid: SyntaxError: non-default argument follows default argument
Hence my question: how could I get a custom argument to work within a Python function used in the QGIS expression builder? (Notice I may need to use more than one custom argument and each may need a default value).
QGIS version: 3.22.0 Python: 3.9.5