Skip to content

Commit 87d2da1

Browse files
authored
[FEATURE][needs-docs] Add new @alg decorator for nicer python processing scripts. (#8586)
@alg() @alg.help() @alg.input() @alg.output()
1 parent d136e92 commit 87d2da1

12 files changed

+778
-40
lines changed

python/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ ENDIF ()
6666
ADD_SUBDIRECTORY(PyQt)
6767
ADD_SUBDIRECTORY(ext-libs)
6868
ADD_SUBDIRECTORY(testing)
69+
ADD_SUBDIRECTORY(processing)
6970

7071
INCLUDE_DIRECTORIES(SYSTEM
7172
${PYTHON_INCLUDE_PATH}

python/plugins/processing/script/DeleteScriptAction.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def execute(self):
5252
QMessageBox.Yes | QMessageBox.No,
5353
QMessageBox.No)
5454
if reply == QMessageBox.Yes:
55-
filePath = ScriptUtils.findAlgorithmSource(self.itemData.__class__.__name__)
55+
filePath = ScriptUtils.findAlgorithmSource(self.itemData.name())
5656
if filePath is not None:
5757
os.remove(filePath)
5858
QgsApplication.processingRegistry().providerById("script").refreshAlgorithms()

python/plugins/processing/script/EditScriptAction.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
import inspect
2929

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

4949
def execute(self):
50-
filePath = ScriptUtils.findAlgorithmSource(self.itemData.__class__.__name__)
50+
filePath = ScriptUtils.findAlgorithmSource(self.itemData.name())
5151
if filePath is not None:
5252
dlg = ScriptEditorDialog(filePath, iface.mainWindow())
5353
dlg.show()

python/plugins/processing/script/ScriptEditorDialog.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
QgsProcessingAlgorithm,
4545
QgsProcessingFeatureBasedAlgorithm)
4646
from qgis.utils import iface, OverrideCursor
47+
from qgis.processing import alg as algfactory
4748

4849
from processing.gui.AlgorithmDialog import AlgorithmDialog
4950
from processing.script import ScriptUtils
@@ -222,9 +223,9 @@ def setHasChanged(self, hasChanged):
222223
self.update_dialog_title()
223224

224225
def runAlgorithm(self):
225-
d = {}
226+
_locals = {}
226227
try:
227-
exec(self.editor.text(), d)
228+
exec(self.editor.text(), _locals)
228229
except Exception as e:
229230
error = QgsError(traceback.format_exc(), "Processing")
230231
QgsErrorDialog.show(error,
@@ -233,10 +234,13 @@ def runAlgorithm(self):
233234
return
234235

235236
alg = None
236-
for k, v in d.items():
237-
if inspect.isclass(v) and issubclass(v, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and v.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"):
238-
alg = v()
239-
break
237+
try:
238+
alg = algfactory.instances.pop().createInstance()
239+
except IndexError:
240+
for name, attr in _locals.items():
241+
if inspect.isclass(attr) and issubclass(attr, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and attr.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"):
242+
alg = attr()
243+
break
240244

241245
if alg is None:
242246
QMessageBox.warning(self,

python/plugins/processing/script/ScriptUtils.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

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

28+
from qgis.processing import alg as algfactory
2829
import os
2930
import inspect
3031
import importlib
@@ -66,20 +67,26 @@ def loadAlgorithm(moduleName, filePath):
6667
spec = importlib.util.spec_from_file_location(moduleName, filePath)
6768
module = importlib.util.module_from_spec(spec)
6869
spec.loader.exec_module(module)
69-
for x in dir(module):
70-
obj = getattr(module, x)
71-
if inspect.isclass(obj) and issubclass(obj, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and obj.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"):
72-
scriptsRegistry[x] = filePath
73-
return obj()
70+
try:
71+
alg = algfactory.instances.pop().createInstance()
72+
scriptsRegistry[alg.name()] = filePath
73+
return alg
74+
except IndexError:
75+
for x in dir(module):
76+
obj = getattr(module, x)
77+
if inspect.isclass(obj) and issubclass(obj, (QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm)) and obj.__name__ not in ("QgsProcessingAlgorithm", "QgsProcessingFeatureBasedAlgorithm"):
78+
o = obj()
79+
scriptsRegistry[o.name()] = filePath
80+
return o
7481
except ImportError as e:
7582
QgsMessageLog.logMessage(QCoreApplication.translate("ScriptUtils", "Could not import script algorithm '{}' from '{}'\n{}").format(moduleName, filePath, str(e)),
7683
QCoreApplication.translate("ScriptUtils", "Processing"),
7784
Qgis.Critical)
7885

7986

80-
def findAlgorithmSource(className):
87+
def findAlgorithmSource(name):
8188
global scriptsRegistry
8289
try:
83-
return scriptsRegistry[className]
90+
return scriptsRegistry[name]
8491
except:
8592
return None

python/processing/CMakeLists.txt

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# See ../CMakeLists.txt for info on staged-plugins* and clean-staged-plugins targets
2+
3+
SET(QGIS_PYTHON_DIR ${PYTHON_SITE_PACKAGES_DIR}/qgis)
4+
SET (PYTHON_OUTPUT_DIRECTORY ${QGIS_OUTPUT_DIRECTORY}/python)
5+
SET (NAME processing)
6+
7+
SET(PY_FILES
8+
__init__.py
9+
algfactory.py
10+
)
11+
12+
FILE (MAKE_DIRECTORY ${QGIS_PYTHON_OUTPUT_DIRECTORY}/${NAME})
13+
INSTALL(FILES ${PY_FILES} DESTINATION "${QGIS_PYTHON_DIR}/${NAME}")
14+
15+
ADD_CUSTOM_TARGET(py${NAME} ALL)
16+
# stage to output to make available when QGIS is run from build directory
17+
FOREACH(pyfile ${PY_FILES})
18+
ADD_CUSTOM_COMMAND(TARGET py${NAME}
19+
POST_BUILD
20+
COMMAND ${CMAKE_COMMAND} -E copy ${pyfile} "${QGIS_PYTHON_OUTPUT_DIRECTORY}/${NAME}"
21+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
22+
DEPENDS ${pyfile}
23+
)
24+
PY_COMPILE(pyutils "${QGIS_PYTHON_OUTPUT_DIRECTORY}/${NAME}/${pyfile}")
25+
ENDFOREACH(pyfile)

python/processing/__init__.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
__init__.py
6+
---------------------
7+
Date : November 2018
8+
Copyright : (C) 2018 by Nathan Woodrow
9+
Email : woodrow dot nathan at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Nathan Woodrow'
21+
__date__ = 'November 2018'
22+
__copyright__ = '(C) 2018, Nathan Woodrow'
23+
# This will get replaced with a git SHA1 when you do a git archive
24+
__revision__ = '$Format:%H$'
25+
26+
from .algfactory import ProcessingAlgFactory
27+
28+
alg = ProcessingAlgFactory()

0 commit comments

Comments
 (0)