Closed gavg712 closed 2 years ago
Guys, I think you have already seen this on twitter. But I wanted to ask you directly, do you think it is a good idea to include it in the plugin?
It sounds useful to me. I can definitely see use cases where you'd need to vary the behaviour depending on the qgis version its being running from!
I'm wondering though if we should take a step back and instead allow evaluation of ANY type of QGIS expression for storage in an r variable. Eg.
##my_r_variable=expression 1+2+3
##qgis_version=expression @qgis_version_no
##some_other_var=expression layer_property('lines layer', ... )
It could address the same use case, but in a much more powerful way..
That might be even better and it covers much wider use cases!
@nyalldawson If you can point out how expressions can be evaluated in the context of the current project, I will try to give it a shot.
I'm wondering though if we should take a step back and instead allow evaluation of ANY type of QGIS expression for storage in an r variable. Eg.
I read it some where. And of course I agree! I will re-think in this approach and give it a shot too.
@JanCaha This is not the nicer example, just as starting point, to evaluate a expression using a context stack of global and project context:
# Build a Context Stack
context = QgsExpressionContext()
context.appendScopes([
QgsExpressionContextUtils.globalScope(),
QgsExpressionContextUtils.projectScope(QgsProject.instance())
])
# to evaluate any expression from Project or Global scopes, except 'layers' and ''
exp = QgsExpression("@project_home")
print(exp.evaluate(context))
exp = QgsExpression("@qgis_version")
print(exp.evaluate(context))
exp = QgsExpression("@user_full_name")
print(exp.evaluate(context))
try:
exp = QgsExpression("@layers")
print(exp.evaluate(context))
except TypeError:
pass
else:
print("expression '@layers' does not work!")
try:
exp = QgsExpression("1 + 1")
print(exp.evaluate(context))
except TypeError:
pass
else:
print("expression '1 + 1' does not work in this context!")
Actually instead of this:
context = QgsExpressionContext()
context.appendScopes([
QgsExpressionContextUtils.globalScope(),
QgsExpressionContextUtils.projectScope(QgsProject.instance())
])
it's better to take the expression context directly from the processing context available to algorithms:
# somewhere in the algorithm's "processAlgorithm" implementation, where "context" is the QgsProcessingContext passed to that function
exp_context = context.createExpressionContext(parameters, context)
That way things will all work just as expected, regardless of where the algorithm is run (through a model, via qgis_process, etc)
try:
exp = QgsExpression("1 + 1")
print(exp.evaluate(context))
except TypeError:
pass
else:
print("expression '1 + 1' does not work in this context!")
I'm curious where this example is taken from. I can't see any way a TypeError would be raised here. The correct approach should be:
exp = QgsExpression("1 + 1")
# if the expression will be evaluated multiple times, e.g. if it's something like "area($geometry)" and you'll be evaluating that same expression against multiple features:
if not exp.prepare(exp_context):
# will happen for a malformed expression , e,g "(1 + 2 ) * ( 3"
print(" bad expression: {}".format(exp.parserErrorString() )
res = exp.evaluate(exp_context)
if exp.hasEvalError():
# will happen when an otherwise valid expression cannot be calculated, e.g. something like
# "centroid('i am not a geometry')"
print("error evaluating expression: {}".format( exp.evalErrorString() )
I'm curious where this example is taken from. I can't see any way a TypeError would be raised here. The correct approach should be:
My mistake. I was typing some things in the python console. However, I caught your approach
@nyalldawson thanks a lot.
Ok, the quick demo seems to be working. Now comes the hard part - I have to prepare for all possible return types from the Expression and process them as needed.
@JanCaha I'd say the priorities would be:
The others types are significantly less important (at least in the context of single values calculated at the start of a script), and are much rarer (eg interval types, dicts)
The first try is available in my fork. Seems to be working rather fine. I need to find some time to write unit tests to be sure ;-)
If you want to take a look, this commit handles most of the important stuff.
@JanCaha does that expose them as expression inputs to the user though? (I don't think we want that, right?)
It does expose them. To be honest I did not really think about that...I was focused on the functionality. You are right that it is probably not desirable.
It seems to be solved in #102. Then I will close it!
This is my official request about global and project variables as objects available in R. I am not sure how important it would be to have these variables. But here are some ideas I have been working on.
Taking a bit from #37 and #31 and my own needs, I have thought that a new parser could be generated for a header line that would allow to have the QGIS variables we need. Use cases:
My progress allows me to have most variables available, except for the project variables
"layers"
and"_project_transform_context"
Here an example:
And the R session behavior
@nyalldawson, @JanCaha, Guys, I think you have already seen this on twitter. But I wanted to ask you directly, do you think it is a good idea to include it in the plugin?