Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run QgsProcessingAlgorithm with explicitely defined QgsProcessingContext and QgisInterfaces. #60805

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
49 changes: 36 additions & 13 deletions python/plugins/processing/ProcessingPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@

import shutil
import os
import sys
from typing import List
from typing import Optional
from functools import partial

from qgis.core import (
QgsApplication,
QgsProcessingUtils,
QgsProcessingModelAlgorithm,
QgsProcessingAlgorithm,
QgsDataItemProvider,
Expand All @@ -36,25 +34,26 @@
QgsMapLayerType,
QgsMimeDataUtils,
QgsSettings,
QgsProcessingContext,
)
from qgis.gui import (
QgsGui,
QgsOptionsWidgetFactory,
QgsCustomDropHandler,
QgsProcessingHistoryDialog,
QgisInterface,
)
from qgis.PyQt.QtCore import (
QObject,
Qt,
QItemSelectionModel,
QCoreApplication,
QDir,
QFileInfo,
pyqtSlot,
QMetaObject,
)
from qgis.PyQt.QtWidgets import QWidget, QMenu, QAction
from qgis.PyQt.QtGui import QIcon, QKeySequence
from qgis.PyQt.QtGui import QKeySequence
from qgis.utils import iface

from processing.core.Processing import Processing
Expand Down Expand Up @@ -82,7 +81,6 @@
createButtons,
removeButtons,
)
from processing.core.ProcessingResults import resultsList

pluginPath = os.path.dirname(__file__)

Expand Down Expand Up @@ -421,7 +419,15 @@ def updateProjectModelMenu(self):
)

@pyqtSlot(str, QWidget, bool, bool)
def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
def executeAlgorithm(
self,
alg_id,
parent,
in_place=False,
as_batch=False,
context: Optional[QgsProcessingContext] = None,
iface: Optional[QgisInterface] = None,
):
"""Executes a project model with GUI interaction if needed.

:param alg_id: algorithm id
Expand All @@ -432,8 +438,16 @@ def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
:type in_place: bool, optional
:param as_batch: execute as batch flag, defaults to False
:type as_batch: bool, optional
:param context: alternative processing context, e.g. to allow users to select layers that are not
available in QgsProject.instance()
:type context: QgsProcessingContext, optional
:param iface: alternative QgisInterface, defaults to standard QgisInterface (QGIS Main GUI)
:type iface: QgisInterface, optional
"""

if iface is None:
iface = self.iface

config = {}
if in_place:
config["IN_PLACE"] = True
Expand All @@ -459,7 +473,9 @@ def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
return

if as_batch:
dlg = BatchAlgorithmDialog(alg, iface.mainWindow())
dlg = BatchAlgorithmDialog(
alg, iface.mainWindow(), context=context, iface=iface
)
dlg.show()
dlg.exec()
else:
Expand All @@ -473,8 +489,12 @@ def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
if d.name() not in (in_place_input_parameter_name, "OUTPUT")
]:
parameters = {}
feedback = MessageBarProgress(algname=alg.displayName())
ok, results = execute_in_place(alg, parameters, feedback=feedback)
feedback = MessageBarProgress(
algname=alg.displayName(), messagebar=iface.messageBar()
)
ok, results = execute_in_place(
alg, parameters, context=context, feedback=feedback, iface=iface
)
if ok:
iface.messageBar().pushSuccess(
"",
Expand All @@ -491,7 +511,10 @@ def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
dlg = alg.createCustomParametersWidget(parent)

if not dlg:
dlg = AlgorithmDialog(alg, in_place, iface.mainWindow())
dlg = AlgorithmDialog(
alg, in_place, context=context, iface=iface
)

canvas = iface.mapCanvas()
prevMapTool = canvas.mapTool()
dlg.show()
Expand All @@ -507,10 +530,10 @@ def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
pass
else:
feedback = MessageBarProgress(algname=alg.displayName())
context = dataobjects.createContext(feedback)
context = dataobjects.createContext(feedback, context=context)
parameters = {}
ret, results = execute(alg, parameters, context, feedback)
handleAlgorithmResults(alg, context, feedback)
handleAlgorithmResults(alg, context, feedback, iface=iface)
feedback.close()

def sync_in_place_button_state(self, layer=None):
Expand Down
26 changes: 21 additions & 5 deletions python/plugins/processing/gui/AlgorithmDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@

import datetime
import time
from typing import Optional

from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtWidgets import QMessageBox, QPushButton, QDialogButtonBox
from qgis.PyQt.QtGui import QColor, QPalette

from qgis.core import (
Qgis,
QgsProcessingContext,
QgsApplication,
QgsProcessingAlgRunnerTask,
QgsProcessingOutputHtml,
Expand All @@ -37,9 +39,9 @@
)
from qgis.gui import (
QgsGui,
QgisInterface,
QgsProcessingAlgorithmDialogBase,
QgsProcessingParametersGenerator,
QgsProcessingContextGenerator,
)
from qgis.utils import iface

Expand All @@ -56,13 +58,21 @@

class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):

def __init__(self, alg, in_place=False, parent=None):
def __init__(
self,
alg,
in_place=False,
parent=None,
context: Optional[QgsProcessingContext] = None,
iface: Optional[QgisInterface] = iface,
):
super().__init__(parent)

self.feedback_dialog = None
self.in_place = in_place
self.active_layer = iface.activeLayer() if self.in_place else None

self.parent_context = context
self.context = None
self.feedback = None
self.history_log_id = None
Expand Down Expand Up @@ -106,7 +116,9 @@ def __init__(self, alg, in_place=False, parent=None):
self.updateRunButtonVisibility()

def getParametersPanel(self, alg, parent):
panel = ParametersPanel(parent, alg, self.in_place, self.active_layer)
panel = ParametersPanel(
parent, alg, self.in_place, self.active_layer, context=self.parent_context
)
return panel

def runAsBatch(self):
Expand Down Expand Up @@ -178,14 +190,18 @@ def createProcessingParameters(
def processingContext(self):
if self.context is None:
self.feedback = self.createFeedback()
self.context = dataobjects.createContext(self.feedback)
self.context = dataobjects.createContext(
self.feedback, context=self.parent_context
)

self.applyContextOverrides(self.context)
return self.context

def runAlgorithm(self):
self.feedback = self.createFeedback()
self.context = dataobjects.createContext(self.feedback)
self.context = dataobjects.createContext(
self.feedback, context=self.parent_context
)
self.applyContextOverrides(self.context)
self.algorithmAboutToRun.emit(self.context)

Expand Down
28 changes: 20 additions & 8 deletions python/plugins/processing/gui/AlgorithmExecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
__copyright__ = "(C) 2012, Victor Olaya"

import sys
from typing import Optional
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
Qgis,
QgsApplication,
QgsFeatureSink,
QgsProcessingFeedback,
QgsProcessingUtils,
Expand All @@ -32,18 +32,16 @@
QgsProcessingFeatureSourceDefinition,
QgsProcessingFeatureSource,
QgsProcessingParameters,
QgsProject,
QgsFeatureRequest,
QgsFeature,
QgsExpression,
QgsWkbTypes,
QgsGeometry,
QgsVectorLayerUtils,
QgsVectorLayer,
)
from processing.gui.Postprocessing import handleAlgorithmResults
from processing.tools import dataobjects
from qgis.utils import iface
from qgis.gui import QgisInterface
from qgis.utils import iface as qgis_iface


def execute(alg, parameters, context=None, feedback=None, catch_exceptions=True):
Expand Down Expand Up @@ -364,7 +362,9 @@ def execute_in_place_run(
return False, {}


def execute_in_place(alg, parameters, context=None, feedback=None):
def execute_in_place(
alg, parameters, context=None, feedback=None, iface: Optional[QgisInterface] = None
):
"""Executes an algorithm modifying features in-place, if the INPUT
parameter is not defined, the current active layer will be used as
INPUT.
Expand All @@ -377,11 +377,14 @@ def execute_in_place(alg, parameters, context=None, feedback=None):
:param context: QgsProcessingContext, optional
:param feedback: feedback, defaults to None
:param feedback: QgsProcessingFeedback, optional
:param iface: QgisInterface, options, defaults to standard QgisInterface (from qgis.utils import iface)
:raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project
:return: a tuple with true if success and results
:rtype: tuple
"""

if iface is None:
iface = qgis_iface
if feedback is None:
feedback = QgsProcessingFeedback()
if context is None:
Expand Down Expand Up @@ -423,12 +426,21 @@ def execute_in_place(alg, parameters, context=None, feedback=None):
return ok, results


def executeIterating(alg, parameters, paramToIter, context, feedback):
def executeIterating(
alg,
parameters,
paramToIter,
context,
feedback,
iface: Optional[QgisInterface] = None,
):
# Generate all single-feature layers
parameter_definition = alg.parameterDefinition(paramToIter)
if not parameter_definition:
return False

if iface is None:
iface = qgis_iface
iter_source = QgsProcessingParameters.parameterAsSource(
parameter_definition, parameters, context
)
Expand Down Expand Up @@ -487,7 +499,7 @@ def executeIterating(alg, parameters, paramToIter, context, feedback):
if not ret:
return False

handleAlgorithmResults(alg, context, feedback)
handleAlgorithmResults(alg, context, feedback, iface=iface)
return True


Expand Down
Loading
Loading