Skip to content
Permalink
Browse files
added field pyculator algorithm
fixed minor issues in algorithm execution and logging
added support for multiline strings
updated credits in about html page

git-svn-id: http://sextante.googlecode.com/svn/trunk/soft/bindings/qgis-plugin@334 881b9c09-3ef8-f3c2-ec3d-21d735c97f4d
  • Loading branch information
volayaf committed Aug 6, 2012
1 parent d4a316a commit 6bf0bfe
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 17 deletions.
@@ -9,9 +9,11 @@ <h2>SEXTANTE for QGIS</h2>
<p>A development by Victor Olaya (volayaf@gmail.com).</p>
<p>Portions of this software contributed by:
<ul>
<li>Michael Nimm (mmqgis algorithms)</li>
<li>Alexander Bruy</li>
<li>Carson Farmer (fTools algorithms)</li>
<li>Julien Malik (Orfeo Toolbox connectors)</li>
<li>Evgeniy Nikulin (Original Field Pyculator code)</li>
<li>Michael Nimm (mmqgis algorithms)</li>
<li>Camilo Polymeris (Threading). Developed as part of Google Summer of Code 2012</li>
</ul>
</p>
@@ -0,0 +1,217 @@
from sextante.core.GeoAlgorithm import GeoAlgorithm
from sextante.outputs.OutputVector import OutputVector
from sextante.parameters.ParameterVector import ParameterVector
from qgis.core import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from sextante.parameters.ParameterString import ParameterString
from sextante.core.QGisLayers import QGisLayers
import os
from PyQt4 import QtGui
from sextante.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from sextante.parameters.ParameterBoolean import ParameterBoolean
import sys


class FieldsPyculator(GeoAlgorithm):

INPUT_LAYER = "INPUT_LAYER"
USE_SELECTED = "USE_SELECTED"
FIELD_NAME = "FIELD_NAME"
#USE_GLOBAL = "USE_GLOBAL"
GLOBAL = "GLOBAL"
FORMULA = "FORMULA"
OUTPUT_LAYER ="OUTPUT_LAYER"
RESULT_VAR_NAME = "value"

def getIcon(self):
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/toolbox.png")

def defineCharacteristics(self):
self.name = "Field Pyculator"
self.group = "Algorithms for vector layers"
self.addParameter(ParameterVector(self.INPUT_LAYER, "Input layer", ParameterVector.VECTOR_TYPE_ANY, False))
self.addParameter(ParameterBoolean(self.USE_SELECTED, "Use only selected features", False))
self.addParameter(ParameterString(self.FIELD_NAME, "Result field name", "NewField"))
#self.addParameter(ParameterBoolean(self.USE_GLOBAL, "Use global expression", False))
self.addParameter(ParameterString(self.GLOBAL, "Global expression", multiline = True))
self.addParameter(ParameterString(self.FORMULA, "Formula", "value = ", multiline = True))
self.addOutput(OutputVector(self.OUTPUT_LAYER, "Output layer"))


def processAlgorithm(self, progress):
fieldname = self.getParameterValue(self.FIELD_NAME)
code = self.getParameterValue(self.FORMULA)
globalExpression = self.getParameterValue(self.GLOBAL)
#useGlobal = self.getParameterValue(self.USE_GLOBAL)
useSelected = self.getParameterValue(self.USE_SELECTED)
settings = QSettings()
systemEncoding = settings.value( "/UI/encoding", "System" ).toString()
output = self.getOutputValue(self.OUTPUT_LAYER)
layer = QGisLayers.getObjectFromUri(self.getParameterValue(self.INPUT_LAYER))
vprovider = layer.dataProvider()
allAttrs = vprovider.attributeIndexes()
vprovider.select( allAttrs )
fields = vprovider.fields()
fields[len(fields)] = QgsField(fieldname, QVariant.Double)
writer = QgsVectorFileWriter( output, systemEncoding,fields, vprovider.geometryType(), vprovider.crs() )
outFeat = QgsFeature()
nFeatures = vprovider.featureCount()
nElement = 0
new_ns = {}

#run global code
if globalExpression.strip() != "":
try:
bytecode = compile(globalExpression, '<string>', 'exec')
exec bytecode in new_ns
except:
raise GeoAlgorithmExecutionException("FieldPyculator code execute error\n" +
"Global code block can't be executed!%s \n %s" %
(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1])))

#replace all fields tags
field_map = vprovider.fields()
for num, field in field_map.iteritems():
field_name = unicode(field.name())
replval = '__attr[' + str(num) + ']'
code = code.replace("<"+field_name+">",replval)

#replace all special vars
code = code.replace('$id','__id')
code = code.replace('$geom','__geom')
need_id = code.find("__id") != -1
need_geom = code.find("__geom") != -1
need_attrs = code.find("__attr") != -1


#compile
try:
bytecode = compile(code, '<string>', 'exec')
except:
raise GeoAlgorithmExecutionException("FieldPyculator code execute error\n"+
"Field code block can't be executed! %s \n %s"
(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1])))


#run
if not useSelected:
feat = QgsFeature()
if need_attrs:
attr_ind = vprovider.attributeIndexes()
else:
attr_ind = []
vprovider.select(attr_ind, QgsRectangle(), True)

while vprovider.nextFeature(feat):
progress.setPercentage(int((100 * nElement)/nFeatures))
attrMap = feat.attributeMap()
feat_id = feat.id()

#add needed vars
if need_id:
new_ns['__id'] = feat_id

if need_geom:
geom = feat.geometry()
new_ns['__geom'] = geom

if need_attrs:
attr = []
for num,a in attrMap.iteritems():
attr.append(self.Qvar2py(a))
new_ns['__attr'] = attr

#clear old result
if new_ns.has_key(self.RESULT_VAR_NAME):
del new_ns[self.RESULT_VAR_NAME]


#exec
#try:
exec bytecode in new_ns
#except:
# raise e
#===============================================================
# GeoAlgorithmExecutionException("FieldPyculator code execute error\n"+
# "Field code block can't be executed for feature %s\n%s\n%s" %
# (unicode(sys.exc_info()[0].__name__),
# unicode(sys.exc_info()[1]),
# unicode(feat_id)))
#===============================================================

#check result
if not new_ns.has_key(self.RESULT_VAR_NAME):
raise GeoAlgorithmExecutionException("FieldPyculator code execute error\n" +
"Field code block does not return '%s1' variable! Please declare this variable in your code!" %
self.RESULT_VAR_NAME)


#write feature
nElement += 1
outFeat.setGeometry( feat.geometry() )
outFeat.setAttributeMap( attrMap )
outFeat.addAttribute(len(vprovider.fields()), QVariant(new_ns[self.RESULT_VAR_NAME]))
writer.addFeature(outFeat)

else:
features = layer.selectedFeatures()
nFeatures = len(features)
for feat in features:
progress.setPercentage(int((100 * nElement)/nFeatures))
attrMap = feat.attributeMap()
feat_id = feat.id()

#add needed vars
if need_id:
new_ns['__id'] = feat_id

if need_geom:
geom = feat.geometry()
new_ns['__geom'] = geom

if need_attrs:
attrMap = feat.attributeMap()
attr = []
for num,a in attrMap.iteritems():
attr.append(self.Qvar2py(a))
new_ns['__attr'] = attr

#clear old result
if new_ns.has_key(self.RESULT_VAR_NAME):
del new_ns[self.RESULT_VAR_NAME]

#exec
exec bytecode in new_ns

#check result
if not new_ns.has_key(self.RESULT_VAR_NAME):
raise GeoAlgorithmExecutionException("FieldPyculator code execute error\n" +
"Field code block does not return '%s1' variable! Please declare this variable in your code!" %
self.RESULT_VAR_NAME)

#write feature
nElement += 1
outFeat.setGeometry( feat.geometry() )
outFeat.setAttributeMap( attrMap )
outFeat.addAttribute(len(vprovider.fields()), QVariant(new_ns[self.RESULT_VAR_NAME]))
writer.addFeature(outFeat)

del writer


def Qvar2py(self,qv):
if qv.type() == 2:
return qv.toInt()[0]
if qv.type() == 10:
return unicode(qv.toString())
if qv.type() == 6:
return qv.toDouble()[0]
return None


def checkParameterValuesBeforeExecuting(self):
##TODO check that formula is correct and fields exist
pass


@@ -6,13 +6,14 @@
from sextante.algs.SaveSelectedFeatures import SaveSelectedFeatures
from sextante.algs.Explode import Explode
from sextante.algs.AutoincrementalField import AutoincrementalField
from sextante.algs.FieldPyculator import FieldsPyculator

class SextanteAlgorithmProvider(AlgorithmProvider):

def __init__(self):
AlgorithmProvider.__init__(self)
self.alglist = [AddTableField(), FieldsCalculator(), SaveSelectedFeatures(),
AutoincrementalField(), Explode()]
AutoincrementalField(), Explode(), FieldsPyculator()]

def initializeSettings(self):
AlgorithmProvider.initializeSettings(self)
@@ -287,10 +287,6 @@ def runAlgorithm(algOrName, onFinish, *args):

msg = alg.checkParameterValuesBeforeExecuting()
if msg:
try:
QMessageBox.critical(None, "Unable to execute algorithm", msg)
return
except:
print ("Unable to execute algorithm\n" + msg)
return

@@ -304,7 +300,8 @@ def runAlgorithm(algOrName, onFinish, *args):
progress.setLabelText("Executing %s..." % alg.name)
def finish():
QApplication.restoreOverrideCursor()
onFinish(alg)
if onFinish is not None:
onFinish(alg)
progress.close()
def error(msg):
QApplication.restoreOverrideCursor()
@@ -323,8 +320,8 @@ def cancel():
algEx.start()
algEx.wait()
else:
UnthreadedAlgorithmExecutor.runalg(alg, SilentProgress())
if onFinish:
ret = UnthreadedAlgorithmExecutor.runalg(alg, SilentProgress())
if onFinish is not None and ret:
onFinish(alg)
QApplication.restoreOverrideCursor()
return alg
@@ -18,7 +18,6 @@
from sextante.parameters.ParameterRange import ParameterRange
from sextante.parameters.ParameterNumber import ParameterNumber

from sextante.gui.ParametersPanel import ParametersPanel
from sextante.parameters.ParameterFile import ParameterFile
from sextante.parameters.ParameterCrs import ParameterCrs
from sextante.core.SextanteConfig import SextanteConfig
@@ -30,6 +29,7 @@
from sextante.core.WrongHelpFileException import WrongHelpFileException
import os
from sextante.gui.UnthreadedAlgorithmExecutor import UnthreadedAlgorithmExecutor
from sextante.parameters.ParameterString import ParameterString

try:
_fromUtf8 = QtCore.QString.fromUtf8
@@ -161,8 +161,14 @@ def setParamValue(self, param, widget):
return param.setValue(value)
elif isinstance(param, (ParameterNumber, ParameterFile, ParameterCrs, ParameterExtent)):
return param.setValue(widget.getValue())
elif isinstance(param, ParameterString):
if param.multiline:
return param.setValue(unicode(widget.toPlainText()))
else:
return param.setValue(unicode(widget.text()))

else:
return param.setValue(str(widget.text()))
return param.setValue(unicode(widget.text()))

@pyqtSlot()
def accept(self):
@@ -213,6 +219,18 @@ def accept(self):
SextanteLog.addToLog(SextanteLog.LOG_ALGORITHM, command)
if UnthreadedAlgorithmExecutor.runalg(self.alg, self):
self.finish()
else:
QApplication.restoreOverrideCursor()
keepOpen = SextanteConfig.getSetting(SextanteConfig.KEEP_DIALOG_OPEN)
if not keepOpen:
self.close()
else:
self.progressLabel.setText("")
self.progress.setMaximum(100)
self.progress.setValue(0)
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Close).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Cancel).setEnabled(False)
else:
QMessageBox.critical(self, "Unable to execute algorithm", "Wrong or missing parameter values")

@@ -231,7 +249,7 @@ def finish(self):
self.progress.setValue(0)
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Close).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Cancel).setEnabled(False)
self.buttonBox.button(QtGui.QDialogButtonBox.Cancel).setEnabled(False)

@pyqtSlot(str)
def error(self, msg):
@@ -246,6 +264,7 @@ def error(self, msg):
self.progressLabel.setText("")
self.progress.setValue(0)
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Close).setEnabled(True)

@pyqtSlot(int)
def iterate(self, i):
@@ -258,17 +277,19 @@ def cancel(self):
self.algEx.finished.disconnect()
self.algEx.terminate()
QApplication.restoreOverrideCursor()
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Close).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Cancel).setEnabled(False)
except:
pass

@pyqtSlot(str)
def setInfo(self, msg, error = False):
if error:
SextanteLog.addToLog(SextanteLog.LOG_ERROR, msg)
#SextanteLog.addToLog(SextanteLog.LOG_ERROR, msg)
self.logText.append('<b>' + msg + '</b>')
else:
SextanteLog.addToLog(SextanteLog.LOG_INFO, msg)
#SextanteLog.addToLog(SextanteLog.LOG_INFO, msg)
self.logText.append(msg)

def setPercentage(self, i):
@@ -4,6 +4,7 @@
from sextante.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from sextante.core.QGisLayers import QGisLayers
from sextante.core.SextanteUtils import SextanteUtils
import sys

class AlgorithmExecutor(QThread):
percentageChanged = pyqtSignal(int)
@@ -55,13 +56,13 @@ def runalg(self):
self.algorithm.execute(self.progress)
except GeoAlgorithmExecutionException,e :
self.error.emit(e.msg)
except BaseException,e:
except Exception,e:
self.error.emit(str(e))
print str(e)
# catch *all* errors, because QGIS tries to handle them in the GUI, which is fatal, this
# being a separate thread.
except:
print "Error executing " + str(self)
self.error.emit("Error executing " + str(self.alg.name) + "\n" + sys.exc_info()[0])

def runalgIterating(self):
try:

0 comments on commit 6bf0bfe

Please sign in to comment.