Skip to content
Permalink
Browse files
Allow adding layers which aren't open in the project to processing
parameters which accept lists of multiple layers

E.g. build vrt alg, merge vector layers alg

Otherwise you may need to load 100's of layers temporarily into
a project to perform algs on them
  • Loading branch information
nyalldawson committed Aug 16, 2017
1 parent a5a4d3b commit 1f2ea02
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 19 deletions.
@@ -29,12 +29,16 @@

import os

from qgis.core import QgsSettings
from qgis.core import (QgsSettings,
QgsProcessing,
QgsVectorFileWriter,
QgsProviderRegistry)
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtCore import QByteArray
from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox
from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox, QFileDialog
from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem
from processing.tools import dataobjects

pluginPath = os.path.split(os.path.dirname(__file__))[0]
WIDGET, BASE = uic.loadUiType(
@@ -43,9 +47,11 @@

class MultipleInputDialog(BASE, WIDGET):

def __init__(self, options, selectedoptions=None):
def __init__(self, options, selectedoptions=None, datatype=None):
super(MultipleInputDialog, self).__init__(None)
self.setupUi(self)
self.datatype = datatype
self.model = None

self.lstLayers.setSelectionMode(QAbstractItemView.NoSelection)

@@ -68,6 +74,11 @@ def __init__(self, options, selectedoptions=None):
self.btnToggleSelection = QPushButton(self.tr('Toggle selection'))
self.buttonBox.addButton(self.btnToggleSelection,
QDialogButtonBox.ActionRole)
if self.datatype is not None:
btnAddFile = QPushButton(self.tr('Add file(s)…'))
btnAddFile.clicked.connect(self.addFiles)
self.buttonBox.addButton(btnAddFile,
QDialogButtonBox.ActionRole)

self.btnSelectAll.clicked.connect(lambda: self.selectAll(True))
self.btnClearSelection.clicked.connect(lambda: self.selectAll(False))
@@ -83,15 +94,23 @@ def saveWindowGeometry(self):
self.settings.setValue("/Processing/multipleInputDialogGeometry", self.saveGeometry())

def populateList(self):
model = QStandardItemModel()
self.model = QStandardItemModel()
for value, text in self.options:
item = QStandardItem(text)
item.setData(value, Qt.UserRole)
item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked)
item.setCheckable(True)
model.appendRow(item)
self.model.appendRow(item)

self.lstLayers.setModel(model)
# add extra options (e.g. manually added layers)
for t in [o for o in self.selectedoptions if not isinstance(o, int)]:
item = QStandardItem(t)
item.setData(t, Qt.UserRole)
item.setCheckState(Qt.Checked)
item.setCheckable(True)
self.model.appendRow(item)

self.lstLayers.setModel(self.model)

def accept(self):
self.selectedoptions = []
@@ -118,3 +137,38 @@ def toggleSelection(self):
item = model.item(i)
checked = item.checkState() == Qt.Checked
item.setCheckState(Qt.Unchecked if checked else Qt.Checked)

def getFileFilter(self, datatype):
"""
Returns a suitable file filter pattern for the specified parameter definition
:param param:
:return:
"""
if datatype == QgsProcessing.TypeRaster:
return QgsProviderRegistry.instance().fileRasterFilters()
elif datatype == QgsProcessing.TypeFile:
return self.tr('All files (*.*)')
else:
exts = QgsVectorFileWriter.supportedFormatExtensions()
for i in range(len(exts)):
exts[i] = self.tr('{0} files (*.{1})').format(exts[i].upper(), exts[i].lower())
return self.tr('All files (*.*)') + ';;' + ';;'.join(exts)

def addFiles(self):
filter = self.getFileFilter(self.datatype)

settings = QgsSettings()
path = str(settings.value('/Processing/LastInputPath'))

ret, selected_filter = QFileDialog.getOpenFileNames(self, self.tr('Select file(s)'),
path, filter)
if ret:
files = list(ret)
settings.setValue('/Processing/LastInputPath',
os.path.dirname(str(files[0])))
for filename in files:
item = QStandardItem(filename)
item.setData(filename, Qt.UserRole)
item.setCheckState(Qt.Checked)
item.setCheckable(True)
self.model.appendRow(item)
@@ -28,9 +28,10 @@

import os

from qgis.core import QgsProcessing
from qgis.PyQt import uic
from qgis.PyQt.QtCore import pyqtSignal

''
from processing.gui.MultipleInputDialog import MultipleInputDialog
from processing.gui.MultipleFileInputDialog import MultipleFileInputDialog

@@ -63,10 +64,10 @@ def setSelectedItems(self, selected):
self.tr('{0} elements selected').format(len(self.selectedoptions)))

def showSelectionDialog(self):
if self.datatype is None:
dlg = MultipleInputDialog(self.options, self.selectedoptions)
else:
if self.datatype == QgsProcessing.TypeFile:
dlg = MultipleFileInputDialog(self.selectedoptions)
else:
dlg = MultipleInputDialog(self.options, self.selectedoptions, datatype=self.datatype)
dlg.exec_()
if dlg.selectedoptions is not None:
self.selectedoptions = dlg.selectedoptions
@@ -76,12 +77,15 @@ def showSelectionDialog(self):

def updateForOptions(self, options):
selectedoptions = []
selected = [self.options[i] for i in self.selectedoptions]
selected = [self.options[i] if isinstance(i, int) else i for i in self.selectedoptions]
for sel in selected:
try:
idx = options.index(sel)
selectedoptions.append(idx)
except ValueError:
pass
if isinstance(sel, int):
try:
idx = options.index(sel)
selectedoptions.append(idx)
except ValueError:
pass
else:
selectedoptions.append(sel)
self.options = options
self.setSelectedItems(selectedoptions)
@@ -546,7 +546,7 @@ def _getOptions(self):
def createWidget(self):
if self.dialogType == DIALOG_STANDARD:
if self.param.layerType() == QgsProcessing.TypeFile:
return MultipleInputPanel(datatype=dataobjects.TYPE_FILE)
return MultipleInputPanel(datatype=QgsProcessing.TypeFile)
else:
if self.param.layerType() == QgsProcessing.TypeRaster:
options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)
@@ -555,7 +555,7 @@ def createWidget(self):
else:
options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False)
opts = [getExtendedLayerName(opt) for opt in options]
return MultipleInputPanel(opts)
return MultipleInputPanel(opts, datatype=self.param.layerType())
elif self.dialogType == DIALOG_BATCH:
widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog)
widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
@@ -599,7 +599,7 @@ def value(self):
options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
else:
options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False)
return [options[i] for i in self.widget.selectedoptions]
return [options[i] if isinstance(i, int) else i for i in self.widget.selectedoptions]
elif self.dialogType == DIALOG_BATCH:
return self.widget.getText()
else:

0 comments on commit 1f2ea02

Please sign in to comment.