QGIS/python/plugins/processing/gui/BatchInputSelectionPanel.py
2024-11-29 15:38:02 +01:00

281 lines
10 KiB
Python

"""
***************************************************************************
BatchInputSelectionPanel.py
---------------------
Date : August 2012
Copyright : (C) 2012 by Victor Olaya
Email : volayaf 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. *
* *
***************************************************************************
"""
__author__ = "Victor Olaya"
__date__ = "August 2012"
__copyright__ = "(C) 2012, Victor Olaya"
import os
from pathlib import Path
from qgis.PyQt.QtCore import pyqtSignal, QCoreApplication
from qgis.PyQt.QtWidgets import (
QWidget,
QHBoxLayout,
QMenu,
QPushButton,
QLineEdit,
QSizePolicy,
QAction,
QFileDialog,
)
from qgis.PyQt.QtGui import QCursor
from qgis.core import (
QgsMapLayer,
QgsRasterLayer,
QgsSettings,
QgsProject,
QgsProcessing,
QgsProcessingUtils,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMeshLayer,
QgsProcessingParameterPointCloudLayer,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterMapLayer,
)
from processing.gui.MultipleInputDialog import MultipleInputDialog
from processing.tools import dataobjects
class BatchInputSelectionPanel(QWidget):
valueChanged = pyqtSignal()
def __init__(self, param, row, col, dialog):
super().__init__(None)
self.param = param
self.dialog = dialog
self.row = row
self.col = col
self.horizontalLayout = QHBoxLayout(self)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setMargin(0)
self.text = QLineEdit()
self.text.setObjectName("text")
self.text.setMinimumWidth(300)
self.setValue("")
self.text.editingFinished.connect(self.textEditingFinished)
self.text.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
)
self.horizontalLayout.addWidget(self.text)
self.pushButton = QPushButton()
self.pushButton.setText("")
self.pushButton.clicked.connect(self.showPopupMenu)
self.horizontalLayout.addWidget(self.pushButton)
self.setLayout(self.horizontalLayout)
def _panel(self):
return self.dialog.mainWidget()
def _table(self):
return self._panel().tblParameters
def showPopupMenu(self):
popupmenu = QMenu()
if not (
isinstance(self.param, QgsProcessingParameterMultipleLayers)
and self.param.layerType == dataobjects.TYPE_FILE
):
selectLayerAction = QAction(
QCoreApplication.translate(
"BatchInputSelectionPanel", "Select from Open Layers…"
),
self.pushButton,
)
selectLayerAction.triggered.connect(self.showLayerSelectionDialog)
popupmenu.addAction(selectLayerAction)
selectFileAction = QAction(
QCoreApplication.translate("BatchInputSelectionPanel", "Select Files…"),
self.pushButton,
)
selectFileAction.triggered.connect(self.showFileSelectionDialog)
popupmenu.addAction(selectFileAction)
selectDirectoryAction = QAction(
QCoreApplication.translate("BatchInputSelectionPanel", "Select Directory…"),
self.pushButton,
)
selectDirectoryAction.triggered.connect(self.showDirectorySelectionDialog)
popupmenu.addAction(selectDirectoryAction)
popupmenu.exec(QCursor.pos())
def showLayerSelectionDialog(self):
layers = []
if isinstance(self.param, QgsProcessingParameterRasterLayer) or (
isinstance(self.param, QgsProcessingParameterMultipleLayers)
and self.param.layerType() == QgsProcessing.SourceType.TypeRaster
):
layers = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance())
elif isinstance(self.param, QgsProcessingParameterVectorLayer):
layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance())
elif isinstance(self.param, QgsProcessingParameterMapLayer):
layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
elif isinstance(self.param, QgsProcessingParameterMeshLayer) or (
isinstance(self.param, QgsProcessingParameterMultipleLayers)
and self.param.layerType() == QgsProcessing.SourceType.TypeMesh
):
layers = QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance())
elif isinstance(self.param, QgsProcessingParameterPointCloudLayer) or (
isinstance(self.param, QgsProcessingParameterMultipleLayers)
and self.param.layerType() == QgsProcessing.SourceType.TypePointCloud
):
layers = QgsProcessingUtils.compatiblePointCloudLayers(
QgsProject.instance()
)
else:
datatypes = [QgsProcessing.SourceType.TypeVectorAnyGeometry]
if isinstance(self.param, QgsProcessingParameterFeatureSource):
datatypes = self.param.dataTypes()
elif isinstance(self.param, QgsProcessingParameterMultipleLayers):
datatypes = [self.param.layerType()]
if QgsProcessing.SourceType.TypeVectorAnyGeometry not in datatypes:
layers = QgsProcessingUtils.compatibleVectorLayers(
QgsProject.instance(), datatypes
)
else:
layers = QgsProcessingUtils.compatibleVectorLayers(
QgsProject.instance()
)
dlg = MultipleInputDialog([layer.name() for layer in layers])
dlg.exec()
def generate_layer_id(layer):
# prefer layer name if unique
if (
len([l for l in layers if l.name().lower() == layer.name().lower()])
== 1
):
return layer.name()
else:
# otherwise fall back to layer id
return layer.id()
if dlg.selectedoptions is not None:
selected = dlg.selectedoptions
if len(selected) == 1:
self.setValue(generate_layer_id(layers[selected[0]]))
else:
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(
generate_layer_id(layers[layeridx])
)
def showFileSelectionDialog(self):
self.showFileDialog(seldir=False)
def showDirectorySelectionDialog(self):
self.showFileDialog(seldir=True)
def showFileDialog(self, seldir):
settings = QgsSettings()
text = str(self.text.text())
if os.path.isdir(text):
path = text
elif not seldir and os.path.isdir(os.path.dirname(text)):
path = os.path.dirname(text)
elif settings.contains("/Processing/LastInputPath"):
path = str(settings.value("/Processing/LastInputPath"))
else:
path = ""
if not seldir:
ret, selected_filter = QFileDialog.getOpenFileNames(
self, self.tr("Select Files"), path, self.param.createFileFilter()
)
else:
ret = QFileDialog.getExistingDirectory(
self, self.tr("Select Directory"), path
)
if ret:
if seldir:
settings.setValue("/Processing/LastInputPath", ret)
files = []
for pp in Path(ret).rglob("*"):
if not pp.is_file():
continue
p = pp.as_posix()
if (
isinstance(self.param, QgsProcessingParameterRasterLayer)
or (
isinstance(self.param, QgsProcessingParameterMultipleLayers)
and self.param.layerType()
== QgsProcessing.SourceType.TypeRaster
)
) and not QgsRasterLayer.isValidRasterFileName(p):
continue
files.append(p)
if not files:
return
else:
files = list(ret)
settings.setValue(
"/Processing/LastInputPath", os.path.dirname(str(files[0]))
)
for i, filename in enumerate(files):
files[i] = dataobjects.getRasterSublayer(filename, self.param)
if len(files) == 1:
self.text.setText(files[0])
self.textEditingFinished()
else:
if isinstance(self.param, QgsProcessingParameterMultipleLayers):
self.text.setText(";".join(str(f) for f in files))
else:
rowdif = len(files) - (self._table().rowCount() - self.row)
for i in range(rowdif):
self._panel().addRow()
for i, f in enumerate(files):
self._table().cellWidget(i + self.row, self.col).setValue(f)
def textEditingFinished(self):
self._value = self.text.text()
self.valueChanged.emit()
def getValue(self):
return self._value if self._value else None
def setValue(self, value):
self._value = value
if isinstance(value, QgsMapLayer):
self.text.setText(value.name())
else: # should be basestring
self.text.setText(value)
self.valueChanged.emit()