diff --git a/python/plugins/processing/gui/AutofillDialog.py b/python/plugins/processing/gui/AutofillDialog.py index 8bc056e05cf..4209005c6ac 100644 --- a/python/plugins/processing/gui/AutofillDialog.py +++ b/python/plugins/processing/gui/AutofillDialog.py @@ -44,10 +44,13 @@ class AutofillDialog(BASE, WIDGET): def __init__(self, alg): super(AutofillDialog, self).__init__(None) self.setupUi(self) + self.mode = None + self.param_index = None + self.alg = alg self.cmbFillType.currentIndexChanged.connect(self.toggleParameters) - for param in alg.parameterDefinitions(): + for param in self.alg.parameterDefinitions(): self.cmbParameters.addItem(param.description()) def toggleParameters(self, index): @@ -60,10 +63,10 @@ class AutofillDialog(BASE, WIDGET): def accept(self): self.mode = self.cmbFillType.currentIndex() - self.param = self.cmbParameters.currentIndex() + self.param_index = self.cmbParameters.currentIndex() QDialog.accept(self) def reject(self): self.mode = None - self.param = None + self.param_index = None QDialog.reject(self) diff --git a/python/plugins/processing/gui/BatchAlgorithmDialog.py b/python/plugins/processing/gui/BatchAlgorithmDialog.py index d34210e2aa3..ab059841350 100644 --- a/python/plugins/processing/gui/BatchAlgorithmDialog.py +++ b/python/plugins/processing/gui/BatchAlgorithmDialog.py @@ -26,11 +26,21 @@ __copyright__ = '(C) 2012, Victor Olaya' __revision__ = '$Format:%H$' +from pprint import pformat +import time + from qgis.PyQt.QtWidgets import QApplication, QMessageBox, QSizePolicy from qgis.PyQt.QtGui import QCursor from qgis.PyQt.QtCore import Qt -from qgis.core import QgsProcessingParameterDefinition +from qgis.core import (QgsProcessingParameterDefinition, + QgsProcessingParameterRasterOutput, + QgsProcessingParameterFeatureSink, + QgsProcessingOutputLayerDefinition, + QgsProcessingOutputHtml, + QgsProcessingOutputNumber, + QgsProject) + from qgis.gui import QgsMessageBar from processing.gui.BatchPanel import BatchPanel @@ -38,11 +48,7 @@ from processing.gui.AlgorithmDialogBase import AlgorithmDialogBase from processing.gui.AlgorithmExecutor import execute from processing.gui.Postprocessing import handleAlgorithmResults -from processing.core.ProcessingResults import ProcessingResults - -from processing.core.outputs import OutputNumber -from processing.core.outputs import OutputString -from processing.core.outputs import OutputHTML +from processing.core.ProcessingResults import resultsList from processing.tools.system import getTempFilename from processing.tools import dataobjects @@ -56,7 +62,6 @@ class BatchAlgorithmDialog(AlgorithmDialogBase): AlgorithmDialogBase.__init__(self, alg) self.alg = alg - self.alg_parameters = [] self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName())) @@ -69,11 +74,11 @@ class BatchAlgorithmDialog(AlgorithmDialogBase): self.layout().insertWidget(0, self.bar) def accept(self): - self.alg_parameters = [] - self.load = [] - self.canceled = False + alg_parameters = [] + load = [] context = dataobjects.createContext() + feedback = self.createFeedback() for row in range(self.mainWidget.tblParameters.rowCount()): col = 0 @@ -87,36 +92,36 @@ class BatchAlgorithmDialog(AlgorithmDialogBase): self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format( param.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) - self.algs = None return col += 1 - for out in alg.destinationParameterDefinitions(): + count_visible_outputs = 0 + for out in self.alg.destinationParameterDefinitions(): if out.flags() & QgsProcessingParameterDefinition.FlagHidden: continue + count_visible_outputs += 1 widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() - if text.strip() != '': - out.value = text + if param.checkValueIsAcceptable(text, context): + if isinstance(out, (QgsProcessingParameterRasterOutput, + QgsProcessingParameterFeatureSink)): + # load rasters and sinks on completion + parameters[out.name()] = QgsProcessingOutputLayerDefinition(text, context.project()) + else: + parameters[out.name()] = text col += 1 else: self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format( out.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) - self.algs = None return - self.alg_parameters.append(parameters) - if self.alg.countVisibleOutputs(): - widget = self.mainWidget.tblParameters.cellWidget(row, col) - self.load.append(widget.currentIndex() == 0) - else: - self.load.append(False) + alg_parameters.append(parameters) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) + self.buttonCancel.setEnabled(True) - self.progressBar.setMaximum(len(self.algs)) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) @@ -124,45 +129,62 @@ class BatchAlgorithmDialog(AlgorithmDialogBase): except: pass - for count, parameters in enumerate(self.alg_parameters): - self.setText(self.tr('\nProcessing algorithm {0}/{1}...').format(count + 1, len(self.alg_parameters))) + start_time = time.time() + + algorithm_results = [] + for count, parameters in enumerate(alg_parameters): + if feedback.isCanceled(): + break + self.setText(self.tr('\nProcessing algorithm {0}/{1}...').format(count + 1, len(alg_parameters))) self.setInfo(self.tr('Algorithm {0} starting...').format(self.alg.displayName()), escape_html=False) - ret, results = execute(self.alg, parameters, context, self.feedback) - if ret and not self.canceled: - if self.load[count]: - handleAlgorithmResults(self.alg, context, self.feedback, False) + + feedback.pushInfo(self.tr('Input parameters:')) + feedback.pushCommandInfo(pformat(parameters)) + feedback.pushInfo('') + + alg_start_time = time.time() + ret, results = execute(self.alg, parameters, context, feedback) + if ret: self.setInfo(self.tr('Algorithm {0} correctly executed...').format(self.alg.displayName()), escape_html=False) + feedback.setProgress(100) + feedback.pushInfo( + self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - alg_start_time))) + feedback.pushInfo(self.tr('Results:')) + feedback.pushCommandInfo(pformat(results)) + feedback.pushInfo('') + algorithm_results.append(results) else: - QApplication.restoreOverrideCursor() - return + break - self.finish() + feedback.pushInfo(self.tr('Batch execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) - def finish(self): - for count, parameters in enumerate(self.alg_parameters): - self.loadHTMLResults(self.alg, count) + handleAlgorithmResults(self.alg, context, feedback, False) - self.createSummaryTable() + self.finish(algorithm_results) + self.buttonCancel.setEnabled(False) + + def finish(self, algorithm_results): + for count, results in enumerate(algorithm_results): + self.loadHTMLResults(results, count) + + self.createSummaryTable(algorithm_results) QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) - def loadHTMLResults(self, alg, num): - for out in alg.outputs: - if out.flags() & QgsProcessingParameterDefinition.FlagHidden or not out.open: - continue + def loadHTMLResults(self, results, num): + for out in self.alg.outputDefinitions(): + if isinstance(out, QgsProcessingOutputHtml) and out.name() in results and results[out.name()]: + resultsList.addResult(icon=self.alg.icon(), name='{} [{}]'.format(out.description(), num), + result=results[out.name()]) - if isinstance(out, OutputHTML): - ProcessingResults.addResult( - '{} [{}]'.format(out.description(), num), out.value) - - def createSummaryTable(self): + def createSummaryTable(self, algorithm_results): createTable = False - for out in self.algs[0].outputs: - if isinstance(out, (OutputNumber, OutputString)): + for out in self.alg.outputDefinitions(): + if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)): createTable = True break @@ -171,12 +193,12 @@ class BatchAlgorithmDialog(AlgorithmDialogBase): outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: - for alg in self.algs: + for res in algorithm_results: f.write('
\n') - for out in alg.outputs: - if isinstance(out, (OutputNumber, OutputString)): - f.write('

{}: {}

\n'.format(out.description(), out.value)) + for out in self.alg.outputDefinitions(): + if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)) and out.name() in res: + f.write('

{}: {}

\n'.format(out.description(), res[out.name()])) f.write('
\n') - ProcessingResults.addResult( - '{} [summary]'.format(self.algs[0].name), outputFile) + resultsList.addResult(self.alg.icon(), + '{} [summary]'.format(self.alg.name()), outputFile) diff --git a/python/plugins/processing/gui/BatchInputSelectionPanel.py b/python/plugins/processing/gui/BatchInputSelectionPanel.py index 1e5afe0d6d9..26d5c996999 100644 --- a/python/plugins/processing/gui/BatchInputSelectionPanel.py +++ b/python/plugins/processing/gui/BatchInputSelectionPanel.py @@ -36,14 +36,15 @@ from qgis.PyQt.QtGui import QCursor from qgis.core import (QgsMapLayer, QgsSettings, QgsProject, - QgsProcessingUtils) + QgsProcessingUtils, + QgsProcessingParameterMultipleLayers, + QgsProcessingParameterRasterLayer, + QgsProcessingParameterDefinition, + QgsProcessingParameterTable, + QgsProcessingParameterFeatureSource) from processing.gui.MultipleInputDialog import MultipleInputDialog -from processing.core.parameters import ParameterMultipleInput -from processing.core.parameters import ParameterRaster -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterTable from processing.gui.ParameterGuiUtils import getFileFilter from processing.tools import dataobjects @@ -84,7 +85,7 @@ class BatchInputSelectionPanel(QWidget): def showPopupMenu(self): popupmenu = QMenu() - if not (isinstance(self.param, ParameterMultipleInput) and + if not (isinstance(self.param, QgsProcessingParameterMultipleLayers) and self.param.datatype == dataobjects.TYPE_FILE): selectLayerAction = QAction( self.tr('Select from open layers'), self.pushButton) @@ -99,19 +100,22 @@ class BatchInputSelectionPanel(QWidget): popupmenu.exec_(QCursor.pos()) def showLayerSelectionDialog(self): - if (isinstance(self.param, ParameterRaster) or - (isinstance(self.param, ParameterMultipleInput) and - self.param.datatype == dataobjects.TYPE_RASTER)): + layers = [] + if (isinstance(self.param, QgsProcessingParameterRasterLayer) or + (isinstance(self.param, QgsProcessingParameterMultipleLayers) and + self.param.layerType() == QgsProcessingParameterDefinition.TypeRaster)): layers = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance()) - elif isinstance(self.param, ParameterTable): + elif isinstance(self.param, QgsProcessingParameterTable): layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance()) else: - if isinstance(self.param, ParameterVector): - datatype = self.param.datatype - else: - datatype = [self.param.datatype] - if datatype != dataobjects.TYPE_VECTOR_ANY: - layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [datatype]) + datatypes = [QgsProcessingParameterDefinition.TypeVectorAny] + if isinstance(self.param, QgsProcessingParameterFeatureSource): + datatypes = self.param.dataTypes() + elif isinstance(self.param, QgsProcessingParameterMultipleLayers): + datatypes = [self.param.layerType()] + + if QgsProcessingParameterDefinition.TypeVectorAny not in datatypes: + layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), datatypes) else: layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance()) @@ -120,17 +124,17 @@ class BatchInputSelectionPanel(QWidget): if dlg.selectedoptions is not None: selected = dlg.selectedoptions if len(selected) == 1: - self.setValue(layers[selected[0]]) + self.setValue(layers[selected[0]].id()) else: - if isinstance(self.param, ParameterMultipleInput): - self.text.setText(';'.join(layers[idx].name() for idx in selected)) + if isinstance(self.param, QgsProcessingParameterMultipleLayers): + self.text.setText(';'.join(layers[idx].id() for idx in selected)) else: rowdif = len(selected) - (self._table().rowCount() - self.row) for i in range(rowdif): self._panel().addRow() for i, layeridx in enumerate(selected): self._table().cellWidget(i + self.row, - self.col).setValue(layers[layeridx]) + self.col).setValue(layers[layeridx].id()) def showFileSelectionDialog(self): settings = QgsSettings() @@ -156,7 +160,7 @@ class BatchInputSelectionPanel(QWidget): self.text.setText(files[0]) self.textEditingFinished() else: - if isinstance(self.param, ParameterMultipleInput): + if isinstance(self.param, QgsProcessingParameterMultipleLayers): self.text.setText(';'.join(str(f) for f in files)) else: rowdif = len(files) - (self._table().rowCount() - self.row) diff --git a/python/plugins/processing/gui/BatchOutputSelectionPanel.py b/python/plugins/processing/gui/BatchOutputSelectionPanel.py index fe4db5f454a..9b7e9bebabd 100644 --- a/python/plugins/processing/gui/BatchOutputSelectionPanel.py +++ b/python/plugins/processing/gui/BatchOutputSelectionPanel.py @@ -31,18 +31,20 @@ __revision__ = '$Format:%H$' import os import re -from qgis.core import QgsMapLayer, QgsSettings +from qgis.core import (QgsMapLayer, + QgsSettings, + QgsProcessingParameterFolderOutput, + QgsProcessingParameterRasterLayer, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterTable, + QgsProcessingParameterMultipleLayers, + QgsProcessingParameterBoolean, + QgsProcessingParameterEnum, + QgsProcessingParameterMatrix) from qgis.PyQt.QtWidgets import QWidget, QPushButton, QLineEdit, QHBoxLayout, QSizePolicy, QFileDialog from processing.gui.AutofillDialog import AutofillDialog -from processing.core.parameters import ParameterMultipleInput -from processing.core.parameters import ParameterRaster -from processing.core.parameters import ParameterTable -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterBoolean -from processing.core.parameters import ParameterSelection -from processing.core.parameters import ParameterFixedTable -from processing.core.outputs import OutputDirectory +from processing.gui.ParameterGuiUtils import getFileFilter class BatchOutputSelectionPanel(QWidget): @@ -71,11 +73,11 @@ class BatchOutputSelectionPanel(QWidget): self.setLayout(self.horizontalLayout) def showSelectionDialog(self): - if isinstance(self.output, OutputDirectory): + if isinstance(self.output, QgsProcessingParameterFolderOutput): self.selectDirectory() return - filefilter = self.output.getFileFilter(self.alg) + filefilter = getFileFilter(self.output) settings = QgsSettings() if settings.contains('/Processing/LastBatchOutputPath'): path = str(settings.value('/Processing/LastBatchOutputPath')) @@ -108,22 +110,23 @@ class BatchOutputSelectionPanel(QWidget): n = self.table.rowCount() - self.row for i in range(n): widget = self.table.cellWidget(i + self.row, - dlg.param) - param = self.alg.parameters[dlg.param] - if isinstance(param, (ParameterRaster, - ParameterVector, ParameterTable, - ParameterMultipleInput)): + dlg.param_index) + param = self.alg.parameterDefinitions()[dlg.param_index] + if isinstance(param, (QgsProcessingParameterRasterLayer, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterTable, + QgsProcessingParameterMultipleLayers)): v = widget.value() if isinstance(v, QgsMapLayer): s = v.name() else: s = os.path.basename(v) s = os.path.splitext(s)[0] - elif isinstance(param, ParameterBoolean): + elif isinstance(param, QgsProcessingParameterBoolean): s = str(widget.currentIndex() == 0) - elif isinstance(param, ParameterSelection): + elif isinstance(param, QgsProcessingParameterEnum): s = str(widget.currentText()) - elif isinstance(param, ParameterFixedTable): + elif isinstance(param, QgsProcessingParameterMatrix): s = str(widget.table) else: s = str(widget.text()) diff --git a/python/plugins/processing/gui/BatchPanel.py b/python/plugins/processing/gui/BatchPanel.py index c689e7d952b..e23b3a56a8f 100644 --- a/python/plugins/processing/gui/BatchPanel.py +++ b/python/plugins/processing/gui/BatchPanel.py @@ -94,7 +94,7 @@ class BatchPanel(BASE, WIDGET): def initWidgets(self): # If there are advanced parameters — show corresponding button - for param in self.alg.parameters: + for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.btnAdvanced.show() break @@ -129,8 +129,8 @@ class BatchPanel(BASE, WIDGET): self.tblParameters.setHorizontalHeaderItem( column, QTableWidgetItem(self.tr('Load in QGIS'))) - # Add three empty rows by default - for i in range(3): + # Add two empty rows by default + for i in range(2): self.addRow() self.tblParameters.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)