Skip to content

Commit

Permalink
[FEATURE][needs-docs] Add new @alg decorator for nicer python process…
Browse files Browse the repository at this point in the history
…ing scripts. (#8586)

@alg()
@alg.help()
@alg.input()
@alg.output()
  • Loading branch information
NathanW2 committed Dec 10, 2018
1 parent d136e92 commit 87d2da1
Show file tree
Hide file tree
Showing 12 changed files with 778 additions and 40 deletions.
1 change: 1 addition & 0 deletions python/CMakeLists.txt
Expand Up @@ -66,6 +66,7 @@ ENDIF ()
ADD_SUBDIRECTORY(PyQt) ADD_SUBDIRECTORY(PyQt)
ADD_SUBDIRECTORY(ext-libs) ADD_SUBDIRECTORY(ext-libs)
ADD_SUBDIRECTORY(testing) ADD_SUBDIRECTORY(testing)
ADD_SUBDIRECTORY(processing)


INCLUDE_DIRECTORIES(SYSTEM INCLUDE_DIRECTORIES(SYSTEM
${PYTHON_INCLUDE_PATH} ${PYTHON_INCLUDE_PATH}
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/script/DeleteScriptAction.py
Expand Up @@ -52,7 +52,7 @@ def execute(self):
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes | QMessageBox.No,
QMessageBox.No) QMessageBox.No)
if reply == QMessageBox.Yes: if reply == QMessageBox.Yes:
filePath = ScriptUtils.findAlgorithmSource(self.itemData.__class__.__name__) filePath = ScriptUtils.findAlgorithmSource(self.itemData.name())
if filePath is not None: if filePath is not None:
os.remove(filePath) os.remove(filePath)
QgsApplication.processingRegistry().providerById("script").refreshAlgorithms() QgsApplication.processingRegistry().providerById("script").refreshAlgorithms()
Expand Down
4 changes: 2 additions & 2 deletions python/plugins/processing/script/EditScriptAction.py
Expand Up @@ -27,7 +27,7 @@


import inspect import inspect


from qgis.core import QgsProcessingAlgorithm from qgis.core import QgsProcessingAlgorithm, QgsMessageLog
from qgis.utils import iface from qgis.utils import iface
from qgis.PyQt.QtCore import QCoreApplication from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtWidgets import QMessageBox from qgis.PyQt.QtWidgets import QMessageBox
Expand All @@ -47,7 +47,7 @@ def isEnabled(self):
return isinstance(self.itemData, QgsProcessingAlgorithm) and self.itemData.provider().id() == "script" return isinstance(self.itemData, QgsProcessingAlgorithm) and self.itemData.provider().id() == "script"


def execute(self): def execute(self):
filePath = ScriptUtils.findAlgorithmSource(self.itemData.__class__.__name__) filePath = ScriptUtils.findAlgorithmSource(self.itemData.name())
if filePath is not None: if filePath is not None:
dlg = ScriptEditorDialog(filePath, iface.mainWindow()) dlg = ScriptEditorDialog(filePath, iface.mainWindow())
dlg.show() dlg.show()
Expand Down
16 changes: 10 additions & 6 deletions python/plugins/processing/script/ScriptEditorDialog.py
Expand Up @@ -44,6 +44,7 @@
QgsProcessingAlgorithm, QgsProcessingAlgorithm,
QgsProcessingFeatureBasedAlgorithm) QgsProcessingFeatureBasedAlgorithm)
from qgis.utils import iface, OverrideCursor from qgis.utils import iface, OverrideCursor
from qgis.processing import alg as algfactory


from processing.gui.AlgorithmDialog import AlgorithmDialog from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.script import ScriptUtils from processing.script import ScriptUtils
Expand Down Expand Up @@ -222,9 +223,9 @@ def setHasChanged(self, hasChanged):
self.update_dialog_title() self.update_dialog_title()


def runAlgorithm(self): def runAlgorithm(self):
d = {} _locals = {}
try: try:
exec(self.editor.text(), d) exec(self.editor.text(), _locals)
except Exception as e: except Exception as e:
error = QgsError(traceback.format_exc(), "Processing") error = QgsError(traceback.format_exc(), "Processing")
QgsErrorDialog.show(error, QgsErrorDialog.show(error,
Expand All @@ -233,10 +234,13 @@ def runAlgorithm(self):
return return


alg = None alg = None
for k, v in d.items(): try:
if inspect.isclass(v) and issubclass(v, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and v.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"): alg = algfactory.instances.pop().createInstance()
alg = v() except IndexError:
break for name, attr in _locals.items():
if inspect.isclass(attr) and issubclass(attr, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and attr.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"):
alg = attr()
break


if alg is None: if alg is None:
QMessageBox.warning(self, QMessageBox.warning(self,
Expand Down
21 changes: 14 additions & 7 deletions python/plugins/processing/script/ScriptUtils.py
Expand Up @@ -25,6 +25,7 @@


__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'


from qgis.processing import alg as algfactory
import os import os
import inspect import inspect
import importlib import importlib
Expand Down Expand Up @@ -66,20 +67,26 @@ def loadAlgorithm(moduleName, filePath):
spec = importlib.util.spec_from_file_location(moduleName, filePath) spec = importlib.util.spec_from_file_location(moduleName, filePath)
module = importlib.util.module_from_spec(spec) module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) spec.loader.exec_module(module)
for x in dir(module): try:
obj = getattr(module, x) alg = algfactory.instances.pop().createInstance()
if inspect.isclass(obj) and issubclass(obj, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and obj.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"): scriptsRegistry[alg.name()] = filePath
scriptsRegistry[x] = filePath return alg
return obj() except IndexError:
for x in dir(module):
obj = getattr(module, x)
if inspect.isclass(obj) and issubclass(obj, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and obj.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"):
o = obj()
scriptsRegistry[o.name()] = filePath
return o
except ImportError as e: except ImportError as e:
QgsMessageLog.logMessage(QCoreApplication.translate("ScriptUtils", "Could not import script algorithm '{}' from '{}'\n{}").format(moduleName, filePath, str(e)), QgsMessageLog.logMessage(QCoreApplication.translate("ScriptUtils", "Could not import script algorithm '{}' from '{}'\n{}").format(moduleName, filePath, str(e)),
QCoreApplication.translate("ScriptUtils", "Processing"), QCoreApplication.translate("ScriptUtils", "Processing"),
Qgis.Critical) Qgis.Critical)




def findAlgorithmSource(className): def findAlgorithmSource(name):
global scriptsRegistry global scriptsRegistry
try: try:
return scriptsRegistry[className] return scriptsRegistry[name]
except: except:
return None return None
25 changes: 25 additions & 0 deletions python/processing/CMakeLists.txt
@@ -0,0 +1,25 @@
# See ../CMakeLists.txt for info on staged-plugins* and clean-staged-plugins targets

SET(QGIS_PYTHON_DIR ${PYTHON_SITE_PACKAGES_DIR}/qgis)
SET (PYTHON_OUTPUT_DIRECTORY ${QGIS_OUTPUT_DIRECTORY}/python)
SET (NAME processing)

SET(PY_FILES
__init__.py
algfactory.py
)

FILE (MAKE_DIRECTORY ${QGIS_PYTHON_OUTPUT_DIRECTORY}/${NAME})
INSTALL(FILES ${PY_FILES} DESTINATION "${QGIS_PYTHON_DIR}/${NAME}")

ADD_CUSTOM_TARGET(py${NAME} ALL)
# stage to output to make available when QGIS is run from build directory
FOREACH(pyfile ${PY_FILES})
ADD_CUSTOM_COMMAND(TARGET py${NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${pyfile} "${QGIS_PYTHON_OUTPUT_DIRECTORY}/${NAME}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${pyfile}
)
PY_COMPILE(pyutils "${QGIS_PYTHON_OUTPUT_DIRECTORY}/${NAME}/${pyfile}")
ENDFOREACH(pyfile)
28 changes: 28 additions & 0 deletions python/processing/__init__.py
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-

"""
***************************************************************************
__init__.py
---------------------
Date : November 2018
Copyright : (C) 2018 by Nathan Woodrow
Email : woodrow dot nathan 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. *
* *
***************************************************************************
"""

__author__ = 'Nathan Woodrow'
__date__ = 'November 2018'
__copyright__ = '(C) 2018, Nathan Woodrow'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'

from .algfactory import ProcessingAlgFactory

alg = ProcessingAlgFactory()

0 comments on commit 87d2da1

Please sign in to comment.