mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-18 00:03:05 -04:00
This somewhat changes the meaning of outputs from processing 2.x. In 2.x processing outputs were used both as a method of specifying inputs to algorithms (file paths to destination layers created by the algorithm) AND pure outputs (such as statistics calculated by the algorithm). This is now split. The old input-type-outputs (destination layers) are now input parameters (since the parameter value IS an input to the algorithm). To differentiate them from parameters indicating pure input layers a new "isDestination()" method was added to QgsProcessingParameterDefinition. Output definitions are now purely indications of values CREATED by the algorithms. Suitable candidates are the existing calculated stats and actual file path/URI of any layers created by the algorithm. Moving forward we should ensure all algorithms output as much useful information as possible - e.g. number of features processed, number of skipped features, count null geometries encountered, etc...
280 lines
11 KiB
Python
280 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
***************************************************************************
|
|
BatchPanel.py
|
|
---------------------
|
|
Date : November 2014
|
|
Copyright : (C) 2014 by Alexander Bruy
|
|
Email : alexander dot bruy 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. *
|
|
* *
|
|
***************************************************************************
|
|
"""
|
|
from builtins import range
|
|
|
|
__author__ = 'Alexander Bruy'
|
|
__date__ = 'November 2014'
|
|
__copyright__ = '(C) 2014, Alexander Bruy'
|
|
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
|
|
__revision__ = '$Format:%H$'
|
|
|
|
import os
|
|
import json
|
|
|
|
from qgis.PyQt import uic
|
|
from qgis.PyQt.QtWidgets import QTableWidgetItem, QComboBox, QHeaderView, QFileDialog, QMessageBox
|
|
|
|
from qgis.core import QgsApplication
|
|
from qgis.gui import QgsMessageBar
|
|
|
|
from processing.gui.BatchOutputSelectionPanel import BatchOutputSelectionPanel
|
|
|
|
from processing.core.parameters import ParameterFile # NOQA
|
|
from processing.core.parameters import ParameterRaster # NOQA
|
|
from processing.core.parameters import ParameterTable # NOQA
|
|
from processing.core.parameters import ParameterVector # NOQA
|
|
from processing.core.parameters import ParameterExtent # NOQA
|
|
from processing.core.parameters import ParameterCrs # NOQA
|
|
from processing.core.parameters import ParameterPoint # NOQA
|
|
from processing.core.parameters import ParameterSelection # NOQA
|
|
from processing.core.parameters import ParameterFixedTable # NOQA
|
|
from processing.core.parameters import ParameterMultipleInput # NOQA
|
|
|
|
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
|
WIDGET, BASE = uic.loadUiType(
|
|
os.path.join(pluginPath, 'ui', 'widgetBatchPanel.ui'))
|
|
|
|
|
|
class BatchPanel(BASE, WIDGET):
|
|
|
|
PARAMETERS = "PARAMETERS"
|
|
OUTPUTS = "OUTPUTS"
|
|
|
|
def __init__(self, parent, alg):
|
|
super(BatchPanel, self).__init__(None)
|
|
self.setupUi(self)
|
|
|
|
self.wrappers = []
|
|
|
|
self.btnAdvanced.hide()
|
|
|
|
# Set icons
|
|
self.btnAdd.setIcon(QgsApplication.getThemeIcon('/symbologyAdd.svg'))
|
|
self.btnRemove.setIcon(QgsApplication.getThemeIcon('/symbologyRemove.svg'))
|
|
self.btnOpen.setIcon(QgsApplication.getThemeIcon('/mActionFileOpen.svg'))
|
|
self.btnSave.setIcon(QgsApplication.getThemeIcon('/mActionFileSave.svg'))
|
|
self.btnAdvanced.setIcon(QgsApplication.getThemeIcon("/processingAlgorithm.svg"))
|
|
|
|
self.alg = alg
|
|
self.parent = parent
|
|
|
|
self.btnAdd.clicked.connect(self.addRow)
|
|
self.btnRemove.clicked.connect(self.removeRows)
|
|
self.btnOpen.clicked.connect(self.load)
|
|
self.btnSave.clicked.connect(self.save)
|
|
self.btnAdvanced.toggled.connect(self.toggleAdvancedMode)
|
|
self.tblParameters.horizontalHeader().sectionDoubleClicked.connect(
|
|
self.fillParameterValues)
|
|
|
|
self.initWidgets()
|
|
|
|
def layerRegistryChanged(self):
|
|
pass
|
|
|
|
def initWidgets(self):
|
|
# If there are advanced parameters — show corresponding button
|
|
for param in self.alg.parameters:
|
|
if param.isAdvanced:
|
|
self.btnAdvanced.show()
|
|
break
|
|
|
|
# Determine column count
|
|
nOutputs = len(self.alg.destinationParameterDefinitions()) + 1
|
|
if nOutputs == 1:
|
|
nOutputs = 0
|
|
|
|
self.tblParameters.setColumnCount(
|
|
self.alg.countVisibleParameters())
|
|
|
|
# Table headers
|
|
column = 0
|
|
for param in self.alg.parameterDefinitions():
|
|
if param.isDestination():
|
|
continue
|
|
self.tblParameters.setHorizontalHeaderItem(
|
|
column, QTableWidgetItem(param.description))
|
|
if param.isAdvanced:
|
|
self.tblParameters.setColumnHidden(column, True)
|
|
column += 1
|
|
|
|
for out in self.alg.destinationParameterDefinitions():
|
|
if not out.hidden:
|
|
self.tblParameters.setHorizontalHeaderItem(
|
|
column, QTableWidgetItem(out.description))
|
|
column += 1
|
|
|
|
# Last column for indicating if output will be added to canvas
|
|
if len(self.alg.destinationParameterDefinitions()) > 0:
|
|
self.tblParameters.setHorizontalHeaderItem(
|
|
column, QTableWidgetItem(self.tr('Load in QGIS')))
|
|
|
|
# Add three empty rows by default
|
|
for i in range(3):
|
|
self.addRow()
|
|
|
|
self.tblParameters.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
|
self.tblParameters.horizontalHeader().setDefaultSectionSize(250)
|
|
self.tblParameters.horizontalHeader().setMinimumSectionSize(150)
|
|
self.tblParameters.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
|
self.tblParameters.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
|
self.tblParameters.horizontalHeader().setStretchLastSection(True)
|
|
|
|
def load(self):
|
|
filename, selected_filter = QFileDialog.getOpenFileName(self,
|
|
self.tr('Open batch'), None,
|
|
self.tr('JSON files (*.json)'))
|
|
if filename:
|
|
with open(filename) as f:
|
|
values = json.load(f)
|
|
else:
|
|
# If the user clicked on the cancel button.
|
|
return
|
|
|
|
self.tblParameters.setRowCount(0)
|
|
try:
|
|
for row, alg in enumerate(values):
|
|
self.addRow()
|
|
params = alg[self.PARAMETERS]
|
|
outputs = alg[self.OUTPUTS]
|
|
column = 0
|
|
for param in self.alg.parameters:
|
|
if param.hidden:
|
|
continue
|
|
if param.name in params:
|
|
value = params[param.name].strip('"')
|
|
wrapper = self.wrappers[row][column]
|
|
wrapper.setValue(value)
|
|
column += 1
|
|
|
|
for out in self.alg.outputs:
|
|
if out.hidden:
|
|
continue
|
|
if out.name in outputs:
|
|
value = outputs[out.name].strip('"')
|
|
widget = self.tblParameters.cellWidget(row, column)
|
|
widget.setValue(value)
|
|
column += 1
|
|
except TypeError:
|
|
QMessageBox.critical(
|
|
self,
|
|
self.tr('Error'),
|
|
self.tr('An error occurred while reading your file.'))
|
|
|
|
def save(self):
|
|
toSave = []
|
|
for row in range(self.tblParameters.rowCount()):
|
|
algParams = {}
|
|
algOutputs = {}
|
|
col = 0
|
|
alg = self.alg
|
|
for param in alg.parameters:
|
|
if param.hidden:
|
|
continue
|
|
wrapper = self.wrappers[row][col]
|
|
if not self.setParamValue(param, wrapper, alg):
|
|
self.parent.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format(
|
|
param.description, row + 1),
|
|
level=QgsMessageBar.WARNING, duration=5)
|
|
return
|
|
algParams[param.name] = param.getValueAsCommandLineParameter()
|
|
col += 1
|
|
for out in alg.outputs:
|
|
if out.hidden:
|
|
continue
|
|
widget = self.tblParameters.cellWidget(row, col)
|
|
text = widget.getValue()
|
|
if text.strip() != '':
|
|
algOutputs[out.name] = text.strip()
|
|
col += 1
|
|
else:
|
|
self.parent.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format(
|
|
out.description, row + 1),
|
|
level=QgsMessageBar.WARNING, duration=5)
|
|
return
|
|
toSave.append({self.PARAMETERS: algParams, self.OUTPUTS: algOutputs})
|
|
|
|
filename, __ = QFileDialog.getSaveFileName(self,
|
|
self.tr('Save batch'),
|
|
None,
|
|
self.tr('JSON files (*.json)'))
|
|
if filename:
|
|
if not filename.endswith('.json'):
|
|
filename += '.json'
|
|
with open(filename, 'w') as f:
|
|
json.dump(toSave, f)
|
|
|
|
def setParamValue(self, param, wrapper, alg=None):
|
|
return param.setValue(wrapper.value())
|
|
|
|
def setCellWrapper(self, row, column, wrapper):
|
|
self.wrappers[row][column] = wrapper
|
|
self.tblParameters.setCellWidget(row, column, wrapper.widget)
|
|
|
|
def addRow(self):
|
|
self.wrappers.append([None] * self.tblParameters.columnCount())
|
|
self.tblParameters.setRowCount(self.tblParameters.rowCount() + 1)
|
|
|
|
wrappers = {}
|
|
row = self.tblParameters.rowCount() - 1
|
|
column = 0
|
|
for param in self.alg.parameterDefinitions():
|
|
if param.hidden or param.isDestination():
|
|
continue
|
|
|
|
wrapper = param.wrapper(self.parent, row, column)
|
|
wrappers[param.name] = wrapper
|
|
self.setCellWrapper(row, column, wrapper)
|
|
column += 1
|
|
|
|
for out in self.alg.destinationParameterDefinitions():
|
|
if out.hidden:
|
|
continue
|
|
|
|
self.tblParameters.setCellWidget(
|
|
row, column, BatchOutputSelectionPanel(
|
|
out, self.alg, row, column, self))
|
|
column += 1
|
|
|
|
if len(self.alg.destinationParameterDefinitions()) > 0:
|
|
item = QComboBox()
|
|
item.addItem(self.tr('Yes'))
|
|
item.addItem(self.tr('No'))
|
|
item.setCurrentIndex(0)
|
|
self.tblParameters.setCellWidget(row, column, item)
|
|
|
|
for wrapper in list(wrappers.values()):
|
|
wrapper.postInitialize(list(wrappers.values()))
|
|
|
|
def removeRows(self):
|
|
if self.tblParameters.rowCount() > 2:
|
|
self.wrappers.pop()
|
|
self.tblParameters.setRowCount(self.tblParameters.rowCount() - 1)
|
|
|
|
def fillParameterValues(self, column):
|
|
wrapper = self.wrappers[0][column]
|
|
for row in range(1, self.tblParameters.rowCount()):
|
|
self.wrappers[row][column].setValue(wrapper.value())
|
|
|
|
def toggleAdvancedMode(self, checked):
|
|
for column, param in enumerate(self.alg.parameters):
|
|
if param.isAdvanced:
|
|
self.tblParameters.setColumnHidden(column, not checked)
|