1# -*- coding: utf-8 -*- 2 3""" 4*************************************************************************** 5 MultipleInputDialog.py 6 --------------------- 7 Date : August 2012 8 Copyright : (C) 2012 by Victor Olaya 9 Email : volayaf at gmail dot com 10*************************************************************************** 11* * 12* This program is free software; you can redistribute it and/or modify * 13* it under the terms of the GNU General Public License as published by * 14* the Free Software Foundation; either version 2 of the License, or * 15* (at your option) any later version. * 16* * 17*************************************************************************** 18""" 19 20__author__ = 'Victor Olaya' 21__date__ = 'August 2012' 22__copyright__ = '(C) 2012, Victor Olaya' 23 24import os 25import warnings 26from pathlib import Path 27 28from qgis.core import (QgsSettings, 29 QgsProcessing, 30 QgsVectorFileWriter, 31 QgsProviderRegistry, 32 QgsProcessingModelChildParameterSource) 33from qgis.PyQt import uic 34from qgis.PyQt.QtCore import Qt, QByteArray, QCoreApplication 35from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox, QFileDialog 36from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem 37 38pluginPath = os.path.split(os.path.dirname(__file__))[0] 39with warnings.catch_warnings(): 40 warnings.filterwarnings("ignore", category=DeprecationWarning) 41 WIDGET, BASE = uic.loadUiType( 42 os.path.join(pluginPath, 'ui', 'DlgMultipleSelection.ui')) 43 44 45class MultipleInputDialog(BASE, WIDGET): 46 47 def __init__(self, options, selectedoptions=None, datatype=None): 48 super(MultipleInputDialog, self).__init__(None) 49 self.setupUi(self) 50 self.datatype = datatype 51 self.model = None 52 53 self.options = [] 54 for i, option in enumerate(options): 55 if option is None or isinstance(option, str): 56 self.options.append((i, option)) 57 else: 58 self.options.append((option[0], option[1])) 59 60 self.selectedoptions = selectedoptions or [] 61 62 # Additional buttons 63 self.btnSelectAll = QPushButton(self.tr('Select All')) 64 self.buttonBox.addButton(self.btnSelectAll, 65 QDialogButtonBox.ActionRole) 66 self.btnClearSelection = QPushButton(self.tr('Clear Selection')) 67 self.buttonBox.addButton(self.btnClearSelection, 68 QDialogButtonBox.ActionRole) 69 self.btnToggleSelection = QPushButton(self.tr('Toggle Selection')) 70 self.buttonBox.addButton(self.btnToggleSelection, 71 QDialogButtonBox.ActionRole) 72 if self.datatype is not None: 73 btnAddFile = QPushButton(QCoreApplication.translate("MultipleInputDialog", 'Add File(s)…')) 74 btnAddFile.clicked.connect(self.addFiles) 75 self.buttonBox.addButton(btnAddFile, 76 QDialogButtonBox.ActionRole) 77 78 btnAddDir = QPushButton(QCoreApplication.translate("MultipleInputDialog", 'Add Directory…')) 79 btnAddDir.clicked.connect(self.addDirectory) 80 self.buttonBox.addButton(btnAddDir, 81 QDialogButtonBox.ActionRole) 82 83 self.btnSelectAll.clicked.connect(lambda: self.selectAll(True)) 84 self.btnClearSelection.clicked.connect(lambda: self.selectAll(False)) 85 self.btnToggleSelection.clicked.connect(self.toggleSelection) 86 87 self.settings = QgsSettings() 88 self.restoreGeometry(self.settings.value("/Processing/multipleInputDialogGeometry", QByteArray())) 89 90 self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection) 91 self.lstLayers.setDragDropMode(QAbstractItemView.InternalMove) 92 93 self.populateList() 94 self.finished.connect(self.saveWindowGeometry) 95 96 def saveWindowGeometry(self): 97 self.settings.setValue("/Processing/multipleInputDialogGeometry", self.saveGeometry()) 98 99 def populateList(self): 100 self.model = QStandardItemModel() 101 for value, text in self.options: 102 item = QStandardItem(text) 103 item.setData(value, Qt.UserRole) 104 item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked) 105 item.setCheckable(True) 106 item.setDropEnabled(False) 107 self.model.appendRow(item) 108 109 # add extra options (e.g. manually added layers) 110 for t in [o for o in self.selectedoptions if not isinstance(o, int)]: 111 if isinstance(t, QgsProcessingModelChildParameterSource): 112 item = QStandardItem(t.staticValue()) 113 else: 114 item = QStandardItem(t) 115 item.setData(item.text(), Qt.UserRole) 116 item.setCheckState(Qt.Checked) 117 item.setCheckable(True) 118 item.setDropEnabled(False) 119 self.model.appendRow(item) 120 121 self.lstLayers.setModel(self.model) 122 123 def accept(self): 124 self.selectedoptions = [] 125 model = self.lstLayers.model() 126 for i in range(model.rowCount()): 127 item = model.item(i) 128 if item.checkState() == Qt.Checked: 129 self.selectedoptions.append(item.data(Qt.UserRole)) 130 QDialog.accept(self) 131 132 def reject(self): 133 self.selectedoptions = None 134 QDialog.reject(self) 135 136 def getItemsToModify(self): 137 items = [] 138 if len(self.lstLayers.selectedIndexes()) > 1: 139 for i in self.lstLayers.selectedIndexes(): 140 items.append(self.model.itemFromIndex(i)) 141 else: 142 for i in range(self.model.rowCount()): 143 items.append(self.model.item(i)) 144 return items 145 146 def selectAll(self, value): 147 for item in self.getItemsToModify(): 148 item.setCheckState(Qt.Checked if value else Qt.Unchecked) 149 150 def toggleSelection(self): 151 for item in self.getItemsToModify(): 152 checked = item.checkState() == Qt.Checked 153 item.setCheckState(Qt.Unchecked if checked else Qt.Checked) 154 155 def getFileFilter(self, datatype): 156 """ 157 Returns a suitable file filter pattern for the specified parameter definition 158 :param param: 159 :return: 160 """ 161 if datatype == QgsProcessing.TypeRaster: 162 return QgsProviderRegistry.instance().fileRasterFilters() 163 elif datatype == QgsProcessing.TypeFile: 164 return self.tr('All files (*.*)') 165 else: 166 exts = QgsVectorFileWriter.supportedFormatExtensions() 167 for i in range(len(exts)): 168 exts[i] = self.tr('{0} files (*.{1})').format(exts[i].upper(), exts[i].lower()) 169 return self.tr('All files (*.*)') + ';;' + ';;'.join(exts) 170 171 def addFiles(self): 172 filter = self.getFileFilter(self.datatype) 173 174 settings = QgsSettings() 175 path = str(settings.value('/Processing/LastInputPath')) 176 177 ret, selected_filter = QFileDialog.getOpenFileNames(self, self.tr('Select File(s)'), 178 path, filter) 179 if ret: 180 files = list(ret) 181 settings.setValue('/Processing/LastInputPath', 182 os.path.dirname(str(files[0]))) 183 for filename in files: 184 item = QStandardItem(filename) 185 item.setData(filename, Qt.UserRole) 186 item.setCheckState(Qt.Checked) 187 item.setCheckable(True) 188 item.setDropEnabled(False) 189 self.model.appendRow(item) 190 191 def addDirectory(self): 192 settings = QgsSettings() 193 path = str(settings.value('/Processing/LastInputPath')) 194 195 ret = QFileDialog.getExistingDirectory(self, self.tr('Select File(s)'), path) 196 if ret: 197 exts = [] 198 199 if self.datatype == QgsProcessing.TypeVector: 200 exts = QgsVectorFileWriter.supportedFormatExtensions() 201 elif self.datatype == QgsProcessing.TypeRaster: 202 for t in QgsProviderRegistry.instance().fileRasterFilters().split(';;')[1:]: 203 for e in t[t.index('(') + 1:-1].split(' '): 204 if e != "*.*" and e.startswith("*."): 205 exts.append(e[2:]) 206 207 files = [] 208 for pp in Path(ret).rglob("*"): 209 if not pp.is_file(): 210 continue 211 212 if exts and pp.suffix[1:] not in exts: 213 continue 214 215 p = pp.as_posix() 216 217 files.append(p) 218 219 settings.setValue('/Processing/LastInputPath', ret) 220 221 for filename in files: 222 item = QStandardItem(filename) 223 item.setData(filename, Qt.UserRole) 224 item.setCheckState(Qt.Checked) 225 item.setCheckable(True) 226 item.setDropEnabled(False) 227 self.model.appendRow(item) 228