Skip to content

Commit

Permalink
Allow adding layers which aren't open in the project to processing
Browse files Browse the repository at this point in the history
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.
66 changes: 60 additions & 6 deletions python/plugins/processing/gui/MultipleInputDialog.py
Expand Up @@ -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(
Expand All @@ -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)

Expand All @@ -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))
Expand All @@ -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 = []
Expand All @@ -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)
24 changes: 14 additions & 10 deletions python/plugins/processing/gui/MultipleInputPanel.py
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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)
6 changes: 3 additions & 3 deletions python/plugins/processing/gui/wrappers.py
Expand Up @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 1f2ea02

Please sign in to comment.