Restore batch algorithm mode following new processing API

This commit is contained in:
Nyall Dawson 2017-06-13 12:32:30 +10:00
parent 020537ac7e
commit 54124bd0aa
5 changed files with 130 additions and 98 deletions

View File

@ -44,10 +44,13 @@ class AutofillDialog(BASE, WIDGET):
def __init__(self, alg): def __init__(self, alg):
super(AutofillDialog, self).__init__(None) super(AutofillDialog, self).__init__(None)
self.setupUi(self) self.setupUi(self)
self.mode = None
self.param_index = None
self.alg = alg
self.cmbFillType.currentIndexChanged.connect(self.toggleParameters) self.cmbFillType.currentIndexChanged.connect(self.toggleParameters)
for param in alg.parameterDefinitions(): for param in self.alg.parameterDefinitions():
self.cmbParameters.addItem(param.description()) self.cmbParameters.addItem(param.description())
def toggleParameters(self, index): def toggleParameters(self, index):
@ -60,10 +63,10 @@ class AutofillDialog(BASE, WIDGET):
def accept(self): def accept(self):
self.mode = self.cmbFillType.currentIndex() self.mode = self.cmbFillType.currentIndex()
self.param = self.cmbParameters.currentIndex() self.param_index = self.cmbParameters.currentIndex()
QDialog.accept(self) QDialog.accept(self)
def reject(self): def reject(self):
self.mode = None self.mode = None
self.param = None self.param_index = None
QDialog.reject(self) QDialog.reject(self)

View File

@ -26,11 +26,21 @@ __copyright__ = '(C) 2012, Victor Olaya'
__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'
from pprint import pformat
import time
from qgis.PyQt.QtWidgets import QApplication, QMessageBox, QSizePolicy from qgis.PyQt.QtWidgets import QApplication, QMessageBox, QSizePolicy
from qgis.PyQt.QtGui import QCursor from qgis.PyQt.QtGui import QCursor
from qgis.PyQt.QtCore import Qt 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 qgis.gui import QgsMessageBar
from processing.gui.BatchPanel import BatchPanel 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.AlgorithmExecutor import execute
from processing.gui.Postprocessing import handleAlgorithmResults from processing.gui.Postprocessing import handleAlgorithmResults
from processing.core.ProcessingResults import ProcessingResults from processing.core.ProcessingResults import resultsList
from processing.core.outputs import OutputNumber
from processing.core.outputs import OutputString
from processing.core.outputs import OutputHTML
from processing.tools.system import getTempFilename from processing.tools.system import getTempFilename
from processing.tools import dataobjects from processing.tools import dataobjects
@ -56,7 +62,6 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
AlgorithmDialogBase.__init__(self, alg) AlgorithmDialogBase.__init__(self, alg)
self.alg = alg self.alg = alg
self.alg_parameters = []
self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName())) self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName()))
@ -69,11 +74,11 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
self.layout().insertWidget(0, self.bar) self.layout().insertWidget(0, self.bar)
def accept(self): def accept(self):
self.alg_parameters = [] alg_parameters = []
self.load = [] load = []
self.canceled = False
context = dataobjects.createContext() context = dataobjects.createContext()
feedback = self.createFeedback()
for row in range(self.mainWidget.tblParameters.rowCount()): for row in range(self.mainWidget.tblParameters.rowCount()):
col = 0 col = 0
@ -87,36 +92,36 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format( self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format(
param.description(), row + 1), param.description(), row + 1),
level=QgsMessageBar.WARNING, duration=5) level=QgsMessageBar.WARNING, duration=5)
self.algs = None
return return
col += 1 col += 1
for out in alg.destinationParameterDefinitions(): count_visible_outputs = 0
for out in self.alg.destinationParameterDefinitions():
if out.flags() & QgsProcessingParameterDefinition.FlagHidden: if out.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue continue
count_visible_outputs += 1
widget = self.mainWidget.tblParameters.cellWidget(row, col) widget = self.mainWidget.tblParameters.cellWidget(row, col)
text = widget.getValue() text = widget.getValue()
if text.strip() != '': if param.checkValueIsAcceptable(text, context):
out.value = text 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 col += 1
else: else:
self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format( self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format(
out.description(), row + 1), out.description(), row + 1),
level=QgsMessageBar.WARNING, duration=5) level=QgsMessageBar.WARNING, duration=5)
self.algs = None
return return
self.alg_parameters.append(parameters) 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)
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
self.mainWidget.setEnabled(False) 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 # Make sure the Log tab is visible before executing the algorithm
try: try:
self.tabWidget.setCurrentIndex(1) self.tabWidget.setCurrentIndex(1)
@ -124,45 +129,62 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
except: except:
pass pass
for count, parameters in enumerate(self.alg_parameters): start_time = time.time()
self.setText(self.tr('\nProcessing algorithm {0}/{1}...').format(count + 1, len(self.alg_parameters)))
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('<b>Algorithm {0} starting...</b>').format(self.alg.displayName()), escape_html=False) self.setInfo(self.tr('<b>Algorithm {0} starting...</b>').format(self.alg.displayName()), escape_html=False)
ret, results = execute(self.alg, parameters, context, self.feedback)
if ret and not self.canceled: feedback.pushInfo(self.tr('Input parameters:'))
if self.load[count]: feedback.pushCommandInfo(pformat(parameters))
handleAlgorithmResults(self.alg, context, self.feedback, False) 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) 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: else:
QApplication.restoreOverrideCursor() break
return
self.finish() feedback.pushInfo(self.tr('Batch execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))
def finish(self): handleAlgorithmResults(self.alg, context, feedback, False)
for count, parameters in enumerate(self.alg_parameters):
self.loadHTMLResults(self.alg, count)
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() QApplication.restoreOverrideCursor()
self.mainWidget.setEnabled(True) self.mainWidget.setEnabled(True)
QMessageBox.information(self, self.tr('Batch processing'), QMessageBox.information(self, self.tr('Batch processing'),
self.tr('Batch processing completed')) self.tr('Batch processing completed'))
def loadHTMLResults(self, alg, num): def loadHTMLResults(self, results, num):
for out in alg.outputs: for out in self.alg.outputDefinitions():
if out.flags() & QgsProcessingParameterDefinition.FlagHidden or not out.open: if isinstance(out, QgsProcessingOutputHtml) and out.name() in results and results[out.name()]:
continue resultsList.addResult(icon=self.alg.icon(), name='{} [{}]'.format(out.description(), num),
result=results[out.name()])
if isinstance(out, OutputHTML): def createSummaryTable(self, algorithm_results):
ProcessingResults.addResult(
'{} [{}]'.format(out.description(), num), out.value)
def createSummaryTable(self):
createTable = False createTable = False
for out in self.algs[0].outputs: for out in self.alg.outputDefinitions():
if isinstance(out, (OutputNumber, OutputString)): if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)):
createTable = True createTable = True
break break
@ -171,12 +193,12 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
outputFile = getTempFilename('html') outputFile = getTempFilename('html')
with codecs.open(outputFile, 'w', encoding='utf-8') as f: with codecs.open(outputFile, 'w', encoding='utf-8') as f:
for alg in self.algs: for res in algorithm_results:
f.write('<hr>\n') f.write('<hr>\n')
for out in alg.outputs: for out in self.alg.outputDefinitions():
if isinstance(out, (OutputNumber, OutputString)): if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)) and out.name() in res:
f.write('<p>{}: {}</p>\n'.format(out.description(), out.value)) f.write('<p>{}: {}</p>\n'.format(out.description(), res[out.name()]))
f.write('<hr>\n') f.write('<hr>\n')
ProcessingResults.addResult( resultsList.addResult(self.alg.icon(),
'{} [summary]'.format(self.algs[0].name), outputFile) '{} [summary]'.format(self.alg.name()), outputFile)

View File

@ -36,14 +36,15 @@ from qgis.PyQt.QtGui import QCursor
from qgis.core import (QgsMapLayer, from qgis.core import (QgsMapLayer,
QgsSettings, QgsSettings,
QgsProject, QgsProject,
QgsProcessingUtils) QgsProcessingUtils,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterDefinition,
QgsProcessingParameterTable,
QgsProcessingParameterFeatureSource)
from processing.gui.MultipleInputDialog import MultipleInputDialog 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.gui.ParameterGuiUtils import getFileFilter
from processing.tools import dataobjects from processing.tools import dataobjects
@ -84,7 +85,7 @@ class BatchInputSelectionPanel(QWidget):
def showPopupMenu(self): def showPopupMenu(self):
popupmenu = QMenu() popupmenu = QMenu()
if not (isinstance(self.param, ParameterMultipleInput) and if not (isinstance(self.param, QgsProcessingParameterMultipleLayers) and
self.param.datatype == dataobjects.TYPE_FILE): self.param.datatype == dataobjects.TYPE_FILE):
selectLayerAction = QAction( selectLayerAction = QAction(
self.tr('Select from open layers'), self.pushButton) self.tr('Select from open layers'), self.pushButton)
@ -99,19 +100,22 @@ class BatchInputSelectionPanel(QWidget):
popupmenu.exec_(QCursor.pos()) popupmenu.exec_(QCursor.pos())
def showLayerSelectionDialog(self): def showLayerSelectionDialog(self):
if (isinstance(self.param, ParameterRaster) or layers = []
(isinstance(self.param, ParameterMultipleInput) and if (isinstance(self.param, QgsProcessingParameterRasterLayer) or
self.param.datatype == dataobjects.TYPE_RASTER)): (isinstance(self.param, QgsProcessingParameterMultipleLayers) and
self.param.layerType() == QgsProcessingParameterDefinition.TypeRaster)):
layers = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance()) layers = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance())
elif isinstance(self.param, ParameterTable): elif isinstance(self.param, QgsProcessingParameterTable):
layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance()) layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance())
else: else:
if isinstance(self.param, ParameterVector): datatypes = [QgsProcessingParameterDefinition.TypeVectorAny]
datatype = self.param.datatype if isinstance(self.param, QgsProcessingParameterFeatureSource):
else: datatypes = self.param.dataTypes()
datatype = [self.param.datatype] elif isinstance(self.param, QgsProcessingParameterMultipleLayers):
if datatype != dataobjects.TYPE_VECTOR_ANY: datatypes = [self.param.layerType()]
layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [datatype])
if QgsProcessingParameterDefinition.TypeVectorAny not in datatypes:
layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), datatypes)
else: else:
layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance()) layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance())
@ -120,17 +124,17 @@ class BatchInputSelectionPanel(QWidget):
if dlg.selectedoptions is not None: if dlg.selectedoptions is not None:
selected = dlg.selectedoptions selected = dlg.selectedoptions
if len(selected) == 1: if len(selected) == 1:
self.setValue(layers[selected[0]]) self.setValue(layers[selected[0]].id())
else: else:
if isinstance(self.param, ParameterMultipleInput): if isinstance(self.param, QgsProcessingParameterMultipleLayers):
self.text.setText(';'.join(layers[idx].name() for idx in selected)) self.text.setText(';'.join(layers[idx].id() for idx in selected))
else: else:
rowdif = len(selected) - (self._table().rowCount() - self.row) rowdif = len(selected) - (self._table().rowCount() - self.row)
for i in range(rowdif): for i in range(rowdif):
self._panel().addRow() self._panel().addRow()
for i, layeridx in enumerate(selected): for i, layeridx in enumerate(selected):
self._table().cellWidget(i + self.row, self._table().cellWidget(i + self.row,
self.col).setValue(layers[layeridx]) self.col).setValue(layers[layeridx].id())
def showFileSelectionDialog(self): def showFileSelectionDialog(self):
settings = QgsSettings() settings = QgsSettings()
@ -156,7 +160,7 @@ class BatchInputSelectionPanel(QWidget):
self.text.setText(files[0]) self.text.setText(files[0])
self.textEditingFinished() self.textEditingFinished()
else: else:
if isinstance(self.param, ParameterMultipleInput): if isinstance(self.param, QgsProcessingParameterMultipleLayers):
self.text.setText(';'.join(str(f) for f in files)) self.text.setText(';'.join(str(f) for f in files))
else: else:
rowdif = len(files) - (self._table().rowCount() - self.row) rowdif = len(files) - (self._table().rowCount() - self.row)

View File

@ -31,18 +31,20 @@ __revision__ = '$Format:%H$'
import os import os
import re 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 qgis.PyQt.QtWidgets import QWidget, QPushButton, QLineEdit, QHBoxLayout, QSizePolicy, QFileDialog
from processing.gui.AutofillDialog import AutofillDialog from processing.gui.AutofillDialog import AutofillDialog
from processing.core.parameters import ParameterMultipleInput from processing.gui.ParameterGuiUtils import getFileFilter
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
class BatchOutputSelectionPanel(QWidget): class BatchOutputSelectionPanel(QWidget):
@ -71,11 +73,11 @@ class BatchOutputSelectionPanel(QWidget):
self.setLayout(self.horizontalLayout) self.setLayout(self.horizontalLayout)
def showSelectionDialog(self): def showSelectionDialog(self):
if isinstance(self.output, OutputDirectory): if isinstance(self.output, QgsProcessingParameterFolderOutput):
self.selectDirectory() self.selectDirectory()
return return
filefilter = self.output.getFileFilter(self.alg) filefilter = getFileFilter(self.output)
settings = QgsSettings() settings = QgsSettings()
if settings.contains('/Processing/LastBatchOutputPath'): if settings.contains('/Processing/LastBatchOutputPath'):
path = str(settings.value('/Processing/LastBatchOutputPath')) path = str(settings.value('/Processing/LastBatchOutputPath'))
@ -108,22 +110,23 @@ class BatchOutputSelectionPanel(QWidget):
n = self.table.rowCount() - self.row n = self.table.rowCount() - self.row
for i in range(n): for i in range(n):
widget = self.table.cellWidget(i + self.row, widget = self.table.cellWidget(i + self.row,
dlg.param) dlg.param_index)
param = self.alg.parameters[dlg.param] param = self.alg.parameterDefinitions()[dlg.param_index]
if isinstance(param, (ParameterRaster, if isinstance(param, (QgsProcessingParameterRasterLayer,
ParameterVector, ParameterTable, QgsProcessingParameterFeatureSource,
ParameterMultipleInput)): QgsProcessingParameterTable,
QgsProcessingParameterMultipleLayers)):
v = widget.value() v = widget.value()
if isinstance(v, QgsMapLayer): if isinstance(v, QgsMapLayer):
s = v.name() s = v.name()
else: else:
s = os.path.basename(v) s = os.path.basename(v)
s = os.path.splitext(s)[0] s = os.path.splitext(s)[0]
elif isinstance(param, ParameterBoolean): elif isinstance(param, QgsProcessingParameterBoolean):
s = str(widget.currentIndex() == 0) s = str(widget.currentIndex() == 0)
elif isinstance(param, ParameterSelection): elif isinstance(param, QgsProcessingParameterEnum):
s = str(widget.currentText()) s = str(widget.currentText())
elif isinstance(param, ParameterFixedTable): elif isinstance(param, QgsProcessingParameterMatrix):
s = str(widget.table) s = str(widget.table)
else: else:
s = str(widget.text()) s = str(widget.text())

View File

@ -94,7 +94,7 @@ class BatchPanel(BASE, WIDGET):
def initWidgets(self): def initWidgets(self):
# If there are advanced parameters — show corresponding button # 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: if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced:
self.btnAdvanced.show() self.btnAdvanced.show()
break break
@ -129,8 +129,8 @@ class BatchPanel(BASE, WIDGET):
self.tblParameters.setHorizontalHeaderItem( self.tblParameters.setHorizontalHeaderItem(
column, QTableWidgetItem(self.tr('Load in QGIS'))) column, QTableWidgetItem(self.tr('Load in QGIS')))
# Add three empty rows by default # Add two empty rows by default
for i in range(3): for i in range(2):
self.addRow() self.addRow()
self.tblParameters.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.tblParameters.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)