BatchInputSelectionPanel.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. """
  2. ***************************************************************************
  3. BatchInputSelectionPanel.py
  4. ---------------------
  5. Date : August 2012
  6. Copyright : (C) 2012 by Victor Olaya
  7. Email : volayaf at gmail dot com
  8. ***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************
  16. """
  17. __author__ = 'Victor Olaya'
  18. __date__ = 'August 2012'
  19. __copyright__ = '(C) 2012, Victor Olaya'
  20. import os
  21. from pathlib import Path
  22. from qgis.PyQt.QtCore import pyqtSignal, QCoreApplication
  23. from qgis.PyQt.QtWidgets import QWidget, QHBoxLayout, QMenu, QPushButton, QLineEdit, QSizePolicy, QAction, QFileDialog
  24. from qgis.PyQt.QtGui import QCursor
  25. from qgis.core import (QgsMapLayer,
  26. QgsRasterLayer,
  27. QgsSettings,
  28. QgsProject,
  29. QgsProcessing,
  30. QgsProcessingUtils,
  31. QgsProcessingParameterMultipleLayers,
  32. QgsProcessingParameterRasterLayer,
  33. QgsProcessingParameterVectorLayer,
  34. QgsProcessingParameterMeshLayer,
  35. QgsProcessingParameterPointCloudLayer,
  36. QgsProcessingParameterFeatureSource,
  37. QgsProcessingParameterMapLayer)
  38. from processing.gui.MultipleInputDialog import MultipleInputDialog
  39. from processing.tools import dataobjects
  40. class BatchInputSelectionPanel(QWidget):
  41. valueChanged = pyqtSignal()
  42. def __init__(self, param, row, col, dialog):
  43. super().__init__(None)
  44. self.param = param
  45. self.dialog = dialog
  46. self.row = row
  47. self.col = col
  48. self.horizontalLayout = QHBoxLayout(self)
  49. self.horizontalLayout.setSpacing(0)
  50. self.horizontalLayout.setMargin(0)
  51. self.text = QLineEdit()
  52. self.text.setObjectName('text')
  53. self.text.setMinimumWidth(300)
  54. self.setValue('')
  55. self.text.editingFinished.connect(self.textEditingFinished)
  56. self.text.setSizePolicy(QSizePolicy.Expanding,
  57. QSizePolicy.Expanding)
  58. self.horizontalLayout.addWidget(self.text)
  59. self.pushButton = QPushButton()
  60. self.pushButton.setText('…')
  61. self.pushButton.clicked.connect(self.showPopupMenu)
  62. self.horizontalLayout.addWidget(self.pushButton)
  63. self.setLayout(self.horizontalLayout)
  64. def _panel(self):
  65. return self.dialog.mainWidget()
  66. def _table(self):
  67. return self._panel().tblParameters
  68. def showPopupMenu(self):
  69. popupmenu = QMenu()
  70. if not (isinstance(self.param, QgsProcessingParameterMultipleLayers)
  71. and self.param.layerType == dataobjects.TYPE_FILE):
  72. selectLayerAction = QAction(
  73. QCoreApplication.translate('BatchInputSelectionPanel', 'Select from Open Layers…'), self.pushButton)
  74. selectLayerAction.triggered.connect(self.showLayerSelectionDialog)
  75. popupmenu.addAction(selectLayerAction)
  76. selectFileAction = QAction(
  77. QCoreApplication.translate('BatchInputSelectionPanel', 'Select Files…'), self.pushButton)
  78. selectFileAction.triggered.connect(self.showFileSelectionDialog)
  79. popupmenu.addAction(selectFileAction)
  80. selectDirectoryAction = QAction(
  81. QCoreApplication.translate('BatchInputSelectionPanel', 'Select Directory…'), self.pushButton)
  82. selectDirectoryAction.triggered.connect(self.showDirectorySelectionDialog)
  83. popupmenu.addAction(selectDirectoryAction)
  84. popupmenu.exec_(QCursor.pos())
  85. def showLayerSelectionDialog(self):
  86. layers = []
  87. if (isinstance(self.param, QgsProcessingParameterRasterLayer)
  88. or (isinstance(self.param, QgsProcessingParameterMultipleLayers) and
  89. self.param.layerType() == QgsProcessing.TypeRaster)):
  90. layers = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance())
  91. elif isinstance(self.param, QgsProcessingParameterVectorLayer):
  92. layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance())
  93. elif isinstance(self.param, QgsProcessingParameterMapLayer):
  94. layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
  95. elif (isinstance(self.param, QgsProcessingParameterMeshLayer)
  96. or (isinstance(self.param, QgsProcessingParameterMultipleLayers) and
  97. self.param.layerType() == QgsProcessing.TypeMesh)):
  98. layers = QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance())
  99. elif (isinstance(self.param, QgsProcessingParameterPointCloudLayer)
  100. or (isinstance(self.param, QgsProcessingParameterMultipleLayers) and
  101. self.param.layerType() == QgsProcessing.TypePointCloud)):
  102. layers = QgsProcessingUtils.compatiblePointCloudLayers(QgsProject.instance())
  103. else:
  104. datatypes = [QgsProcessing.TypeVectorAnyGeometry]
  105. if isinstance(self.param, QgsProcessingParameterFeatureSource):
  106. datatypes = self.param.dataTypes()
  107. elif isinstance(self.param, QgsProcessingParameterMultipleLayers):
  108. datatypes = [self.param.layerType()]
  109. if QgsProcessing.TypeVectorAnyGeometry not in datatypes:
  110. layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), datatypes)
  111. else:
  112. layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance())
  113. dlg = MultipleInputDialog([layer.name() for layer in layers])
  114. dlg.exec_()
  115. def generate_layer_id(layer):
  116. # prefer layer name if unique
  117. if len([l for l in layers if l.name().lower() == layer.name().lower()]) == 1:
  118. return layer.name()
  119. else:
  120. # otherwise fall back to layer id
  121. return layer.id()
  122. if dlg.selectedoptions is not None:
  123. selected = dlg.selectedoptions
  124. if len(selected) == 1:
  125. self.setValue(generate_layer_id(layers[selected[0]]))
  126. else:
  127. if isinstance(self.param, QgsProcessingParameterMultipleLayers):
  128. self.text.setText(';'.join(layers[idx].id() for idx in selected))
  129. else:
  130. rowdif = len(selected) - (self._table().rowCount() - self.row)
  131. for i in range(rowdif):
  132. self._panel().addRow()
  133. for i, layeridx in enumerate(selected):
  134. self._table().cellWidget(i + self.row,
  135. self.col).setValue(generate_layer_id(layers[layeridx]))
  136. def showFileSelectionDialog(self):
  137. self.showFileDialog(seldir=False)
  138. def showDirectorySelectionDialog(self):
  139. self.showFileDialog(seldir=True)
  140. def showFileDialog(self, seldir):
  141. settings = QgsSettings()
  142. text = str(self.text.text())
  143. if os.path.isdir(text):
  144. path = text
  145. elif not seldir and os.path.isdir(os.path.dirname(text)):
  146. path = os.path.dirname(text)
  147. elif settings.contains('/Processing/LastInputPath'):
  148. path = str(settings.value('/Processing/LastInputPath'))
  149. else:
  150. path = ''
  151. if not seldir:
  152. ret, selected_filter = QFileDialog.getOpenFileNames(
  153. self, self.tr('Select Files'), path, self.param.createFileFilter()
  154. )
  155. else:
  156. ret = QFileDialog.getExistingDirectory(self, self.tr('Select Directory'), path)
  157. if ret:
  158. if seldir:
  159. settings.setValue('/Processing/LastInputPath', ret)
  160. files = []
  161. for pp in Path(ret).rglob("*"):
  162. if not pp.is_file():
  163. continue
  164. p = pp.as_posix()
  165. if ((isinstance(self.param, QgsProcessingParameterRasterLayer)
  166. or (isinstance(self.param, QgsProcessingParameterMultipleLayers) and self.param.layerType() == QgsProcessing.TypeRaster)) and
  167. not QgsRasterLayer.isValidRasterFileName(p)):
  168. continue
  169. files.append(p)
  170. if not files:
  171. return
  172. else:
  173. files = list(ret)
  174. settings.setValue('/Processing/LastInputPath', os.path.dirname(str(files[0])))
  175. for i, filename in enumerate(files):
  176. files[i] = dataobjects.getRasterSublayer(filename, self.param)
  177. if len(files) == 1:
  178. self.text.setText(files[0])
  179. self.textEditingFinished()
  180. else:
  181. if isinstance(self.param, QgsProcessingParameterMultipleLayers):
  182. self.text.setText(';'.join(str(f) for f in files))
  183. else:
  184. rowdif = len(files) - (self._table().rowCount() - self.row)
  185. for i in range(rowdif):
  186. self._panel().addRow()
  187. for i, f in enumerate(files):
  188. self._table().cellWidget(i + self.row,
  189. self.col).setValue(f)
  190. def textEditingFinished(self):
  191. self._value = self.text.text()
  192. self.valueChanged.emit()
  193. def getValue(self):
  194. return self._value if self._value else None
  195. def setValue(self, value):
  196. self._value = value
  197. if isinstance(value, QgsMapLayer):
  198. self.text.setText(value.name())
  199. else: # should be basestring
  200. self.text.setText(value)
  201. self.valueChanged.emit()