2012-10-04 19:33:47 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
***************************************************************************
|
|
|
|
ScriptAlgorithm.py
|
|
|
|
---------------------
|
|
|
|
Date : August 2012
|
|
|
|
Copyright : (C) 2012 by Victor Olaya
|
|
|
|
Email : volayaf 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. *
|
|
|
|
* *
|
|
|
|
***************************************************************************
|
|
|
|
"""
|
2016-09-21 18:24:26 +02:00
|
|
|
from builtins import str
|
2013-10-01 20:52:22 +03:00
|
|
|
|
2012-10-04 19:33:47 +02:00
|
|
|
__author__ = 'Victor Olaya'
|
|
|
|
__date__ = 'August 2012'
|
|
|
|
__copyright__ = '(C) 2012, Victor Olaya'
|
2013-10-01 20:52:22 +03:00
|
|
|
|
2012-10-04 19:33:47 +02:00
|
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
2013-10-01 20:52:22 +03:00
|
|
|
|
2012-10-04 19:33:47 +02:00
|
|
|
__revision__ = '$Format:%H$'
|
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
import os
|
2016-04-07 12:02:50 +02:00
|
|
|
import re
|
2016-05-31 17:23:28 +02:00
|
|
|
import json
|
2017-01-09 12:34:46 +10:00
|
|
|
from qgis.core import (QgsExpressionContextUtils,
|
|
|
|
QgsExpressionContext,
|
|
|
|
QgsProject,
|
|
|
|
QgsApplication)
|
|
|
|
|
2013-08-12 20:44:27 +02:00
|
|
|
from processing.core.GeoAlgorithm import GeoAlgorithm
|
2014-04-19 00:48:56 +02:00
|
|
|
from processing.gui.Help2Html import getHtmlFromHelpFile
|
2016-09-08 10:30:30 +02:00
|
|
|
from processing.core.parameters import getParameterFromString
|
2014-07-14 14:19:09 +02:00
|
|
|
from processing.core.outputs import getOutputFromString
|
2016-04-26 12:47:29 +02:00
|
|
|
from processing.core.ProcessingLog import ProcessingLog
|
2016-08-23 19:33:42 +03:00
|
|
|
from processing.script.WrongScriptException import WrongScriptException
|
|
|
|
|
2015-05-18 19:51:26 +03:00
|
|
|
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
|
|
|
|
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
class ScriptAlgorithm(GeoAlgorithm):
|
|
|
|
|
|
|
|
def __init__(self, descriptionFile, script=None):
|
2013-10-01 20:52:22 +03:00
|
|
|
"""The script parameter can be used to directly pass the code
|
|
|
|
of the script without a file.
|
|
|
|
|
|
|
|
This is to be used from the script edition dialog, but should
|
|
|
|
not be used in other cases.
|
|
|
|
"""
|
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
GeoAlgorithm.__init__(self)
|
2017-01-09 12:34:46 +10:00
|
|
|
self._icon = QgsApplication.getThemeIcon("/processingScript.svg")
|
2017-03-29 12:04:09 +10:00
|
|
|
self._group = ''
|
2016-01-20 17:00:11 +01:00
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
self.script = script
|
2014-07-10 17:24:07 +02:00
|
|
|
self.allowEdit = True
|
2016-01-14 11:41:41 +01:00
|
|
|
self.noCRSWarning = False
|
2012-09-15 18:25:25 +03:00
|
|
|
self.descriptionFile = descriptionFile
|
|
|
|
if script is not None:
|
|
|
|
self.defineCharacteristicsFromScript()
|
|
|
|
if descriptionFile is not None:
|
|
|
|
self.defineCharacteristicsFromFile()
|
|
|
|
|
|
|
|
def getCopy(self):
|
|
|
|
newone = ScriptAlgorithm(self.descriptionFile)
|
|
|
|
newone.provider = self.provider
|
|
|
|
return newone
|
|
|
|
|
2017-01-09 09:18:25 +10:00
|
|
|
def icon(self):
|
2014-07-10 17:24:07 +02:00
|
|
|
return self._icon
|
2012-09-15 18:25:25 +03:00
|
|
|
|
2017-03-29 12:04:09 +10:00
|
|
|
def group(self):
|
|
|
|
return self._group
|
|
|
|
|
2017-03-29 10:42:42 +10:00
|
|
|
def svgIconPath(self):
|
|
|
|
return QgsApplication.iconPath("processingScript.svg")
|
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
def defineCharacteristicsFromFile(self):
|
2016-05-18 13:42:32 +02:00
|
|
|
self.error = None
|
2013-10-01 20:52:22 +03:00
|
|
|
self.script = ''
|
2012-09-15 18:25:25 +03:00
|
|
|
filename = os.path.basename(self.descriptionFile)
|
2013-10-01 20:52:22 +03:00
|
|
|
self.name = filename[:filename.rfind('.')].replace('_', ' ')
|
2017-03-29 12:04:09 +10:00
|
|
|
self._group = self.tr('User scripts', 'ScriptAlgorithm')
|
2016-11-07 10:35:15 +10:00
|
|
|
with open(self.descriptionFile) as lines:
|
2012-09-15 18:25:25 +03:00
|
|
|
line = lines.readline()
|
2016-11-07 10:35:15 +10:00
|
|
|
while line != '':
|
|
|
|
if line.startswith('##'):
|
|
|
|
try:
|
|
|
|
self.processParameterLine(line.strip('\n'))
|
|
|
|
except:
|
|
|
|
self.error = self.tr('This script has a syntax errors.\n'
|
2017-03-04 16:23:36 +01:00
|
|
|
'Problem with line: {0}', 'ScriptAlgorithm').format(line)
|
2016-11-07 10:35:15 +10:00
|
|
|
self.script += line
|
|
|
|
line = lines.readline()
|
2017-03-29 12:04:09 +10:00
|
|
|
if self._group == self.tr('[Test scripts]', 'ScriptAlgorithm'):
|
2013-09-21 12:44:51 +02:00
|
|
|
self.showInModeler = False
|
|
|
|
self.showInToolbox = False
|
2012-09-15 18:25:25 +03:00
|
|
|
|
|
|
|
def defineCharacteristicsFromScript(self):
|
2013-10-01 20:52:22 +03:00
|
|
|
lines = self.script.split('\n')
|
2015-07-26 03:48:27 +02:00
|
|
|
self.name, self.i18n_name = self.trAlgorithm('[Unnamed algorithm]', 'ScriptAlgorithm')
|
2017-03-29 12:04:09 +10:00
|
|
|
self._group = self.tr('User scripts', 'ScriptAlgorithm')
|
2012-09-15 18:25:25 +03:00
|
|
|
for line in lines:
|
2013-10-01 20:52:22 +03:00
|
|
|
if line.startswith('##'):
|
2012-09-15 18:25:25 +03:00
|
|
|
try:
|
2013-10-01 20:52:22 +03:00
|
|
|
self.processParameterLine(line.strip('\n'))
|
2012-09-15 18:25:25 +03:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2016-05-18 13:42:32 +02:00
|
|
|
def checkBeforeOpeningParametersDialog(self):
|
|
|
|
return self.error
|
|
|
|
|
2016-01-14 11:41:41 +01:00
|
|
|
def checkInputCRS(self):
|
|
|
|
if self.noCRSWarning:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return GeoAlgorithm.checkInputCRS(self)
|
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
def createDescriptiveName(self, s):
|
2013-10-01 20:52:22 +03:00
|
|
|
return s.replace('_', ' ')
|
2012-09-15 18:25:25 +03:00
|
|
|
|
2013-10-01 20:52:22 +03:00
|
|
|
def processParameterLine(self, line):
|
2012-09-15 18:25:25 +03:00
|
|
|
param = None
|
2013-10-01 20:52:22 +03:00
|
|
|
line = line.replace('#', '')
|
|
|
|
|
2014-06-05 13:29:45 +02:00
|
|
|
if line == "nomodeler":
|
|
|
|
self.showInModeler = False
|
2014-06-10 22:37:43 +02:00
|
|
|
return
|
2016-01-14 11:41:41 +01:00
|
|
|
if line == "nocrswarning":
|
|
|
|
self.noCRSWarning = True
|
|
|
|
return
|
2013-11-06 17:52:43 +01:00
|
|
|
tokens = line.split('=', 1)
|
2012-09-15 18:25:25 +03:00
|
|
|
desc = self.createDescriptiveName(tokens[0])
|
2013-10-01 20:52:22 +03:00
|
|
|
if tokens[1].lower().strip() == 'group':
|
2017-03-29 12:04:09 +10:00
|
|
|
self._group = tokens[0]
|
2012-09-15 18:25:25 +03:00
|
|
|
return
|
2013-10-01 20:52:22 +03:00
|
|
|
if tokens[1].lower().strip() == 'name':
|
2015-07-26 03:48:27 +02:00
|
|
|
self.name = self.i18n_name = tokens[0]
|
2012-11-19 20:06:33 +01:00
|
|
|
return
|
2015-11-06 14:02:38 +01:00
|
|
|
|
2016-09-08 10:30:30 +02:00
|
|
|
out = getOutputFromString(line)
|
|
|
|
if out is None:
|
2016-09-05 12:59:21 +02:00
|
|
|
param = getParameterFromString(line)
|
2015-11-06 14:02:38 +01:00
|
|
|
|
2015-10-23 18:06:43 +02:00
|
|
|
if param is not None:
|
|
|
|
self.addParameter(param)
|
|
|
|
elif out is not None:
|
|
|
|
out.name = tokens[0]
|
|
|
|
out.description = desc
|
|
|
|
self.addOutput(out)
|
|
|
|
else:
|
|
|
|
raise WrongScriptException(
|
2017-03-04 16:23:36 +01:00
|
|
|
self.tr('Could not load script: {0}.\n'
|
|
|
|
'Problem with line "{1}"', 'ScriptAlgorithm').format(self.descriptionFile or '', line))
|
2015-10-23 18:06:43 +02:00
|
|
|
|
2017-01-06 20:04:00 +10:00
|
|
|
def processAlgorithm(self, feedback):
|
2012-11-19 08:04:03 +01:00
|
|
|
ns = {}
|
2017-01-06 20:04:00 +10:00
|
|
|
ns['feedback'] = feedback
|
2015-01-22 14:33:17 +01:00
|
|
|
ns['scriptDescriptionFile'] = self.descriptionFile
|
2012-12-10 00:12:07 +01:00
|
|
|
|
2012-09-15 18:25:25 +03:00
|
|
|
for param in self.parameters:
|
2012-11-19 08:04:03 +01:00
|
|
|
ns[param.name] = param.value
|
2012-09-15 18:25:25 +03:00
|
|
|
|
|
|
|
for out in self.outputs:
|
2012-11-19 08:04:03 +01:00
|
|
|
ns[out.name] = out.value
|
2012-09-15 18:25:25 +03:00
|
|
|
|
2016-05-13 09:34:25 +03:00
|
|
|
variables = re.findall('@[a-zA-Z0-9_]*', self.script)
|
2016-04-07 12:02:50 +02:00
|
|
|
script = 'import processing\n'
|
2013-10-01 20:52:22 +03:00
|
|
|
script += self.script
|
2016-04-07 12:02:50 +02:00
|
|
|
|
2016-05-13 09:34:25 +03:00
|
|
|
context = QgsExpressionContext()
|
|
|
|
context.appendScope(QgsExpressionContextUtils.globalScope())
|
2017-01-06 10:21:47 +08:00
|
|
|
context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
|
2016-04-07 12:02:50 +02:00
|
|
|
for var in variables:
|
|
|
|
varname = var[1:]
|
2016-05-13 09:34:25 +03:00
|
|
|
if context.hasVariable(varname):
|
|
|
|
script = script.replace(var, context.variable(varname))
|
2016-04-26 12:47:29 +02:00
|
|
|
else:
|
2017-03-04 16:23:36 +01:00
|
|
|
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('Cannot find variable: {0}').format(varname))
|
2016-04-07 12:02:50 +02:00
|
|
|
|
2015-08-22 14:29:41 +02:00
|
|
|
exec((script), ns)
|
2012-11-19 08:04:03 +01:00
|
|
|
for out in self.outputs:
|
|
|
|
out.setValue(ns[out.name])
|
2012-09-15 18:25:25 +03:00
|
|
|
|
2014-04-19 00:48:56 +02:00
|
|
|
def help(self):
|
2014-03-27 15:13:20 +01:00
|
|
|
if self.descriptionFile is None:
|
2014-04-24 17:25:36 +02:00
|
|
|
return False, None
|
2014-01-11 15:01:15 +01:00
|
|
|
helpfile = self.descriptionFile + '.help'
|
2012-09-15 18:25:25 +03:00
|
|
|
if os.path.exists(helpfile):
|
2014-04-19 00:48:56 +02:00
|
|
|
return True, getHtmlFromHelpFile(self, helpfile)
|
2012-09-15 18:25:25 +03:00
|
|
|
else:
|
2014-04-24 17:25:36 +02:00
|
|
|
return False, None
|
2016-05-26 11:59:04 +02:00
|
|
|
|
2016-05-28 09:17:21 +02:00
|
|
|
def shortHelp(self):
|
|
|
|
if self.descriptionFile is None:
|
|
|
|
return None
|
2016-09-21 18:24:26 +02:00
|
|
|
helpFile = str(self.descriptionFile) + '.help'
|
2016-05-28 09:17:21 +02:00
|
|
|
if os.path.exists(helpFile):
|
|
|
|
with open(helpFile) as f:
|
|
|
|
try:
|
|
|
|
descriptions = json.load(f)
|
|
|
|
if 'ALG_DESC' in descriptions:
|
2016-09-21 18:24:26 +02:00
|
|
|
return self._formatHelp(str(descriptions['ALG_DESC']))
|
2016-05-28 09:17:21 +02:00
|
|
|
except:
|
|
|
|
return None
|
|
|
|
return None
|
|
|
|
|
2016-05-26 11:59:04 +02:00
|
|
|
def getParameterDescriptions(self):
|
|
|
|
descs = {}
|
2016-05-28 09:17:21 +02:00
|
|
|
if self.descriptionFile is None:
|
|
|
|
return descs
|
2016-09-21 18:24:26 +02:00
|
|
|
helpFile = str(self.descriptionFile) + '.help'
|
2016-05-28 09:17:21 +02:00
|
|
|
if os.path.exists(helpFile):
|
2016-05-26 11:59:04 +02:00
|
|
|
with open(helpFile) as f:
|
2016-05-28 09:17:21 +02:00
|
|
|
try:
|
|
|
|
descriptions = json.load(f)
|
|
|
|
for param in self.parameters:
|
|
|
|
if param.name in descriptions:
|
2016-09-21 18:24:26 +02:00
|
|
|
descs[param.name] = str(descriptions[param.name])
|
2016-05-28 09:17:21 +02:00
|
|
|
except:
|
2016-05-31 17:23:28 +02:00
|
|
|
return descs
|
2016-05-26 11:59:04 +02:00
|
|
|
return descs
|