wrappers.py 84 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947
  1. """
  2. ***************************************************************************
  3. wrappers.py - Standard parameters widget wrappers
  4. ---------------------
  5. Date : May 2016
  6. Copyright : (C) 2016 by Arnaud Morvan, Victor Olaya
  7. Email : arnaud dot morvan at camptocamp dot com
  8. volayaf at gmail dot com
  9. ***************************************************************************
  10. * *
  11. * This program is free software; you can redistribute it and/or modify *
  12. * it under the terms of the GNU General Public License as published by *
  13. * the Free Software Foundation; either version 2 of the License, or *
  14. * (at your option) any later version. *
  15. * *
  16. ***************************************************************************
  17. """
  18. __author__ = 'Arnaud Morvan'
  19. __date__ = 'May 2016'
  20. __copyright__ = '(C) 2016, Arnaud Morvan'
  21. import os
  22. import re
  23. from inspect import isclass
  24. from copy import deepcopy
  25. from qgis.core import (
  26. QgsApplication,
  27. QgsCoordinateReferenceSystem,
  28. QgsExpression,
  29. QgsFieldProxyModel,
  30. QgsSettings,
  31. QgsProject,
  32. QgsMapLayerType,
  33. QgsVectorLayer,
  34. QgsProcessing,
  35. QgsProcessingUtils,
  36. QgsProcessingParameterDefinition,
  37. QgsProcessingParameterBoolean,
  38. QgsProcessingParameterCrs,
  39. QgsProcessingParameterExtent,
  40. QgsProcessingParameterPoint,
  41. QgsProcessingParameterFile,
  42. QgsProcessingParameterMultipleLayers,
  43. QgsProcessingParameterNumber,
  44. QgsProcessingParameterRasterLayer,
  45. QgsProcessingParameterEnum,
  46. QgsProcessingParameterString,
  47. QgsProcessingParameterExpression,
  48. QgsProcessingParameterVectorLayer,
  49. QgsProcessingParameterMeshLayer,
  50. QgsProcessingParameterField,
  51. QgsProcessingParameterFeatureSource,
  52. QgsProcessingParameterMapLayer,
  53. QgsProcessingParameterBand,
  54. QgsProcessingParameterMatrix,
  55. QgsProcessingParameterDistance,
  56. QgsProcessingParameterDuration,
  57. QgsProcessingFeatureSourceDefinition,
  58. QgsProcessingOutputRasterLayer,
  59. QgsProcessingOutputVectorLayer,
  60. QgsProcessingOutputMapLayer,
  61. QgsProcessingOutputMultipleLayers,
  62. QgsProcessingOutputFile,
  63. QgsProcessingOutputString,
  64. QgsProcessingOutputNumber,
  65. QgsProcessingModelChildParameterSource,
  66. NULL,
  67. Qgis)
  68. from qgis.PyQt.QtWidgets import (
  69. QCheckBox,
  70. QComboBox,
  71. QLabel,
  72. QDialog,
  73. QFileDialog,
  74. QHBoxLayout,
  75. QLineEdit,
  76. QPlainTextEdit,
  77. QToolButton,
  78. QWidget,
  79. QSizePolicy
  80. )
  81. from qgis.PyQt.QtGui import QIcon
  82. from qgis.gui import (
  83. QgsGui,
  84. QgsExpressionLineEdit,
  85. QgsExpressionBuilderDialog,
  86. QgsFieldComboBox,
  87. QgsFieldExpressionWidget,
  88. QgsProjectionSelectionDialog,
  89. QgsMapLayerComboBox,
  90. QgsProjectionSelectionWidget,
  91. QgsRasterBandComboBox,
  92. QgsProcessingGui,
  93. QgsAbstractProcessingParameterWidgetWrapper,
  94. QgsProcessingMapLayerComboBox
  95. )
  96. from qgis.PyQt.QtCore import QVariant, Qt
  97. from qgis.utils import iface
  98. from processing.core.ProcessingConfig import ProcessingConfig
  99. from processing.modeler.MultilineTextPanel import MultilineTextPanel
  100. from processing.gui.NumberInputPanel import NumberInputPanel, ModelerNumberInputPanel, DistanceInputPanel
  101. from processing.gui.RangePanel import RangePanel
  102. from processing.gui.PointSelectionPanel import PointSelectionPanel
  103. from processing.gui.FileSelectionPanel import FileSelectionPanel
  104. from processing.gui.CheckboxesPanel import CheckboxesPanel
  105. from processing.gui.MultipleInputPanel import MultipleInputPanel
  106. from processing.gui.BatchInputSelectionPanel import BatchInputSelectionPanel
  107. from processing.gui.FixedTablePanel import FixedTablePanel
  108. from processing.gui.ExtentSelectionPanel import ExtentSelectionPanel
  109. from processing.tools import dataobjects
  110. DIALOG_STANDARD = QgsProcessingGui.Standard
  111. DIALOG_BATCH = QgsProcessingGui.Batch
  112. DIALOG_MODELER = QgsProcessingGui.Modeler
  113. pluginPath = os.path.split(os.path.dirname(__file__))[0]
  114. class InvalidParameterValue(Exception):
  115. pass
  116. dialogTypes = {"AlgorithmDialog": DIALOG_STANDARD,
  117. "ModelerParametersDialog": DIALOG_MODELER,
  118. "BatchAlgorithmDialog": DIALOG_BATCH}
  119. def getExtendedLayerName(layer):
  120. authid = layer.crs().authid()
  121. if ProcessingConfig.getSetting(ProcessingConfig.SHOW_CRS_DEF) and authid is not None:
  122. return f'{layer.name()} [{authid}]'
  123. else:
  124. return layer.name()
  125. class WidgetWrapper(QgsAbstractProcessingParameterWidgetWrapper):
  126. NOT_SET_OPTION = '~~~~!!!!NOT SET!!!!~~~~~~~'
  127. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  128. self.dialogType = dialogTypes.get(dialog.__class__.__name__, QgsProcessingGui.Standard)
  129. super().__init__(param, self.dialogType)
  130. self.dialog = dialog
  131. self.row = row
  132. self.col = col
  133. self.widget = self.createWidget(**kwargs)
  134. self.label = self.createLabel()
  135. if param.defaultValue() is not None:
  136. self.setValue(param.defaultValue())
  137. def comboValue(self, validator=None, combobox=None):
  138. if combobox is None:
  139. combobox = self.widget
  140. idx = combobox.findText(combobox.currentText())
  141. if idx < 0:
  142. v = combobox.currentText().strip()
  143. if validator is not None and not validator(v):
  144. raise InvalidParameterValue()
  145. return v
  146. if combobox.currentData() == self.NOT_SET_OPTION:
  147. return None
  148. elif combobox.currentData() is not None:
  149. return combobox.currentData()
  150. else:
  151. return combobox.currentText()
  152. def createWidget(self, **kwargs):
  153. pass
  154. def createLabel(self):
  155. if self.dialogType == DIALOG_BATCH:
  156. return None
  157. desc = self.parameterDefinition().description()
  158. if isinstance(self.parameterDefinition(), QgsProcessingParameterExtent):
  159. desc += self.tr(' (xmin, xmax, ymin, ymax)')
  160. if isinstance(self.parameterDefinition(), QgsProcessingParameterPoint):
  161. desc += self.tr(' (x, y)')
  162. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  163. desc += self.tr(' [optional]')
  164. label = QLabel(desc)
  165. label.setToolTip(self.parameterDefinition().name())
  166. return label
  167. def setValue(self, value):
  168. pass
  169. def value(self):
  170. return None
  171. def widgetValue(self):
  172. return self.value()
  173. def setWidgetValue(self, value, context):
  174. self.setValue(value)
  175. def setComboValue(self, value, combobox=None):
  176. if combobox is None:
  177. combobox = self.widget
  178. if isinstance(value, list):
  179. if value:
  180. value = value[0]
  181. else:
  182. value = None
  183. values = [combobox.itemData(i) for i in range(combobox.count())]
  184. try:
  185. idx = values.index(value)
  186. combobox.setCurrentIndex(idx)
  187. return
  188. except ValueError:
  189. pass
  190. if combobox.isEditable():
  191. if value is not None:
  192. combobox.setEditText(str(value))
  193. else:
  194. combobox.setCurrentIndex(0)
  195. def refresh(self):
  196. pass
  197. def getFileName(self, initial_value=''):
  198. """Shows a file open dialog"""
  199. settings = QgsSettings()
  200. if os.path.isdir(initial_value):
  201. path = initial_value
  202. elif os.path.isdir(os.path.dirname(initial_value)):
  203. path = os.path.dirname(initial_value)
  204. elif settings.contains('/Processing/LastInputPath'):
  205. path = str(settings.value('/Processing/LastInputPath'))
  206. else:
  207. path = ''
  208. # TODO: should use selectedFilter argument for default file format
  209. filename, selected_filter = QFileDialog.getOpenFileName(self.widget, self.tr('Select File'),
  210. path, self.parameterDefinition().createFileFilter())
  211. if filename:
  212. settings.setValue('/Processing/LastInputPath',
  213. os.path.dirname(str(filename)))
  214. return filename, selected_filter
  215. class BasicWidgetWrapper(WidgetWrapper):
  216. def createWidget(self):
  217. return QLineEdit()
  218. def setValue(self, value):
  219. self.widget.setText(value)
  220. def value(self):
  221. return self.widget.text()
  222. class BooleanWidgetWrapper(WidgetWrapper):
  223. def __init__(self, *args, **kwargs):
  224. super().__init__(*args, **kwargs)
  225. """
  226. .. deprecated:: 3.4
  227. Do not use, will be removed in QGIS 4.0
  228. """
  229. from warnings import warn
  230. warn("BooleanWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  231. def createLabel(self):
  232. if self.dialogType == DIALOG_STANDARD:
  233. return None
  234. else:
  235. return super().createLabel()
  236. def createWidget(self):
  237. if self.dialogType == DIALOG_STANDARD:
  238. return QCheckBox()
  239. elif self.dialogType == DIALOG_BATCH:
  240. widget = QComboBox()
  241. widget.addItem(self.tr('Yes'), True)
  242. widget.addItem(self.tr('No'), False)
  243. return widget
  244. else:
  245. widget = QComboBox()
  246. widget.addItem(self.tr('Yes'), True)
  247. widget.addItem(self.tr('No'), False)
  248. bools = self.dialog.getAvailableValuesOfType(QgsProcessingParameterBoolean, None)
  249. for b in bools:
  250. widget.addItem(self.dialog.resolveValueDescription(b), b)
  251. return widget
  252. def setValue(self, value):
  253. if value is None or value == NULL:
  254. return
  255. if self.dialogType == DIALOG_STANDARD:
  256. self.widget.setChecked(value)
  257. else:
  258. self.setComboValue(value)
  259. def value(self):
  260. if self.dialogType == DIALOG_STANDARD:
  261. return self.widget.isChecked()
  262. else:
  263. return self.comboValue()
  264. class CrsWidgetWrapper(WidgetWrapper):
  265. def __init__(self, *args, **kwargs):
  266. super().__init__(*args, **kwargs)
  267. """
  268. .. deprecated:: 3.4
  269. Do not use, will be removed in QGIS 4.0
  270. """
  271. from warnings import warn
  272. warn("CrsWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  273. def createWidget(self):
  274. if self.dialogType == DIALOG_MODELER:
  275. self.combo = QComboBox()
  276. widget = QWidget()
  277. layout = QHBoxLayout()
  278. layout.setMargin(0)
  279. layout.setContentsMargins(0, 0, 0, 0)
  280. layout.setSpacing(1)
  281. layout.addWidget(self.combo)
  282. btn = QToolButton()
  283. btn.setIcon(QgsApplication.getThemeIcon("mActionSetProjection.svg"))
  284. btn.setToolTip(self.tr("Select CRS"))
  285. btn.clicked.connect(self.selectProjection)
  286. layout.addWidget(btn)
  287. widget.setLayout(layout)
  288. self.combo.setEditable(True)
  289. crss = self.dialog.getAvailableValuesOfType((QgsProcessingParameterCrs, QgsProcessingParameterString), QgsProcessingOutputString)
  290. for crs in crss:
  291. self.combo.addItem(self.dialog.resolveValueDescription(crs), crs)
  292. layers = self.dialog.getAvailableValuesOfType([QgsProcessingParameterRasterLayer,
  293. QgsProcessingParameterVectorLayer,
  294. QgsProcessingParameterMeshLayer,
  295. QgsProcessingParameterFeatureSource],
  296. [QgsProcessingOutputVectorLayer,
  297. QgsProcessingOutputRasterLayer,
  298. QgsProcessingOutputMapLayer])
  299. for l in layers:
  300. self.combo.addItem("Crs of layer " + self.dialog.resolveValueDescription(l), l)
  301. if self.parameterDefinition().defaultValue():
  302. self.combo.setEditText(self.parameterDefinition().defaultValue())
  303. return widget
  304. else:
  305. widget = QgsProjectionSelectionWidget()
  306. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  307. widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True)
  308. if self.parameterDefinition().defaultValue():
  309. if self.parameterDefinition().defaultValue() == 'ProjectCrs':
  310. crs = QgsProject.instance().crs()
  311. else:
  312. crs = QgsCoordinateReferenceSystem(self.parameterDefinition().defaultValue())
  313. widget.setCrs(crs)
  314. else:
  315. widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True)
  316. widget.crsChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  317. return widget
  318. def selectProjection(self):
  319. dialog = QgsProjectionSelectionDialog(self.widget)
  320. current_crs = QgsCoordinateReferenceSystem(self.combo.currentText())
  321. if current_crs.isValid():
  322. dialog.setCrs(current_crs)
  323. if dialog.exec_():
  324. self.setValue(dialog.crs().authid())
  325. def setValue(self, value):
  326. if value is None or value == NULL:
  327. return
  328. if self.dialogType == DIALOG_MODELER:
  329. self.setComboValue(value, self.combo)
  330. elif value == 'ProjectCrs':
  331. self.widget.setCrs(QgsProject.instance().crs())
  332. else:
  333. self.widget.setCrs(QgsCoordinateReferenceSystem(value))
  334. def value(self):
  335. if self.dialogType == DIALOG_MODELER:
  336. return self.comboValue(combobox=self.combo)
  337. else:
  338. crs = self.widget.crs()
  339. if crs.isValid():
  340. return self.widget.crs().authid()
  341. else:
  342. return None
  343. class ExtentWidgetWrapper(WidgetWrapper):
  344. USE_MIN_COVERING_EXTENT = "[Use min covering extent]"
  345. def __init__(self, *args, **kwargs):
  346. super().__init__(*args, **kwargs)
  347. """
  348. .. deprecated:: 3.14
  349. Do not use, will be removed in QGIS 4.0
  350. """
  351. from warnings import warn
  352. warn("ExtentWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  353. def createWidget(self):
  354. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  355. widget = ExtentSelectionPanel(self.dialog, self.parameterDefinition())
  356. widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  357. return widget
  358. else:
  359. widget = QComboBox()
  360. widget.setEditable(True)
  361. extents = self.dialog.getAvailableValuesOfType(QgsProcessingParameterExtent, (QgsProcessingOutputString))
  362. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  363. widget.addItem(self.USE_MIN_COVERING_EXTENT, None)
  364. layers = self.dialog.getAvailableValuesOfType([QgsProcessingParameterFeatureSource,
  365. QgsProcessingParameterRasterLayer,
  366. QgsProcessingParameterVectorLayer,
  367. QgsProcessingParameterMeshLayer],
  368. [QgsProcessingOutputRasterLayer,
  369. QgsProcessingOutputVectorLayer,
  370. QgsProcessingOutputMapLayer])
  371. for ex in extents:
  372. widget.addItem(self.dialog.resolveValueDescription(ex), ex)
  373. for l in layers:
  374. widget.addItem("Extent of " + self.dialog.resolveValueDescription(l), l)
  375. if not self.parameterDefinition().defaultValue():
  376. widget.setEditText(self.parameterDefinition().defaultValue())
  377. return widget
  378. def setValue(self, value):
  379. if value is None or value == NULL:
  380. return
  381. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  382. self.widget.setExtentFromString(value)
  383. else:
  384. self.setComboValue(value)
  385. def value(self):
  386. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  387. return self.widget.getValue()
  388. else:
  389. idx = self.widget.findText(self.widget.currentText())
  390. if idx < 0:
  391. s = str(self.widget.currentText()).strip()
  392. if s:
  393. try:
  394. tokens = s.split(',')
  395. if len(tokens) != 4:
  396. raise InvalidParameterValue()
  397. for token in tokens:
  398. float(token)
  399. except:
  400. raise InvalidParameterValue()
  401. elif self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  402. s = None
  403. else:
  404. raise InvalidParameterValue()
  405. return s
  406. else:
  407. return self.widget.currentData()
  408. class PointWidgetWrapper(WidgetWrapper):
  409. def __init__(self, *args, **kwargs):
  410. super().__init__(*args, **kwargs)
  411. """
  412. .. deprecated:: 3.4
  413. Do not use, will be removed in QGIS 4.0
  414. """
  415. from warnings import warn
  416. warn("PointWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  417. def createWidget(self):
  418. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  419. return PointSelectionPanel(self.dialog, self.parameterDefinition().defaultValue())
  420. else:
  421. item = QComboBox()
  422. item.setEditable(True)
  423. points = self.dialog.getAvailableValuesOfType((QgsProcessingParameterPoint, QgsProcessingParameterString), (QgsProcessingOutputString))
  424. for p in points:
  425. item.addItem(self.dialog.resolveValueDescription(p), p)
  426. item.setEditText(str(self.parameterDefinition().defaultValue()))
  427. return item
  428. def setValue(self, value):
  429. if value is None or value == NULL:
  430. return
  431. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  432. self.widget.setPointFromString(value)
  433. else:
  434. self.setComboValue(value)
  435. def value(self):
  436. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  437. return self.widget.getValue()
  438. else:
  439. idx = self.widget.findText(self.widget.currentText())
  440. if idx < 0:
  441. s = str(self.widget.currentText()).strip()
  442. if s:
  443. try:
  444. tokens = s.split(',')
  445. if len(tokens) != 2:
  446. raise InvalidParameterValue()
  447. for token in tokens:
  448. float(token)
  449. except:
  450. raise InvalidParameterValue()
  451. elif self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  452. s = None
  453. else:
  454. raise InvalidParameterValue()
  455. return s
  456. else:
  457. return self.widget.currentData()
  458. class FileWidgetWrapper(WidgetWrapper):
  459. def __init__(self, *args, **kwargs):
  460. super().__init__(*args, **kwargs)
  461. """
  462. .. deprecated:: 3.4
  463. Do not use, will be removed in QGIS 4.0
  464. """
  465. from warnings import warn
  466. warn("FileWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  467. def createWidget(self):
  468. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  469. return FileSelectionPanel(self.parameterDefinition().behavior() == QgsProcessingParameterFile.Folder,
  470. self.parameterDefinition().extension())
  471. else:
  472. self.combo = QComboBox()
  473. self.combo.setEditable(True)
  474. files = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, (QgsProcessingOutputRasterLayer, QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputFile, QgsProcessingOutputString))
  475. for f in files:
  476. self.combo.addItem(self.dialog.resolveValueDescription(f), f)
  477. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  478. self.combo.setEditText("")
  479. widget = QWidget()
  480. layout = QHBoxLayout()
  481. layout.setMargin(0)
  482. layout.setContentsMargins(0, 0, 0, 0)
  483. layout.setSpacing(6)
  484. layout.addWidget(self.combo)
  485. btn = QToolButton()
  486. btn.setText('…')
  487. btn.setToolTip(self.tr("Select file"))
  488. btn.clicked.connect(self.selectFile)
  489. layout.addWidget(btn)
  490. widget.setLayout(layout)
  491. return widget
  492. def selectFile(self):
  493. settings = QgsSettings()
  494. if os.path.isdir(os.path.dirname(self.combo.currentText())):
  495. path = os.path.dirname(self.combo.currentText())
  496. if settings.contains('/Processing/LastInputPath'):
  497. path = settings.value('/Processing/LastInputPath')
  498. else:
  499. path = ''
  500. if self.parameterDefinition().extension():
  501. filter = self.tr('{} files').format(
  502. self.parameterDefinition().extension().upper()) + ' (*.' + self.parameterDefinition().extension() + self.tr(
  503. ');;All files (*.*)')
  504. else:
  505. filter = self.tr('All files (*.*)')
  506. filename, selected_filter = QFileDialog.getOpenFileName(self.widget,
  507. self.tr('Select File'), path,
  508. filter)
  509. if filename:
  510. self.combo.setEditText(filename)
  511. def setValue(self, value):
  512. if value is None or value == NULL:
  513. return
  514. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  515. self.widget.setText(value)
  516. else:
  517. self.setComboValue(value, combobox=self.combo)
  518. def value(self):
  519. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  520. return self.widget.getValue()
  521. else:
  522. return self.comboValue(combobox=self.combo)
  523. class FixedTableWidgetWrapper(WidgetWrapper):
  524. def __init__(self, *args, **kwargs):
  525. super().__init__(*args, **kwargs)
  526. """
  527. .. deprecated:: 3.4
  528. Do not use, will be removed in QGIS 4.0
  529. """
  530. from warnings import warn
  531. warn("FixedTableWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  532. def createWidget(self):
  533. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  534. return FixedTablePanel(self.parameterDefinition())
  535. else:
  536. self.combobox = QComboBox()
  537. values = self.dialog.getAvailableValuesOfType(QgsProcessingParameterMatrix)
  538. for v in values:
  539. self.combobox.addItem(self.dialog.resolveValueDescription(v), v)
  540. return self.combobox
  541. def setValue(self, value):
  542. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  543. self.widget.setValue(value)
  544. else:
  545. self.setComboValue(value, combobox=self.combobox)
  546. def value(self):
  547. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  548. return self.widget.table
  549. else:
  550. return self.comboValue(combobox=self.combobox)
  551. class MultipleLayerWidgetWrapper(WidgetWrapper):
  552. def _getOptions(self):
  553. if self.parameterDefinition().layerType() == QgsProcessing.TypeVectorAnyGeometry:
  554. options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
  555. QgsProcessingParameterVectorLayer,
  556. QgsProcessingParameterMultipleLayers),
  557. [QgsProcessingOutputVectorLayer,
  558. QgsProcessingOutputMapLayer,
  559. QgsProcessingOutputMultipleLayers])
  560. elif self.parameterDefinition().layerType() == QgsProcessing.TypeVector:
  561. options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
  562. QgsProcessingParameterVectorLayer,
  563. QgsProcessingParameterMultipleLayers),
  564. [QgsProcessingOutputVectorLayer,
  565. QgsProcessingOutputMapLayer,
  566. QgsProcessingOutputMultipleLayers],
  567. [QgsProcessing.TypeVector])
  568. elif self.parameterDefinition().layerType() == QgsProcessing.TypeVectorPoint:
  569. options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
  570. QgsProcessingParameterVectorLayer,
  571. QgsProcessingParameterMultipleLayers),
  572. [QgsProcessingOutputVectorLayer,
  573. QgsProcessingOutputMapLayer,
  574. QgsProcessingOutputMultipleLayers],
  575. [QgsProcessing.TypeVectorPoint,
  576. QgsProcessing.TypeVectorAnyGeometry])
  577. elif self.parameterDefinition().layerType() == QgsProcessing.TypeVectorLine:
  578. options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
  579. QgsProcessingParameterVectorLayer,
  580. QgsProcessingParameterMultipleLayers),
  581. [QgsProcessingOutputVectorLayer,
  582. QgsProcessingOutputMapLayer,
  583. QgsProcessingOutputMultipleLayers],
  584. [QgsProcessing.TypeVectorLine,
  585. QgsProcessing.TypeVectorAnyGeometry])
  586. elif self.parameterDefinition().layerType() == QgsProcessing.TypeVectorPolygon:
  587. options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
  588. QgsProcessingParameterVectorLayer,
  589. QgsProcessingParameterMultipleLayers),
  590. [QgsProcessingOutputVectorLayer,
  591. QgsProcessingOutputMapLayer,
  592. QgsProcessingOutputMultipleLayers],
  593. [QgsProcessing.TypeVectorPolygon,
  594. QgsProcessing.TypeVectorAnyGeometry])
  595. elif self.parameterDefinition().layerType() == QgsProcessing.TypeRaster:
  596. options = self.dialog.getAvailableValuesOfType(
  597. (QgsProcessingParameterRasterLayer, QgsProcessingParameterMultipleLayers),
  598. [QgsProcessingOutputRasterLayer,
  599. QgsProcessingOutputMapLayer,
  600. QgsProcessingOutputMultipleLayers])
  601. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMesh:
  602. options = self.dialog.getAvailableValuesOfType(
  603. (QgsProcessingParameterMeshLayer, QgsProcessingParameterMultipleLayers),
  604. [])
  605. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer:
  606. options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterRasterLayer,
  607. QgsProcessingParameterFeatureSource,
  608. QgsProcessingParameterVectorLayer,
  609. QgsProcessingParameterMeshLayer,
  610. QgsProcessingParameterMultipleLayers),
  611. [QgsProcessingOutputRasterLayer,
  612. QgsProcessingOutputVectorLayer,
  613. QgsProcessingOutputMapLayer,
  614. QgsProcessingOutputMultipleLayers])
  615. else:
  616. options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, QgsProcessingOutputFile)
  617. options = sorted(options, key=lambda opt: self.dialog.resolveValueDescription(opt))
  618. return options
  619. def createWidget(self):
  620. if self.dialogType == DIALOG_STANDARD:
  621. if self.parameterDefinition().layerType() == QgsProcessing.TypeFile:
  622. return MultipleInputPanel(datatype=QgsProcessing.TypeFile)
  623. else:
  624. if self.parameterDefinition().layerType() == QgsProcessing.TypeRaster:
  625. options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)
  626. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMesh:
  627. options = QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance(), False)
  628. elif self.parameterDefinition().layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector):
  629. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
  630. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer:
  631. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
  632. options.extend(QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False))
  633. options.extend(QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance(), False))
  634. else:
  635. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.parameterDefinition().layerType()],
  636. False)
  637. opts = [getExtendedLayerName(opt) for opt in options]
  638. return MultipleInputPanel(opts, datatype=self.parameterDefinition().layerType())
  639. elif self.dialogType == DIALOG_BATCH:
  640. widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog)
  641. widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  642. return widget
  643. else:
  644. options = [self.dialog.resolveValueDescription(opt) for opt in self._getOptions()]
  645. return MultipleInputPanel(options, datatype=self.parameterDefinition().layerType())
  646. def refresh(self):
  647. if self.parameterDefinition().layerType() != QgsProcessing.TypeFile:
  648. if self.parameterDefinition().layerType() == QgsProcessing.TypeRaster:
  649. options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)
  650. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMesh:
  651. options = QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance(), False)
  652. elif self.parameterDefinition().layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector):
  653. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
  654. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer:
  655. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
  656. options.extend(QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False))
  657. options.extend(QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance(), False))
  658. else:
  659. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.parameterDefinition().layerType()],
  660. False)
  661. opts = [getExtendedLayerName(opt) for opt in options]
  662. self.widget.updateForOptions(opts)
  663. def setValue(self, value):
  664. if value is None or value == NULL:
  665. return
  666. if self.dialogType == DIALOG_STANDARD:
  667. pass # TODO
  668. elif self.dialogType == DIALOG_BATCH:
  669. return self.widget.setValue(value)
  670. else:
  671. options = self._getOptions()
  672. if not isinstance(value, (tuple, list)):
  673. value = [value]
  674. selected_options = []
  675. for sel in value:
  676. if sel in options:
  677. selected_options.append(options.index(sel))
  678. elif isinstance(sel, QgsProcessingModelChildParameterSource):
  679. selected_options.append(sel.staticValue())
  680. else:
  681. selected_options.append(sel)
  682. self.widget.setSelectedItems(selected_options)
  683. def value(self):
  684. if self.dialogType == DIALOG_STANDARD:
  685. if self.parameterDefinition().layerType() == QgsProcessing.TypeFile:
  686. return self.widget.selectedoptions
  687. else:
  688. if self.parameterDefinition().layerType() == QgsProcessing.TypeRaster:
  689. options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)
  690. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMesh:
  691. options = QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance(), False)
  692. elif self.parameterDefinition().layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector):
  693. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
  694. elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer:
  695. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False)
  696. options.extend(QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False))
  697. options.extend(QgsProcessingUtils.compatibleMeshLayers(QgsProject.instance(), False))
  698. else:
  699. options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.parameterDefinition().layerType()],
  700. False)
  701. return [options[i] if isinstance(i, int) else i for i in self.widget.selectedoptions]
  702. elif self.dialogType == DIALOG_BATCH:
  703. return self.widget.getValue()
  704. else:
  705. options = self._getOptions()
  706. values = [options[i] if isinstance(i, int) else QgsProcessingModelChildParameterSource.fromStaticValue(i)
  707. for i in self.widget.selectedoptions]
  708. if len(values) == 0 and not self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  709. raise InvalidParameterValue()
  710. return values
  711. class NumberWidgetWrapper(WidgetWrapper):
  712. def __init__(self, *args, **kwargs):
  713. super().__init__(*args, **kwargs)
  714. """
  715. .. deprecated:: 3.4
  716. Do not use, will be removed in QGIS 4.0
  717. """
  718. from warnings import warn
  719. warn("NumberWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  720. def createWidget(self):
  721. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  722. widget = NumberInputPanel(self.parameterDefinition())
  723. widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  724. return widget
  725. else:
  726. return ModelerNumberInputPanel(self.parameterDefinition(), self.dialog)
  727. def setValue(self, value):
  728. if value is None or value == NULL:
  729. return
  730. self.widget.setValue(value)
  731. def value(self):
  732. return self.widget.getValue()
  733. def postInitialize(self, wrappers):
  734. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH) and self.parameterDefinition().isDynamic():
  735. for wrapper in wrappers:
  736. if wrapper.parameterDefinition().name() == self.parameterDefinition().dynamicLayerParameterName():
  737. self.widget.setDynamicLayer(wrapper.parameterValue())
  738. wrapper.widgetValueHasChanged.connect(self.parentLayerChanged)
  739. break
  740. def parentLayerChanged(self, wrapper):
  741. self.widget.setDynamicLayer(wrapper.parameterValue())
  742. class DistanceWidgetWrapper(WidgetWrapper):
  743. def __init__(self, *args, **kwargs):
  744. super().__init__(*args, **kwargs)
  745. """
  746. .. deprecated:: 3.4
  747. Do not use, will be removed in QGIS 4.0
  748. """
  749. from warnings import warn
  750. warn("DistanceWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  751. def createWidget(self):
  752. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  753. widget = DistanceInputPanel(self.parameterDefinition())
  754. widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  755. return widget
  756. else:
  757. return ModelerNumberInputPanel(self.parameterDefinition(), self.dialog)
  758. def setValue(self, value):
  759. if value is None or value == NULL:
  760. return
  761. self.widget.setValue(value)
  762. def value(self):
  763. return self.widget.getValue()
  764. def postInitialize(self, wrappers):
  765. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  766. for wrapper in wrappers:
  767. if wrapper.parameterDefinition().name() == self.parameterDefinition().dynamicLayerParameterName():
  768. self.widget.setDynamicLayer(wrapper.parameterValue())
  769. wrapper.widgetValueHasChanged.connect(self.dynamicLayerChanged)
  770. if wrapper.parameterDefinition().name() == self.parameterDefinition().parentParameterName():
  771. self.widget.setUnitParameterValue(wrapper.parameterValue())
  772. wrapper.widgetValueHasChanged.connect(self.parentParameterChanged)
  773. def dynamicLayerChanged(self, wrapper):
  774. self.widget.setDynamicLayer(wrapper.parameterValue())
  775. def parentParameterChanged(self, wrapper):
  776. self.widget.setUnitParameterValue(wrapper.parameterValue())
  777. class RangeWidgetWrapper(WidgetWrapper):
  778. def __init__(self, *args, **kwargs):
  779. super().__init__(*args, **kwargs)
  780. """
  781. .. deprecated:: 3.4
  782. Do not use, will be removed in QGIS 4.0
  783. """
  784. from warnings import warn
  785. warn("RangeWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  786. def createWidget(self):
  787. widget = RangePanel(self.parameterDefinition())
  788. widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  789. return widget
  790. def setValue(self, value):
  791. if value is None or value == NULL:
  792. return
  793. self.widget.setValue(value)
  794. def value(self):
  795. return self.widget.getValue()
  796. class MapLayerWidgetWrapper(WidgetWrapper):
  797. NOT_SELECTED = '[Not selected]'
  798. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  799. """
  800. .. deprecated:: 3.14
  801. Do not use, will be removed in QGIS 4.0
  802. """
  803. from warnings import warn
  804. warn("MapLayerWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  805. super().__init__(param, dialog, row, col, **kwargs)
  806. def createWidget(self):
  807. if self.dialogType == DIALOG_STANDARD:
  808. self.combo = QgsProcessingMapLayerComboBox(self.parameterDefinition())
  809. self.context = dataobjects.createContext()
  810. try:
  811. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  812. self.combo.setValue(self.parameterDefinition().defaultValue(), self.context)
  813. else:
  814. if self.parameterDefinition().defaultValue():
  815. self.combo.setValue(self.parameterDefinition().defaultValue(), self.context)
  816. else:
  817. self.combo.setLayer(iface.activeLayer())
  818. except:
  819. pass
  820. self.combo.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  821. return self.combo
  822. elif self.dialogType == DIALOG_BATCH:
  823. widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog)
  824. widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  825. return widget
  826. else:
  827. self.combo = QComboBox()
  828. layers = self.getAvailableLayers()
  829. self.combo.setEditable(True)
  830. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  831. self.combo.addItem(self.NOT_SELECTED, self.NOT_SET_OPTION)
  832. for layer in layers:
  833. self.combo.addItem(self.dialog.resolveValueDescription(layer), layer)
  834. widget = QWidget()
  835. layout = QHBoxLayout()
  836. layout.setMargin(0)
  837. layout.setContentsMargins(0, 0, 0, 0)
  838. layout.setSpacing(6)
  839. layout.addWidget(self.combo)
  840. btn = QToolButton()
  841. btn.setText('…')
  842. btn.setToolTip(self.tr("Select file"))
  843. btn.clicked.connect(self.selectFile)
  844. layout.addWidget(btn)
  845. widget.setLayout(layout)
  846. return widget
  847. def getAvailableLayers(self):
  848. return self.dialog.getAvailableValuesOfType(
  849. [QgsProcessingParameterRasterLayer, QgsProcessingParameterMeshLayer, QgsProcessingParameterVectorLayer, QgsProcessingParameterMapLayer, QgsProcessingParameterString],
  850. [QgsProcessingOutputRasterLayer, QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputString, QgsProcessingOutputFile])
  851. def selectFile(self):
  852. filename, selected_filter = self.getFileName(self.combo.currentText())
  853. if filename:
  854. if isinstance(self.combo, QgsProcessingMapLayerComboBox):
  855. self.combo.setValue(filename, self.context)
  856. elif isinstance(self.combo, QgsMapLayerComboBox):
  857. items = self.combo.additionalItems()
  858. items.append(filename)
  859. self.combo.setAdditionalItems(items)
  860. self.combo.setCurrentIndex(self.combo.findText(filename))
  861. else:
  862. self.combo.setEditText(filename)
  863. self.widgetValueHasChanged.emit(self)
  864. def setValue(self, value):
  865. if value is None or value == NULL:
  866. return
  867. if self.dialogType == DIALOG_STANDARD:
  868. if isinstance(value, str):
  869. layer = QgsProject.instance().mapLayer(value)
  870. if layer is not None:
  871. value = layer
  872. self.combo.setValue(value, self.context)
  873. elif self.dialogType == DIALOG_BATCH:
  874. self.widget.setValue(value)
  875. else:
  876. self.setComboValue(value, combobox=self.combo)
  877. self.widgetValueHasChanged.emit(self)
  878. def value(self):
  879. if self.dialogType == DIALOG_STANDARD:
  880. return self.combo.value()
  881. elif self.dialogType == DIALOG_BATCH:
  882. return self.widget.getValue()
  883. else:
  884. def validator(v):
  885. if not bool(v):
  886. return self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  887. else:
  888. return os.path.exists(v)
  889. return self.comboValue(validator, combobox=self.combo)
  890. class RasterWidgetWrapper(MapLayerWidgetWrapper):
  891. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  892. """
  893. .. deprecated:: 3.14
  894. Do not use, will be removed in QGIS 4.0
  895. """
  896. from warnings import warn
  897. warn("RasterWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  898. super().__init__(param, dialog, row, col, **kwargs)
  899. def getAvailableLayers(self):
  900. return self.dialog.getAvailableValuesOfType((QgsProcessingParameterRasterLayer, QgsProcessingParameterString),
  901. (QgsProcessingOutputRasterLayer, QgsProcessingOutputFile, QgsProcessingOutputString))
  902. def selectFile(self):
  903. filename, selected_filter = self.getFileName(self.combo.currentText())
  904. if filename:
  905. filename = dataobjects.getRasterSublayer(filename, self.parameterDefinition())
  906. if isinstance(self.combo, QgsProcessingMapLayerComboBox):
  907. self.combo.setValue(filename, self.context)
  908. elif isinstance(self.combo, QgsMapLayerComboBox):
  909. items = self.combo.additionalItems()
  910. items.append(filename)
  911. self.combo.setAdditionalItems(items)
  912. self.combo.setCurrentIndex(self.combo.findText(filename))
  913. else:
  914. self.combo.setEditText(filename)
  915. self.widgetValueHasChanged.emit(self)
  916. class MeshWidgetWrapper(MapLayerWidgetWrapper):
  917. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  918. """
  919. .. deprecated:: 3.14
  920. Do not use, will be removed in QGIS 4.0
  921. """
  922. from warnings import warn
  923. warn("MeshWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  924. super().__init__(param, dialog, row, col, **kwargs)
  925. def getAvailableLayers(self):
  926. return self.dialog.getAvailableValuesOfType((QgsProcessingParameterMeshLayer, QgsProcessingParameterString),
  927. ())
  928. def selectFile(self):
  929. filename, selected_filter = self.getFileName(self.combo.currentText())
  930. if filename:
  931. if isinstance(self.combo, QgsProcessingMapLayerComboBox):
  932. self.combo.setValue(filename, self.context)
  933. elif isinstance(self.combo, QgsMapLayerComboBox):
  934. items = self.combo.additionalItems()
  935. items.append(filename)
  936. self.combo.setAdditionalItems(items)
  937. self.combo.setCurrentIndex(self.combo.findText(filename))
  938. else:
  939. self.combo.setEditText(filename)
  940. self.widgetValueHasChanged.emit(self)
  941. class EnumWidgetWrapper(WidgetWrapper):
  942. NOT_SELECTED = '[Not selected]'
  943. def __init__(self, *args, **kwargs):
  944. super().__init__(*args, **kwargs)
  945. """
  946. .. deprecated:: 3.4
  947. Do not use, will be removed in QGIS 4.0
  948. """
  949. from warnings import warn
  950. warn("EnumWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  951. def createWidget(self, useCheckBoxes=False, columns=1):
  952. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  953. self._useCheckBoxes = useCheckBoxes
  954. if self._useCheckBoxes and not self.dialogType == DIALOG_BATCH:
  955. return CheckboxesPanel(options=self.parameterDefinition().options(),
  956. multiple=self.parameterDefinition().allowMultiple(),
  957. columns=columns)
  958. if self.parameterDefinition().allowMultiple():
  959. return MultipleInputPanel(options=self.parameterDefinition().options())
  960. else:
  961. widget = QComboBox()
  962. for i, option in enumerate(self.parameterDefinition().options()):
  963. widget.addItem(option, i)
  964. if self.parameterDefinition().defaultValue():
  965. widget.setCurrentIndex(widget.findData(self.parameterDefinition().defaultValue()))
  966. return widget
  967. else:
  968. self.combobox = QComboBox()
  969. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  970. self.combobox.addItem(self.NOT_SELECTED, self.NOT_SET_OPTION)
  971. for i, option in enumerate(self.parameterDefinition().options()):
  972. self.combobox.addItem(option, i)
  973. values = self.dialog.getAvailableValuesOfType(QgsProcessingParameterEnum)
  974. for v in values:
  975. self.combobox.addItem(self.dialog.resolveValueDescription(v), v)
  976. return self.combobox
  977. def setValue(self, value):
  978. if value is None or value == NULL:
  979. return
  980. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  981. if self._useCheckBoxes and not self.dialogType == DIALOG_BATCH:
  982. self.widget.setValue(value)
  983. return
  984. if self.parameterDefinition().allowMultiple():
  985. self.widget.setSelectedItems(value)
  986. else:
  987. self.widget.setCurrentIndex(self.widget.findData(value))
  988. else:
  989. self.setComboValue(value, combobox=self.combobox)
  990. def value(self):
  991. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  992. if self._useCheckBoxes and not self.dialogType == DIALOG_BATCH:
  993. return self.widget.value()
  994. if self.parameterDefinition().allowMultiple():
  995. return self.widget.selectedoptions
  996. else:
  997. return self.widget.currentData()
  998. else:
  999. return self.comboValue(combobox=self.combobox)
  1000. class FeatureSourceWidgetWrapper(WidgetWrapper):
  1001. NOT_SELECTED = '[Not selected]'
  1002. def __init__(self, *args, **kwargs):
  1003. """
  1004. .. deprecated:: 3.4
  1005. Do not use, will be removed in QGIS 4.0
  1006. """
  1007. from warnings import warn
  1008. warn("FeatureSourceWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  1009. self.map_layer_combo = None
  1010. super().__init__(*args, **kwargs)
  1011. def createWidget(self):
  1012. if self.dialogType == DIALOG_STANDARD:
  1013. self.map_layer_combo = QgsProcessingMapLayerComboBox(self.parameterDefinition())
  1014. self.context = dataobjects.createContext()
  1015. try:
  1016. if iface.activeLayer().type() == QgsMapLayerType.VectorLayer:
  1017. self.map_layer_combo.setLayer(iface.activeLayer())
  1018. except:
  1019. pass
  1020. self.map_layer_combo.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  1021. return self.map_layer_combo
  1022. elif self.dialogType == DIALOG_BATCH:
  1023. widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog)
  1024. widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  1025. return widget
  1026. else:
  1027. self.combo = QComboBox()
  1028. layers = self.dialog.getAvailableValuesOfType(
  1029. (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer),
  1030. (QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputString, QgsProcessingOutputFile), self.parameterDefinition().dataTypes())
  1031. self.combo.setEditable(True)
  1032. for layer in layers:
  1033. self.combo.addItem(self.dialog.resolveValueDescription(layer), layer)
  1034. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  1035. self.combo.setEditText("")
  1036. widget = QWidget()
  1037. layout = QHBoxLayout()
  1038. layout.setMargin(0)
  1039. layout.setContentsMargins(0, 0, 0, 0)
  1040. layout.setSpacing(2)
  1041. layout.addWidget(self.combo)
  1042. btn = QToolButton()
  1043. btn.setText('…')
  1044. btn.setToolTip(self.tr("Select file"))
  1045. btn.clicked.connect(self.selectFile)
  1046. layout.addWidget(btn)
  1047. widget.setLayout(layout)
  1048. return widget
  1049. def setWidgetContext(self, context):
  1050. if self.map_layer_combo:
  1051. self.map_layer_combo.setWidgetContext(context)
  1052. super().setWidgetContext(context)
  1053. def selectFile(self):
  1054. filename, selected_filter = self.getFileName(self.combo.currentText())
  1055. if filename:
  1056. if isinstance(self.combo, QgsProcessingMapLayerComboBox):
  1057. self.combo.setValue(filename, self.context)
  1058. elif isinstance(self.combo, QgsMapLayerComboBox):
  1059. items = self.combo.additionalItems()
  1060. items.append(filename)
  1061. self.combo.setAdditionalItems(items)
  1062. self.combo.setCurrentIndex(self.combo.findText(filename))
  1063. else:
  1064. self.combo.setEditText(filename)
  1065. self.widgetValueHasChanged.emit(self)
  1066. def setValue(self, value):
  1067. if value is None or value == NULL:
  1068. return
  1069. if self.dialogType == DIALOG_STANDARD:
  1070. if isinstance(value, str):
  1071. layer = QgsProject.instance().mapLayer(value)
  1072. if layer is not None:
  1073. value = layer
  1074. self.map_layer_combo.setValue(value, self.context)
  1075. elif self.dialogType == DIALOG_BATCH:
  1076. self.widget.setValue(value)
  1077. else:
  1078. self.setComboValue(value, combobox=self.combo)
  1079. self.widgetValueHasChanged.emit(self)
  1080. def value(self):
  1081. if self.dialogType == DIALOG_STANDARD:
  1082. return self.map_layer_combo.value()
  1083. elif self.dialogType == DIALOG_BATCH:
  1084. return self.widget.getValue()
  1085. else:
  1086. def validator(v):
  1087. if not bool(v):
  1088. return self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  1089. else:
  1090. return os.path.exists(v)
  1091. if self.combo.currentText():
  1092. return self.comboValue(validator, combobox=self.combo)
  1093. else:
  1094. return None
  1095. class StringWidgetWrapper(WidgetWrapper):
  1096. def __init__(self, *args, **kwargs):
  1097. super().__init__(*args, **kwargs)
  1098. """
  1099. .. deprecated:: 3.4
  1100. Do not use, will be removed in QGIS 4.0
  1101. """
  1102. from warnings import warn
  1103. warn("StringWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  1104. def createWidget(self):
  1105. if self.dialogType == DIALOG_STANDARD:
  1106. if self.parameterDefinition().multiLine():
  1107. widget = QPlainTextEdit()
  1108. else:
  1109. self._lineedit = QLineEdit()
  1110. widget = self._lineedit
  1111. elif self.dialogType == DIALOG_BATCH:
  1112. widget = QLineEdit()
  1113. else:
  1114. # strings, numbers, files and table fields are all allowed input types
  1115. strings = self.dialog.getAvailableValuesOfType(
  1116. [QgsProcessingParameterString, QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterFile,
  1117. QgsProcessingParameterField, QgsProcessingParameterExpression],
  1118. [QgsProcessingOutputString, QgsProcessingOutputFile])
  1119. options = [(self.dialog.resolveValueDescription(s), s) for s in strings]
  1120. if self.parameterDefinition().multiLine():
  1121. widget = MultilineTextPanel(options)
  1122. else:
  1123. widget = QComboBox()
  1124. widget.setEditable(True)
  1125. for desc, val in options:
  1126. widget.addItem(desc, val)
  1127. return widget
  1128. def showExpressionsBuilder(self):
  1129. context = dataobjects.createExpressionContext()
  1130. value = self.value()
  1131. if not isinstance(value, str):
  1132. value = ''
  1133. dlg = QgsExpressionBuilderDialog(None, value, self.widget, 'generic', context)
  1134. dlg.setWindowTitle(self.tr('Expression based input'))
  1135. if dlg.exec_() == QDialog.Accepted:
  1136. exp = QgsExpression(dlg.expressionText())
  1137. if not exp.hasParserError():
  1138. if self.dialogType == DIALOG_STANDARD:
  1139. self.setValue(str(exp.evaluate(context)))
  1140. else:
  1141. self.setValue(dlg.expressionText())
  1142. def setValue(self, value):
  1143. if value is None or value == NULL:
  1144. return
  1145. if self.dialogType == DIALOG_STANDARD:
  1146. if self.parameterDefinition().multiLine():
  1147. self.widget.setPlainText(value)
  1148. else:
  1149. self._lineedit.setText(value)
  1150. elif self.dialogType == DIALOG_BATCH:
  1151. self.widget.setText(value)
  1152. else:
  1153. if self.parameterDefinition().multiLine():
  1154. self.widget.setValue(value)
  1155. else:
  1156. self.setComboValue(value)
  1157. def value(self):
  1158. if self.dialogType == DIALOG_STANDARD:
  1159. if self.parameterDefinition().multiLine():
  1160. text = self.widget.toPlainText()
  1161. else:
  1162. text = self._lineedit.text()
  1163. return text
  1164. elif self.dialogType == DIALOG_BATCH:
  1165. return self.widget.text()
  1166. else:
  1167. if self.parameterDefinition().multiLine():
  1168. value = self.widget.getValue()
  1169. option = self.widget.getOption()
  1170. if option == MultilineTextPanel.USE_TEXT:
  1171. if value == '':
  1172. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  1173. return None
  1174. else:
  1175. raise InvalidParameterValue()
  1176. else:
  1177. return value
  1178. else:
  1179. return value
  1180. else:
  1181. def validator(v):
  1182. return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  1183. return self.comboValue(validator)
  1184. class ExpressionWidgetWrapper(WidgetWrapper):
  1185. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  1186. """
  1187. .. deprecated:: 3.4
  1188. Do not use, will be removed in QGIS 4.0
  1189. """
  1190. from warnings import warn
  1191. warn("StringWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  1192. super().__init__(param, dialog, row, col, **kwargs)
  1193. self.context = dataobjects.createContext()
  1194. def createWidget(self):
  1195. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1196. if self.parameterDefinition().parentLayerParameterName():
  1197. widget = QgsFieldExpressionWidget()
  1198. else:
  1199. widget = QgsExpressionLineEdit()
  1200. if self.parameterDefinition().defaultValue():
  1201. widget.setExpression(self.parameterDefinition().defaultValue())
  1202. else:
  1203. strings = self.dialog.getAvailableValuesOfType(
  1204. [QgsProcessingParameterExpression, QgsProcessingParameterString, QgsProcessingParameterNumber, QgsProcessingParameterDistance],
  1205. (QgsProcessingOutputString, QgsProcessingOutputNumber))
  1206. options = [(self.dialog.resolveValueDescription(s), s) for s in strings]
  1207. widget = QComboBox()
  1208. widget.setEditable(True)
  1209. for desc, val in options:
  1210. widget.addItem(desc, val)
  1211. widget.setEditText(self.parameterDefinition().defaultValue() or "")
  1212. return widget
  1213. def postInitialize(self, wrappers):
  1214. for wrapper in wrappers:
  1215. if wrapper.parameterDefinition().name() == self.parameterDefinition().parentLayerParameterName():
  1216. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1217. self.setLayer(wrapper.parameterValue())
  1218. wrapper.widgetValueHasChanged.connect(self.parentLayerChanged)
  1219. break
  1220. def parentLayerChanged(self, wrapper):
  1221. self.setLayer(wrapper.parameterValue())
  1222. def setLayer(self, layer):
  1223. if isinstance(layer, QgsProcessingFeatureSourceDefinition):
  1224. layer, ok = layer.source.valueAsString(self.context.expressionContext())
  1225. if isinstance(layer, str):
  1226. layer = QgsProcessingUtils.mapLayerFromString(layer, self.context)
  1227. self.widget.setLayer(layer)
  1228. def setValue(self, value):
  1229. if value is None or value == NULL:
  1230. return
  1231. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1232. self.widget.setExpression(value)
  1233. else:
  1234. self.setComboValue(value)
  1235. def value(self):
  1236. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1237. try:
  1238. return self.widget.asExpression()
  1239. except:
  1240. return self.widget.expression()
  1241. else:
  1242. def validator(v):
  1243. return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  1244. return self.comboValue(validator)
  1245. class VectorLayerWidgetWrapper(WidgetWrapper):
  1246. NOT_SELECTED = '[Not selected]'
  1247. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  1248. """
  1249. .. deprecated:: 3.14
  1250. Do not use, will be removed in QGIS 4.0
  1251. """
  1252. from warnings import warn
  1253. warn("VectorLayerWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  1254. super().__init__(param, dialog, row, col, **kwargs)
  1255. def createWidget(self):
  1256. if self.dialogType == DIALOG_STANDARD:
  1257. self.combo = QgsProcessingMapLayerComboBox(self.parameterDefinition())
  1258. self.context = dataobjects.createContext()
  1259. try:
  1260. if iface.activeLayer().type() == QgsMapLayerType.VectorLayer:
  1261. self.combo.setLayer(iface.activeLayer())
  1262. except:
  1263. pass
  1264. self.combo.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  1265. return self.combo
  1266. elif self.dialogType == DIALOG_BATCH:
  1267. widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog)
  1268. widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  1269. return widget
  1270. else:
  1271. self.combo = QComboBox()
  1272. self.combo.setEditable(True)
  1273. tables = self.dialog.getAvailableValuesOfType((QgsProcessingParameterVectorLayer, QgsProcessingParameterString),
  1274. (QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputFile, QgsProcessingOutputString))
  1275. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  1276. self.combo.addItem(self.NOT_SELECTED, self.NOT_SET_OPTION)
  1277. for table in tables:
  1278. self.combo.addItem(self.dialog.resolveValueDescription(table), table)
  1279. widget = QWidget()
  1280. layout = QHBoxLayout()
  1281. layout.setMargin(0)
  1282. layout.setContentsMargins(0, 0, 0, 0)
  1283. layout.setSpacing(6)
  1284. layout.addWidget(self.combo)
  1285. btn = QToolButton()
  1286. btn.setText('…')
  1287. btn.setToolTip(self.tr("Select file"))
  1288. btn.clicked.connect(self.selectFile)
  1289. layout.addWidget(btn)
  1290. widget.setLayout(layout)
  1291. return widget
  1292. def selectFile(self):
  1293. filename, selected_filter = self.getFileName(self.combo.currentText())
  1294. if filename:
  1295. filename = dataobjects.getRasterSublayer(filename, self.parameterDefinition())
  1296. if isinstance(self.combo, QgsProcessingMapLayerComboBox):
  1297. self.combo.setValue(filename, self.context)
  1298. elif isinstance(self.combo, QgsMapLayerComboBox):
  1299. items = self.combo.additionalItems()
  1300. items.append(filename)
  1301. self.combo.setAdditionalItems(items)
  1302. self.combo.setCurrentIndex(self.combo.findText(filename))
  1303. else:
  1304. self.combo.setEditText(filename)
  1305. self.widgetValueHasChanged.emit(self)
  1306. def setValue(self, value):
  1307. if value is None or value == NULL:
  1308. return
  1309. if self.dialogType == DIALOG_STANDARD:
  1310. if isinstance(value, str):
  1311. layer = QgsProject.instance().mapLayer(value)
  1312. if layer is not None:
  1313. value = layer
  1314. self.combo.setValue(value, self.context)
  1315. elif self.dialogType == DIALOG_BATCH:
  1316. return self.widget.setValue(value)
  1317. else:
  1318. self.setComboValue(value, combobox=self.combo)
  1319. self.widgetValueHasChanged.emit(self)
  1320. def value(self):
  1321. if self.dialogType == DIALOG_STANDARD:
  1322. return self.combo.value()
  1323. elif self.dialogType == DIALOG_BATCH:
  1324. return self.widget.getValue()
  1325. else:
  1326. def validator(v):
  1327. return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  1328. return self.comboValue(validator, combobox=self.combo)
  1329. class TableFieldWidgetWrapper(WidgetWrapper):
  1330. NOT_SET = '[Not set]'
  1331. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  1332. super().__init__(param, dialog, row, col, **kwargs)
  1333. """
  1334. .. deprecated:: 3.12
  1335. Do not use, will be removed in QGIS 4.0
  1336. """
  1337. from warnings import warn
  1338. warn("TableFieldWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  1339. self.context = dataobjects.createContext()
  1340. def createWidget(self):
  1341. self._layer = None
  1342. self.parent_file_based_layers = {}
  1343. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1344. if self.parameterDefinition().allowMultiple():
  1345. return MultipleInputPanel(options=[])
  1346. else:
  1347. widget = QgsFieldComboBox()
  1348. widget.setAllowEmptyFieldName(self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional)
  1349. widget.fieldChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  1350. if self.parameterDefinition().dataType() == QgsProcessingParameterField.Numeric:
  1351. widget.setFilters(QgsFieldProxyModel.Numeric)
  1352. elif self.parameterDefinition().dataType() == QgsProcessingParameterField.String:
  1353. widget.setFilters(QgsFieldProxyModel.String)
  1354. elif self.parameterDefinition().dataType() == QgsProcessingParameterField.DateTime:
  1355. widget.setFilters(QgsFieldProxyModel.Date | QgsFieldProxyModel.Time)
  1356. return widget
  1357. else:
  1358. widget = QComboBox()
  1359. widget.setEditable(True)
  1360. fields = self.dialog.getAvailableValuesOfType([QgsProcessingParameterField, QgsProcessingParameterString],
  1361. [QgsProcessingOutputString])
  1362. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  1363. widget.addItem(self.NOT_SET, self.NOT_SET_OPTION)
  1364. for f in fields:
  1365. widget.addItem(self.dialog.resolveValueDescription(f), f)
  1366. widget.setToolTip(
  1367. self.tr(
  1368. 'Input parameter, or name of field (separate field names with ; for multiple field parameters)'))
  1369. return widget
  1370. def postInitialize(self, wrappers):
  1371. for wrapper in wrappers:
  1372. if wrapper.parameterDefinition().name() == self.parameterDefinition().parentLayerParameterName():
  1373. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1374. self.setLayer(wrapper.parameterValue())
  1375. wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
  1376. break
  1377. def parentValueChanged(self, wrapper):
  1378. value = wrapper.parameterValue()
  1379. if isinstance(value, str) and value in self.parent_file_based_layers:
  1380. self.setLayer(self.parent_file_based_layers[value])
  1381. else:
  1382. self.setLayer(value)
  1383. if isinstance(value, str):
  1384. self.parent_file_based_layers[value] = self._layer
  1385. def setLayer(self, layer):
  1386. if isinstance(layer, QgsProcessingFeatureSourceDefinition):
  1387. layer, ok = layer.source.valueAsString(self.context.expressionContext())
  1388. if isinstance(layer, str):
  1389. if not layer: # empty string
  1390. layer = None
  1391. else:
  1392. layer = QgsProcessingUtils.mapLayerFromString(layer, self.context)
  1393. if not isinstance(layer, QgsVectorLayer) or not layer.isValid():
  1394. self.dialog.messageBar().clearWidgets()
  1395. self.dialog.messageBar().pushMessage("", self.tr("Could not load selected layer/table. Dependent field could not be populated"),
  1396. level=Qgis.Warning, duration=5)
  1397. return
  1398. self._layer = layer
  1399. self.refreshItems()
  1400. if self.parameterDefinition().allowMultiple() and self.parameterDefinition().defaultToAllFields():
  1401. self.setValue(self.getFields())
  1402. def refreshItems(self):
  1403. if self.parameterDefinition().allowMultiple():
  1404. self.widget.updateForOptions(self.getFields())
  1405. else:
  1406. self.widget.setLayer(self._layer)
  1407. self.widget.setCurrentIndex(0)
  1408. if self.parameterDefinition().defaultValue() is not None:
  1409. self.setValue(self.parameterDefinition().defaultValue())
  1410. def getFields(self):
  1411. if self._layer is None:
  1412. return []
  1413. fieldTypes = []
  1414. if self.parameterDefinition().dataType() == QgsProcessingParameterField.String:
  1415. fieldTypes = [QVariant.String]
  1416. elif self.parameterDefinition().dataType() == QgsProcessingParameterField.Numeric:
  1417. fieldTypes = [QVariant.Int, QVariant.Double, QVariant.LongLong,
  1418. QVariant.UInt, QVariant.ULongLong]
  1419. elif self.parameterDefinition().dataType() == QgsProcessingParameterField.DateTime:
  1420. fieldTypes = [QVariant.Date, QVariant.Time, QVariant.DateTime]
  1421. fieldNames = []
  1422. for field in self._layer.fields():
  1423. if not fieldTypes or field.type() in fieldTypes:
  1424. fieldNames.append(str(field.name()))
  1425. return fieldNames
  1426. def setValue(self, value):
  1427. if value is None or value == NULL:
  1428. return
  1429. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1430. if self.parameterDefinition().allowMultiple():
  1431. options = self.widget.options
  1432. selected = []
  1433. if isinstance(value, str):
  1434. value = value.split(';')
  1435. for v in value:
  1436. for i, opt in enumerate(options):
  1437. if opt == v:
  1438. selected.append(i)
  1439. # case insensitive check - only do if matching case value is not present
  1440. elif v not in options and opt.lower() == v.lower():
  1441. selected.append(i)
  1442. self.widget.setSelectedItems(selected)
  1443. else:
  1444. self.widget.setField(value)
  1445. else:
  1446. self.setComboValue(value)
  1447. def value(self):
  1448. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1449. if self.parameterDefinition().allowMultiple():
  1450. return [self.widget.options[i] for i in self.widget.selectedoptions]
  1451. else:
  1452. f = self.widget.currentField()
  1453. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional and not f:
  1454. return None
  1455. return f
  1456. else:
  1457. def validator(v):
  1458. return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  1459. return self.comboValue(validator)
  1460. class BandWidgetWrapper(WidgetWrapper):
  1461. NOT_SET = '[Not set]'
  1462. def __init__(self, param, dialog, row=0, col=0, **kwargs):
  1463. """
  1464. .. deprecated:: 3.14
  1465. Do not use, will be removed in QGIS 4.0
  1466. """
  1467. from warnings import warn
  1468. warn("BandWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
  1469. super().__init__(param, dialog, row, col, **kwargs)
  1470. self.context = dataobjects.createContext()
  1471. def createWidget(self):
  1472. self._layer = None
  1473. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1474. if self.parameterDefinition().allowMultiple():
  1475. return MultipleInputPanel(options=[])
  1476. widget = QgsRasterBandComboBox()
  1477. widget.setShowNotSetOption(self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional)
  1478. widget.bandChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
  1479. return widget
  1480. else:
  1481. widget = QComboBox()
  1482. widget.setEditable(True)
  1483. fields = self.dialog.getAvailableValuesOfType([QgsProcessingParameterBand, QgsProcessingParameterDistance, QgsProcessingParameterNumber],
  1484. [QgsProcessingOutputNumber])
  1485. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
  1486. widget.addItem(self.NOT_SET, self.NOT_SET_OPTION)
  1487. for f in fields:
  1488. widget.addItem(self.dialog.resolveValueDescription(f), f)
  1489. return widget
  1490. def postInitialize(self, wrappers):
  1491. for wrapper in wrappers:
  1492. if wrapper.parameterDefinition().name() == self.parameterDefinition().parentLayerParameterName():
  1493. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1494. self.setLayer(wrapper.parameterValue())
  1495. wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
  1496. break
  1497. def parentValueChanged(self, wrapper):
  1498. self.setLayer(wrapper.parameterValue())
  1499. def setLayer(self, layer):
  1500. if isinstance(layer, QgsProcessingParameterRasterLayer):
  1501. layer, ok = layer.source.valueAsString(self.context.expressionContext())
  1502. if isinstance(layer, str):
  1503. layer = QgsProcessingUtils.mapLayerFromString(layer, self.context)
  1504. self._layer = layer
  1505. self.refreshItems()
  1506. def getBands(self):
  1507. bands = []
  1508. if self._layer is not None:
  1509. provider = self._layer.dataProvider()
  1510. for band in range(1, provider.bandCount() + 1):
  1511. name = provider.generateBandName(band)
  1512. interpretation = provider.colorInterpretationName(band)
  1513. if interpretation != "Undefined":
  1514. name = name + f' ({interpretation})'
  1515. bands.append(name)
  1516. return bands
  1517. def refreshItems(self):
  1518. if self.param.allowMultiple():
  1519. self.widget.setSelectedItems([])
  1520. self.widget.updateForOptions(self.getBands())
  1521. else:
  1522. self.widget.setLayer(self._layer)
  1523. self.widget.setCurrentIndex(0)
  1524. def setValue(self, value):
  1525. if value is None or value == NULL:
  1526. return
  1527. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1528. if self.parameterDefinition().allowMultiple():
  1529. options = self.widget.options
  1530. selected = []
  1531. if isinstance(value, str):
  1532. value = value.split(';')
  1533. for v in value:
  1534. for i, opt in enumerate(options):
  1535. match = re.search(f'(?:\\A|[^0-9]){v}(?:\\Z|[^0-9]|)', opt)
  1536. if match:
  1537. selected.append(i)
  1538. self.widget.setSelectedItems(selected)
  1539. else:
  1540. self.widget.setBand(value)
  1541. else:
  1542. self.setComboValue(value)
  1543. def value(self):
  1544. if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
  1545. if self.parameterDefinition().allowMultiple():
  1546. bands = []
  1547. for i in self.widget.selectedoptions:
  1548. match = re.search('(?:\\A|[^0-9])([0-9]+)(?:\\Z|[^0-9]|)', self.widget.options[i])
  1549. if match:
  1550. bands.append(match.group(1))
  1551. return bands
  1552. else:
  1553. f = self.widget.currentBand()
  1554. if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional and not f:
  1555. return None
  1556. return f
  1557. else:
  1558. def validator(v):
  1559. return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional
  1560. return self.comboValue(validator)
  1561. class WidgetWrapperFactory:
  1562. """
  1563. Factory for parameter widget wrappers
  1564. """
  1565. @staticmethod
  1566. def create_wrapper(param, dialog, row=0, col=0):
  1567. wrapper_metadata = param.metadata().get('widget_wrapper', None)
  1568. # VERY messy logic here to avoid breaking 3.0 API which allowed metadata "widget_wrapper" value to be either
  1569. # a string name of a class OR a dict.
  1570. # TODO QGIS 4.0 -- require widget_wrapper to be a dict.
  1571. if wrapper_metadata and (not isinstance(wrapper_metadata, dict) or wrapper_metadata.get('class', None) is not None):
  1572. return WidgetWrapperFactory.create_wrapper_from_metadata(param, dialog, row, col)
  1573. else:
  1574. # try from c++ registry first
  1575. class_type = dialog.__class__.__name__
  1576. if class_type == 'ModelerParametersDialog':
  1577. wrapper = QgsGui.processingGuiRegistry().createModelerParameterWidget(dialog.model,
  1578. dialog.childId,
  1579. param,
  1580. dialog.context)
  1581. else:
  1582. dialog_type = dialogTypes.get(class_type,
  1583. QgsProcessingGui.Standard)
  1584. wrapper = QgsGui.processingGuiRegistry().createParameterWidgetWrapper(param, dialog_type)
  1585. if wrapper is not None:
  1586. wrapper.setDialog(dialog)
  1587. return wrapper
  1588. # fallback to Python registry
  1589. return WidgetWrapperFactory.create_wrapper_from_class(param, dialog, row, col)
  1590. @staticmethod
  1591. def create_wrapper_from_metadata(param, dialog, row=0, col=0):
  1592. wrapper = param.metadata().get('widget_wrapper', None)
  1593. params = {}
  1594. # wrapper metadata should be a dict with class key
  1595. if isinstance(wrapper, dict):
  1596. params = deepcopy(wrapper)
  1597. wrapper = params.pop('class')
  1598. # wrapper metadata should be a class path
  1599. if isinstance(wrapper, str):
  1600. tokens = wrapper.split('.')
  1601. mod = __import__('.'.join(tokens[:-1]), fromlist=[tokens[-1]])
  1602. wrapper = getattr(mod, tokens[-1])
  1603. # or directly a class object
  1604. if isclass(wrapper):
  1605. wrapper = wrapper(param, dialog, row, col, **params)
  1606. # or a wrapper instance
  1607. return wrapper
  1608. @staticmethod
  1609. def create_wrapper_from_class(param, dialog, row=0, col=0):
  1610. wrapper = None
  1611. if param.type() == 'boolean':
  1612. # deprecated, moved to c++
  1613. wrapper = BooleanWidgetWrapper
  1614. elif param.type() == 'crs':
  1615. # deprecated, moved to c++
  1616. wrapper = CrsWidgetWrapper
  1617. elif param.type() == 'extent':
  1618. # deprecated, moved to c++
  1619. wrapper = ExtentWidgetWrapper
  1620. elif param.type() == 'point':
  1621. # deprecated, moved to c++
  1622. wrapper = PointWidgetWrapper
  1623. elif param.type() == 'file':
  1624. # deprecated, moved to c++
  1625. wrapper = FileWidgetWrapper
  1626. elif param.type() == 'multilayer':
  1627. wrapper = MultipleLayerWidgetWrapper
  1628. elif param.type() == 'number':
  1629. # deprecated, moved to c++
  1630. wrapper = NumberWidgetWrapper
  1631. elif param.type() == 'distance':
  1632. # deprecated, moved to c++
  1633. wrapper = DistanceWidgetWrapper
  1634. elif param.type() == 'raster':
  1635. # deprecated, moved to c++
  1636. wrapper = RasterWidgetWrapper
  1637. elif param.type() == 'enum':
  1638. # deprecated, moved to c++
  1639. wrapper = EnumWidgetWrapper
  1640. elif param.type() == 'string':
  1641. # deprecated, moved to c++
  1642. wrapper = StringWidgetWrapper
  1643. elif param.type() == 'expression':
  1644. # deprecated, moved to c++
  1645. wrapper = ExpressionWidgetWrapper
  1646. elif param.type() == 'vector':
  1647. # deprecated, moved to c++
  1648. wrapper = VectorLayerWidgetWrapper
  1649. elif param.type() == 'field':
  1650. # deprecated, moved to c++
  1651. wrapper = TableFieldWidgetWrapper
  1652. elif param.type() == 'source':
  1653. # deprecated, moved to c++
  1654. wrapper = FeatureSourceWidgetWrapper
  1655. elif param.type() == 'band':
  1656. # deprecated, moved to c++
  1657. wrapper = BandWidgetWrapper
  1658. elif param.type() == 'layer':
  1659. # deprecated, moved to c++
  1660. wrapper = MapLayerWidgetWrapper
  1661. elif param.type() == 'range':
  1662. # deprecated, moved to c++
  1663. wrapper = RangeWidgetWrapper
  1664. elif param.type() == 'matrix':
  1665. # deprecated, moved to c++
  1666. wrapper = FixedTableWidgetWrapper
  1667. elif param.type() == 'mesh':
  1668. # deprecated, moved to c++
  1669. wrapper = MeshWidgetWrapper
  1670. else:
  1671. assert False, param.type()
  1672. return wrapper(param, dialog, row, col)