Skip to content
Permalink
Browse files

[processing] brought back 'export model as python' functionality

  • Loading branch information
volaya committed Nov 6, 2015
1 parent b05fb0b commit 227af8ac8ea3b10f0700d92a59b65a3037b47161
@@ -127,6 +127,9 @@ def setValue(self, value):
self.value = bool(value)
return True

def getAsScriptCode(self):
return '##' + self.name + '=boolean ' + str(self.default)


class ParameterCrs(Parameter):

@@ -153,6 +156,8 @@ def setValue(self, value):
def getValueAsCommandLineParameter(self):
return '"' + unicode(self.value) + '"'

def getAsScriptCode(self):
return '##' + self.name + '=crs ' + str(self.default)

class ParameterDataObject(Parameter):

@@ -197,6 +202,8 @@ def setValue(self, text):
def getValueAsCommandLineParameter(self):
return '"' + unicode(self.value) + '"'

def getAsScriptCode(self):
return '##' + self.name + '=extent'

class ParameterFile(Parameter):

@@ -225,6 +232,12 @@ def typeName(self):
else:
return 'file'

def getAsScriptCode(self):
if self.isFolder:
return '##' + self.name + '=folder'
else:
return '##' + self.name + '=file'


class ParameterFixedTable(Parameter):

@@ -406,6 +419,13 @@ def dataType(self):
else:
return 'any vectors'

def getAsScriptCode(self):
if self.datatype == self.TYPE_RASTER:
return '##' + self.name + '=multiple raster'
if self.datatype == self.TYPE_FILE:
return '##' + self.name + '=multiple file'
else:
return '##' + self.name + '=multiple vector'

class ParameterNumber(Parameter):

@@ -451,6 +471,9 @@ def setValue(self, n):
return False


def getAsScriptCode(self):
return '##' + self.name + '=number ' + str(self.default)

class ParameterRange(Parameter):

def __init__(self, name='', description='', default='0,1', optional=False):
@@ -543,6 +566,9 @@ def getFileFilter(self):
exts[i] = self.tr('%s files(*.%s)', 'ParameterRaster') % (exts[i].upper(), exts[i].lower())
return ';;'.join(exts)

def getAsScriptCode(self):
return '##' + self.name + '=raster'


class ParameterSelection(Parameter):

@@ -609,6 +635,8 @@ def getValueAsCommandLineParameter(self):
ParameterString.ESCAPED_NEWLINE)) + '"'
if self.value is not None else unicode(None))

def getAsScriptCode(self):
return '##' + self.name + '=string ' + self.default

class ParameterTable(ParameterDataObject):

@@ -674,6 +702,9 @@ def getFileFilter(self):
exts[i] = self.tr('%s files(*.%s)', 'ParameterTable') % (exts[i].upper(), exts[i].lower())
return ';;'.join(exts)

def getAsScriptCode(self):
return '##' + self.name + '=table'


class ParameterTableField(Parameter):

@@ -715,6 +746,9 @@ def dataType(self):
else:
return 'any'

def getAsScriptCode(self):
return '##' + self.name + '=field ' + self.parent


class ParameterVector(ParameterDataObject):

@@ -800,6 +834,9 @@ def dataType(self):

return types[:-2]

def getAsScriptCode(self):
return '##' + self.name + '=vector'


class ParameterGeometryPredicate(Parameter):

@@ -89,7 +89,7 @@ def __init__(self, consoleName=""):
#A dict of Input object. keys are param names
self.params = {}

#A dict of Output with final output descriptions. Keys are output names.
#A dict of ModelerOutput with final output descriptions. Keys are output names.
#Outputs not final are not stored in this dict
self.outputs = {}

@@ -119,6 +119,33 @@ def setName(self, model):
name = self.consoleName + "_" + unicode(i)
self.name = name

def getOutputType(self, outputName):
output = self.algorithm.getOutputFromName(outputName)
return "output " + output.__class__.__name__.split(".")[-1][6:].lower()

def toPython(self):
s = []
params = []
for param in self.algorithm.parameters:
value = self.params[param.name]
def _toString(v):
if isinstance(v, (ValueFromInput, ValueFromOutput)):
return v.asPythonParameter()
elif isinstance(v, basestring):
return "'%s'" % v
elif isinstance(v, list):
return "[%s]" % ",".join([_toString(val) for val in v])
else:
return unicode(value)
params.append(_toString(value))
for out in self.algorithm.outputs:
if out.name in self.outputs:
params.append(safeName(self.outputs[out.name].description).lower())
else:
params.append(str(None))
s.append("outputs_%s=processing.runalg('%s', %s)" % (self.name, self.consoleName, ",".join(params)))
return s


class ValueFromInput():

@@ -137,6 +164,8 @@ def __eq__(self, other):
except:
return False

def asPythonParameter(self):
return self.name

class ValueFromOutput():

@@ -156,6 +185,9 @@ def __eq__(self, other):
def __str__(self):
return self.alg + "," + self.output

def asPythonParameter(self):
return "outputs_%s['%s']" % (self.alg, self.output)


class ModelerAlgorithm(GeoAlgorithm):

@@ -654,3 +686,33 @@ def _tr(s):
raise e
else:
raise WrongModelException(_tr('Error in model definition line: ') + '%s\n%s' % (line.strip(), traceback.format_exc()))


def toPython(self):
s = ['##%s=name' % self.name]
for param in self.inputs.values():
s.append(param.param.getAsScriptCode())
for alg in self.algs.values():
for name, out in alg.outputs.iteritems():
s.append('##%s=%s' % (safeName(out.description).lower(), alg.getOutputType(name)))

executed = []
toExecute = [alg for alg in self.algs.values() if alg.active]
while len(executed) < len(toExecute):
for alg in toExecute:
if alg.name not in executed:
canExecute = True
required = self.getDependsOnAlgorithms(alg.name)
for requiredAlg in required:
if requiredAlg != alg.name and requiredAlg not in executed:
canExecute = False
break
if canExecute:
s.extend(alg.toPython())
executed.append(alg.name)

return '\n'.join(s)

def safeName(name):
validChars = 'abcdefghijklmnopqrstuvwxyz'
return ''.join(c for c in name.lower() if c in validChars)
@@ -39,7 +39,6 @@
from processing.core.ProcessingLog import ProcessingLog
from processing.gui.HelpEditionDialog import HelpEditionDialog
from processing.gui.AlgorithmDialog import AlgorithmDialog
import processing.gui.AlgorithmClassification
from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
from processing.modeler.ModelerAlgorithm import ModelerAlgorithm, ModelerParameter
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
@@ -161,6 +160,7 @@ def _mimeDataAlgorithm(items):
self.btnSave.setIcon(QgsApplication.getThemeIcon('/mActionFileSave.svg'))
self.btnSaveAs.setIcon(QgsApplication.getThemeIcon('/mActionFileSaveAs.svg'))
self.btnExportImage.setIcon(QgsApplication.getThemeIcon('/mActionSaveMapAsImage.png'))
self.btnExportPython.setIcon(QgsApplication.getThemeIcon('/console/iconSaveAsConsole.png'))
self.btnEditHelp.setIcon(QIcon(os.path.join(pluginPath, 'images', 'edithelp.png')))
self.btnRun.setIcon(QIcon(os.path.join(pluginPath, 'images', 'runalgorithm.png')))

@@ -184,6 +184,7 @@ def _mimeDataAlgorithm(items):
self.btnSave.clicked.connect(self.save)
self.btnSaveAs.clicked.connect(self.saveAs)
self.btnExportImage.clicked.connect(self.exportAsImage)
self.btnExportPython.clicked.connect(self.exportAsPython)
self.btnEditHelp.clicked.connect(self.editHelp)
self.btnRun.clicked.connect(self.runModel)

@@ -281,6 +282,23 @@ def exportAsImage(self):

img.save(filename)

def exportAsPython(self):
filename = unicode(QFileDialog.getSaveFileName(self,
self.tr('Save Model As Python Script'), '',
self.tr('Python files (*.py *.PY)')))
if not filename:
return

if not filename.lower().endswith('.py'):
filename += '.py'

text = self.alg.toPython()
with codecs.open(filename, 'w', encoding='utf-8') as fout:
fout.write(text)
QMessageBox.information(self, self.tr('Model exported'),
self.tr('Model was correctly exported.'))


def saveModel(self, saveAs):
if unicode(self.textGroup.text()).strip() == '' \
or unicode(self.textName.text()).strip() == '':
@@ -198,7 +198,7 @@ def setupUi(self):
self.yesNoCombo.setCurrentIndex(
1 if self.param.optional else 0)
self.verticalLayout.addLayout(self.horizontalLayout2)

self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
@@ -221,8 +221,10 @@ def okPressed(self):
validChars = \
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
safeName = ''.join(c for c in description if c in validChars)
name = self.paramType.upper().replace(' ', '') + '_' \
+ safeName.upper()
name = safeName.lower()
i = 2
while name in self.alg.inputs:
name = safeName.lower() + str(i)
else:
name = self.param.name
if self.paramType \
@@ -96,6 +96,16 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnExportPython">
<property name="text">
<string>...</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">

8 comments on commit 227af8a

@joaandrade

This comment has been minimized.

Copy link

@joaandrade joaandrade replied Mar 9, 2016

Hello,

I installed the new QGIS release (2.14.0) and I was expecting that this new version brought back the feature 'export model as python'. Can you tell me if you are planning to bring it back on a next release?

Thank in advance, best regards,
João Andrade

@gioman

This comment has been minimized.

Copy link
Contributor

@gioman gioman replied Mar 9, 2016

@joaandrade do you have a "processing" folder inside .qgis2/python/plugins ? if yes you are probably masking the Processing version shipped with 2.14. Delete that folder and restart qgis.

@joaandrade

This comment has been minimized.

Copy link

@joaandrade joaandrade replied Mar 9, 2016

@gioman I reinstalled QGIS and now I don't have that folder. However, the option still does not appear.

@gioman

This comment has been minimized.

Copy link
Contributor

@gioman gioman replied Mar 9, 2016

@joaandrade that folder is not removed with (re)install so you probably looked in the wrong place. See in c:\users\yourusername.qgis2\python\plugins

@joaandrade

This comment has been minimized.

Copy link

@joaandrade joaandrade replied Mar 9, 2016

@gioman The only folder inside the directory C:\Users\myuser.qgis2\python is expressions.
I don't have the directory c:\users\myuser.qgis2\python\plugins in this QGIS installation.

@gioman

This comment has been minimized.

Copy link
Contributor

@gioman gioman replied Mar 9, 2016

@joaandrade ummm... anyway... here I see without issues the "export to python script" in the modeler window...

@joaandrade

This comment has been minimized.

Copy link

@joaandrade joaandrade replied Mar 9, 2016

@gioman a colleague tried to build QGIS 2.14 from source on a VM with Linux and the same is happening.

@gioman

This comment has been minimized.

Copy link
Contributor

@gioman gioman replied Mar 9, 2016

exp1

seems ok here on both 2.14 and master

Please sign in to comment.
You can’t perform that action at this time.