scipion-pyworkflow 3.11.0__py3-none-any.whl → 3.11.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. pyworkflow/apps/__init__.py +29 -0
  2. pyworkflow/apps/pw_manager.py +37 -0
  3. pyworkflow/apps/pw_plot.py +51 -0
  4. pyworkflow/apps/pw_project.py +130 -0
  5. pyworkflow/apps/pw_protocol_list.py +143 -0
  6. pyworkflow/apps/pw_protocol_run.py +51 -0
  7. pyworkflow/apps/pw_run_tests.py +268 -0
  8. pyworkflow/apps/pw_schedule_run.py +322 -0
  9. pyworkflow/apps/pw_sleep.py +37 -0
  10. pyworkflow/apps/pw_sync_data.py +440 -0
  11. pyworkflow/apps/pw_viewer.py +78 -0
  12. pyworkflow/constants.py +1 -1
  13. pyworkflow/gui/__init__.py +36 -0
  14. pyworkflow/gui/browser.py +768 -0
  15. pyworkflow/gui/canvas.py +1190 -0
  16. pyworkflow/gui/dialog.py +981 -0
  17. pyworkflow/gui/form.py +2727 -0
  18. pyworkflow/gui/graph.py +247 -0
  19. pyworkflow/gui/graph_layout.py +271 -0
  20. pyworkflow/gui/gui.py +571 -0
  21. pyworkflow/gui/matplotlib_image.py +233 -0
  22. pyworkflow/gui/plotter.py +247 -0
  23. pyworkflow/gui/project/__init__.py +25 -0
  24. pyworkflow/gui/project/base.py +193 -0
  25. pyworkflow/gui/project/constants.py +139 -0
  26. pyworkflow/gui/project/labels.py +205 -0
  27. pyworkflow/gui/project/project.py +491 -0
  28. pyworkflow/gui/project/searchprotocol.py +240 -0
  29. pyworkflow/gui/project/searchrun.py +181 -0
  30. pyworkflow/gui/project/steps.py +171 -0
  31. pyworkflow/gui/project/utils.py +332 -0
  32. pyworkflow/gui/project/variables.py +179 -0
  33. pyworkflow/gui/project/viewdata.py +472 -0
  34. pyworkflow/gui/project/viewprojects.py +519 -0
  35. pyworkflow/gui/project/viewprotocols.py +2141 -0
  36. pyworkflow/gui/project/viewprotocols_extra.py +562 -0
  37. pyworkflow/gui/text.py +774 -0
  38. pyworkflow/gui/tooltip.py +185 -0
  39. pyworkflow/gui/tree.py +684 -0
  40. pyworkflow/gui/widgets.py +307 -0
  41. pyworkflow/mapper/__init__.py +26 -0
  42. pyworkflow/mapper/mapper.py +226 -0
  43. pyworkflow/mapper/sqlite.py +1583 -0
  44. pyworkflow/mapper/sqlite_db.py +145 -0
  45. pyworkflow/object.py +1 -0
  46. pyworkflow/plugin.py +4 -4
  47. pyworkflow/project/__init__.py +31 -0
  48. pyworkflow/project/config.py +454 -0
  49. pyworkflow/project/manager.py +180 -0
  50. pyworkflow/project/project.py +2095 -0
  51. pyworkflow/project/usage.py +165 -0
  52. pyworkflow/protocol/__init__.py +38 -0
  53. pyworkflow/protocol/bibtex.py +48 -0
  54. pyworkflow/protocol/constants.py +87 -0
  55. pyworkflow/protocol/executor.py +515 -0
  56. pyworkflow/protocol/hosts.py +318 -0
  57. pyworkflow/protocol/launch.py +277 -0
  58. pyworkflow/protocol/package.py +42 -0
  59. pyworkflow/protocol/params.py +781 -0
  60. pyworkflow/protocol/protocol.py +2712 -0
  61. pyworkflow/resources/protlabels.xcf +0 -0
  62. pyworkflow/resources/sprites.png +0 -0
  63. pyworkflow/resources/sprites.xcf +0 -0
  64. pyworkflow/template.py +1 -1
  65. pyworkflow/tests/__init__.py +29 -0
  66. pyworkflow/tests/test_utils.py +25 -0
  67. pyworkflow/tests/tests.py +342 -0
  68. pyworkflow/utils/__init__.py +38 -0
  69. pyworkflow/utils/dataset.py +414 -0
  70. pyworkflow/utils/echo.py +104 -0
  71. pyworkflow/utils/graph.py +169 -0
  72. pyworkflow/utils/log.py +293 -0
  73. pyworkflow/utils/path.py +528 -0
  74. pyworkflow/utils/process.py +154 -0
  75. pyworkflow/utils/profiler.py +92 -0
  76. pyworkflow/utils/progressbar.py +154 -0
  77. pyworkflow/utils/properties.py +618 -0
  78. pyworkflow/utils/reflection.py +129 -0
  79. pyworkflow/utils/utils.py +880 -0
  80. pyworkflow/utils/which.py +229 -0
  81. pyworkflow/webservices/__init__.py +8 -0
  82. pyworkflow/webservices/config.py +8 -0
  83. pyworkflow/webservices/notifier.py +152 -0
  84. pyworkflow/webservices/repository.py +59 -0
  85. pyworkflow/webservices/workflowhub.py +86 -0
  86. pyworkflowtests/tests/__init__.py +0 -0
  87. pyworkflowtests/tests/test_canvas.py +72 -0
  88. pyworkflowtests/tests/test_domain.py +45 -0
  89. pyworkflowtests/tests/test_logs.py +74 -0
  90. pyworkflowtests/tests/test_mappers.py +392 -0
  91. pyworkflowtests/tests/test_object.py +507 -0
  92. pyworkflowtests/tests/test_project.py +42 -0
  93. pyworkflowtests/tests/test_protocol_execution.py +146 -0
  94. pyworkflowtests/tests/test_protocol_export.py +78 -0
  95. pyworkflowtests/tests/test_protocol_output.py +158 -0
  96. pyworkflowtests/tests/test_streaming.py +47 -0
  97. pyworkflowtests/tests/test_utils.py +210 -0
  98. {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/METADATA +2 -2
  99. scipion_pyworkflow-3.11.2.dist-info/RECORD +162 -0
  100. scipion_pyworkflow-3.11.0.dist-info/RECORD +0 -71
  101. {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/WHEEL +0 -0
  102. {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/entry_points.txt +0 -0
  103. {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/licenses/LICENSE.txt +0 -0
  104. {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,781 @@
1
+ # **************************************************************************
2
+ # *
3
+ # * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
4
+ # *
5
+ # * [1] SciLifeLab, Stockholm University
6
+ # *
7
+ # * This program is free software: you can redistribute it and/or modify
8
+ # * it under the terms of the GNU General Public License as published by
9
+ # * the Free Software Foundation, either version 3 of the License, or
10
+ # * (at your option) any later version.
11
+ # *
12
+ # * This program is distributed in the hope that it will be useful,
13
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # * GNU General Public License for more details.
16
+ # *
17
+ # * You should have received a copy of the GNU General Public License
18
+ # * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ # *
20
+ # * All comments concerning this program package may be sent to the
21
+ # * e-mail address 'scipion@cnb.csic.es'
22
+ # *
23
+ # **************************************************************************
24
+
25
+
26
+ import re
27
+ import collections
28
+
29
+ from pyworkflow.object import *
30
+ from .constants import *
31
+
32
+ BIN_THREADS_PARAM = 'binThreads'
33
+ PARALLELIZATION = 'Parallelization'
34
+
35
+ class FormElement(Object):
36
+ """Base for any element on the form"""
37
+ ATTRIBUTES = ['label', 'expertLevel', 'condition', 'important', 'help',
38
+ 'default', 'paramClass']
39
+
40
+ def __init__(self, **args):
41
+ super().__init__(**args)
42
+ self.label = String(args.get('label', None))
43
+ self.expertLevel = Integer(args.get('expertLevel', LEVEL_NORMAL))
44
+ self.condition = String(args.get('condition', None))
45
+ self._isImportant = Boolean(args.get('important', False))
46
+ self.help = String(args.get('help', None))
47
+ # This two list will be filled by the Form
48
+ # which have a global-view of all parameters
49
+ # All param names in which condition appears this param
50
+ self._dependants = []
51
+ # All param names that appears in the condition
52
+ self._conditionParams = []
53
+
54
+ def isExpert(self):
55
+ return self.expertLevel > LEVEL_NORMAL
56
+
57
+ def setExpert(self):
58
+ self.expertLevel.set(LEVEL_ADVANCED)
59
+
60
+ def isImportant(self):
61
+ return self._isImportant.get()
62
+
63
+ def setImportant(self, value):
64
+ self._isImportant.set(value)
65
+
66
+ def hasCondition(self):
67
+ return self.condition.hasValue()
68
+
69
+ def getLabel(self):
70
+ return self.label.get()
71
+
72
+ def getHelp(self):
73
+ return self.help.get()
74
+
75
+ def config(self, **kwargs):
76
+ """ Configure the object and set attributes
77
+ coming in the keyword-arguments, the
78
+ same as in the __init__
79
+ """
80
+ for key in self.ATTRIBUTES:
81
+ if key in kwargs:
82
+ self.setAttributeValue(key, kwargs.get(key))
83
+
84
+
85
+ class Param(FormElement):
86
+ """Definition of a protocol parameter"""
87
+ def __init__(self, **args):
88
+ FormElement.__init__(self, **args)
89
+ # This should be defined in subclasses
90
+ self.paramClass = args.get('paramClass', None)
91
+ self.default = String(args.get('default', None))
92
+
93
+ # Allow pointers (used for scalars)
94
+ self.allowsPointers = args.get('allowsPointers', False)
95
+ self.validators = args.get('validators', [])
96
+ self.readOnly = args.get("readOnly", False)
97
+
98
+ def __str__(self):
99
+ return " label: %s" % self.label.get()
100
+
101
+ def addValidator(self, validator):
102
+ """ Validators should be callables that
103
+ receive a value and return a list of errors if so.
104
+ If everything is ok, the result should be an empty list.
105
+ """
106
+ self.validators.append(validator)
107
+
108
+ def validate(self, value):
109
+ errors = []
110
+ for val in self.validators:
111
+ errors += val(value)
112
+ return errors
113
+
114
+ def getDefault(self):
115
+ return self.default.get()
116
+
117
+ def setDefault(self, newDefault):
118
+ self.default.set(newDefault)
119
+
120
+
121
+ class ElementGroup(FormElement):
122
+ """ Class to group some params in the form.
123
+ Such as: Labeled group or params in the same line.
124
+ """
125
+ def __init__(self, form=None, **args):
126
+ FormElement.__init__(self, **args)
127
+ self._form = form
128
+ self._paramList = []
129
+
130
+ def iterParams(self):
131
+ """ Return key and param for every child param. """
132
+ for name in self._paramList:
133
+ yield name, self._form.getParam(name)
134
+
135
+ def addParam(self, paramName, ParamClass, **kwargs):
136
+ """Add a new param to the group"""
137
+ param = ParamClass(**kwargs)
138
+ self._paramList.append(paramName)
139
+ self._form.registerParam(paramName, param)
140
+ return param
141
+
142
+ def addHidden(self, paramName, ParamClass, **kwargs):
143
+ """Add a hidden parameter to be used in conditions. """
144
+ kwargs.update({'label': '', 'condition': 'False'})
145
+ self.addParam(paramName, ParamClass, **kwargs)
146
+
147
+ def addLine(self, lineName, **kwargs):
148
+
149
+ labelName = lineName
150
+ for symbol in ' ()':
151
+ labelName = labelName.replace(symbol, '_')
152
+
153
+ return self.addParam(labelName, Line, form=self._form,
154
+ label=lineName, **kwargs)
155
+
156
+
157
+ # ----------- Some type of ElementGroup --------------------------
158
+
159
+ class Line(ElementGroup):
160
+ """ Group to put some parameters in the same line. """
161
+ pass
162
+
163
+
164
+ class Group(ElementGroup):
165
+ """ Group some parameters with a labeled frame. """
166
+ pass
167
+
168
+
169
+ class Section(ElementGroup):
170
+ """Definition of a section to hold other params"""
171
+ def __init__(self, form, **args):
172
+ ElementGroup.__init__(self, form, **args)
173
+ self.questionParam = String(args.get('questionParam', ''))
174
+
175
+ def hasQuestion(self):
176
+ """Return True if a question param was set"""
177
+ return self.questionParam.get() in self._paramList
178
+
179
+ def getQuestionName(self):
180
+ """ Return the name of the question param. """
181
+ return self.questionParam.get()
182
+
183
+ def getQuestion(self):
184
+ """ Return the question param"""
185
+ return self._form.getParam(self.questionParam.get())
186
+
187
+ def addGroup(self, groupName, **kwargs):
188
+ labelName = groupName
189
+ for symbol in ' ()':
190
+ labelName = labelName.replace(symbol, '_')
191
+
192
+ return self.addParam(labelName, Group, form=self._form,
193
+ label=groupName, **kwargs)
194
+
195
+
196
+ class Form(object):
197
+ """Store all sections and parameters"""
198
+ def __init__(self, protocol):
199
+ """ Build a Form from a given protocol. """
200
+ object.__init__(self)
201
+ self._sectionList = [] # Store list of sections
202
+ # Dictionary to store all params, grouped by sections
203
+ self._paramsDict = collections.OrderedDict()
204
+ self._lastSection = None
205
+ self._protocol = protocol
206
+ self.addGeneralSection()
207
+
208
+ def getClass(self):
209
+ return type(self)
210
+
211
+ def addSection(self, label='', updateSection=True, **kwargs):
212
+ """Add a new section"""
213
+ newSection = Section(self, label=label, **kwargs)
214
+ if updateSection:
215
+ self.lastSection = newSection
216
+ self._sectionList.append(newSection)
217
+ return newSection
218
+
219
+ def getSection(self, label):
220
+ """ get section by label from _sectionList"""
221
+ for s in self._sectionList:
222
+ if s.label == label:
223
+ return s
224
+ return
225
+
226
+ def hasSection(self, label):
227
+ return self.getSection(label) is not None
228
+
229
+ def addGroup(self, *args, **kwargs):
230
+ return self.lastSection.addGroup(*args, **kwargs)
231
+
232
+ def addLine(self, *args, **kwargs):
233
+ return self.lastSection.addLine(*args, **kwargs)
234
+
235
+ def registerParam(self, paramName, param):
236
+ """ Register a given param in the form. """
237
+ self._paramsDict[paramName] = param
238
+ self._analizeCondition(paramName, param)
239
+
240
+ def addParam(self, *args, **kwargs):
241
+ """Add a new param to last section"""
242
+ if args[0] == BIN_THREADS_PARAM:
243
+ section = self.getParallelSection(updateSection=False)
244
+ else:
245
+ section = self.lastSection
246
+ return section.addParam(*args, **kwargs)
247
+
248
+ # Adhoc method for specific params
249
+ def addBooleanParam(self, name, label, help, default=True, **kwargs):
250
+ return self.addParam(name, BooleanParam, label=label, help=help, default=default, **kwargs)
251
+
252
+ def addHidden(self, *args, **kwargs):
253
+ return self.lastSection.addHidden(*args, **kwargs)
254
+
255
+ def _analizeCondition(self, paramName, param):
256
+ if param.hasCondition():
257
+ param._conditionParams = []
258
+ tokens = re.split(r'\W+', param.condition.get())
259
+ for t in tokens:
260
+ if self.hasParam(t):
261
+ self.getParam(t)._dependants.append(paramName)
262
+ param._conditionParams.append(t)
263
+ if self._protocol.hasAttribute(t):
264
+ param._conditionParams.append(t)
265
+
266
+ def evalParamCondition(self, paramName):
267
+ """Evaluate if a condition is True for a give param
268
+ with the values of a particular Protocol"""
269
+ param = self.getParam(paramName)
270
+
271
+ if not param.hasCondition():
272
+ return True
273
+ condStr = param.condition.get()
274
+ localDict = {}
275
+ globalDict = dict(globals())
276
+ # FIXME: Check why this import is here
277
+ from pyworkflow import Config
278
+ globalDict.update(Config.getDomain().getObjects())
279
+
280
+ for t in param._conditionParams:
281
+ if self.hasParam(t) or self._protocol.hasAttribute(t):
282
+ localDict[t] = self._protocol.getAttributeValue(t)
283
+
284
+ return eval(condStr, globalDict, localDict)
285
+
286
+ def validateParams(self, protocol):
287
+ """ Check that all validations of the params in the form
288
+ are met for the protocol param values.
289
+ It will return a list with errors, just in the same
290
+ way of the Protocol.validate function
291
+ """
292
+ errors = []
293
+
294
+ for name, param in self.iterParams():
295
+ value = protocol.getAttributeValue(name)
296
+ errors += param.validate(value)
297
+
298
+ return errors
299
+
300
+ def getParam(self, paramName):
301
+ """Retrieve a param given a the param name
302
+ None is returned if not found
303
+ """
304
+ return self._paramsDict.get(paramName, None)
305
+
306
+ def hasParam(self, paramName):
307
+ return paramName in self._paramsDict
308
+
309
+ def __str__(self):
310
+ s = "Form: \n"
311
+ for section in self.iterSections():
312
+ s += str(section)
313
+ return s
314
+
315
+ def iterSections(self):
316
+ return self._sectionList
317
+
318
+ def iterAllParams(self):
319
+ """ Iter all parameters, including ElementGroups. """
320
+ return self._paramsDict.items()
321
+
322
+ def iterParams(self):
323
+ """ Iter parameters disregarding the ElementGroups. """
324
+ for k, v in self._paramsDict.items():
325
+ if not isinstance(v, ElementGroup):
326
+ yield k, v
327
+
328
+ def iterPointerParams(self):
329
+ for paramName, param in self._paramsDict.items():
330
+ if isinstance(param, PointerParam):
331
+ yield paramName, param
332
+
333
+ def addGeneralSection(self):
334
+ self.addSection(label='General')
335
+ self.addParam('runName', StringParam, label="Run name:", important=True,
336
+ help='Select run name label to identify this run.')
337
+ self.addParam('runMode', EnumParam, choices=['resume', 'restart'],
338
+ label="Run mode", display=EnumParam.DISPLAY_COMBO, default=0,
339
+ help='The <resume> mode will try to start the execution'
340
+ 'from the last successfully finished step if possible.'
341
+ 'On the contrary, <restart> will delete all previous results'
342
+ 'of this particular run and start from the beginning. This option'
343
+ 'should be used carefully.'
344
+ )
345
+
346
+ def getParallelSection(self, updateSection=True):
347
+ section = self.getSection(PARALLELIZATION)
348
+ return section if section else self.addSection(label=PARALLELIZATION, updateSection=updateSection)
349
+
350
+ def addParallelSection(self, threads=1, mpi=8, binThreads=0, binThreadsHelp=None):
351
+
352
+ """ Adds the parallelization section to the form
353
+ pass threads=0 to disable threads parameter and mpi=0 to disable mpi params
354
+
355
+ :param threads: default value for of threads, defaults to 1
356
+ :param mpi: default value for mpi, defaults to 8
357
+ :param binThreads: Threads to pass as an argument to the program
358
+ """
359
+ self.addSection(label=PARALLELIZATION)
360
+ self.addParam('hostName', StringParam, default="localhost",
361
+ label='Execution host',
362
+ help='Select in which of the available do you want to launch this protocol.')
363
+
364
+ # WARNING. THis is confusing but is described here. For legacy reasons it is not obvious how to disentangle this
365
+ # threads ahs 2 meanings:
366
+ # 1.- threads for the binary when execution mode is serial
367
+ # 2.- threads for Scipion when execution mode is parallel
368
+ # In this case (#2), there could be a binThreads which are the binary threads as in #1 case
369
+
370
+ binLabel = "Threads"
371
+ binHelpMsg = ("*Threads*:\nThis refers to different execution threads in the same process that "
372
+ "can share memory. They run in the same computer. This value is an argument"
373
+ " passed to the program integrated")
374
+ binHelpMsg = binThreadsHelp if binThreadsHelp else binHelpMsg
375
+
376
+ if threads > 0:
377
+
378
+ label= "Scipion threads"
379
+ helpMsg= ("*Scipion threads*:\n threads created by Scipion to run the steps."
380
+ " 1 thread is always used by the master/main process. Then extra threads will allow"
381
+ " this protocol to run several steps at the same time, taking always into account "
382
+ "restrictions to previous steps and 'theoretical GPU availability'")
383
+
384
+ if self._protocol.modeSerial():
385
+ label = binLabel
386
+ helpMsg = binHelpMsg
387
+
388
+
389
+ self.addParam('numberOfThreads', IntParam, default=threads,
390
+ label=label, help=helpMsg)
391
+ if mpi > 0:
392
+ mpiHelp=("*MPI*:\nThis is a number of independent processes"
393
+ " that communicate through message passing "
394
+ "over the network (or the same computer).\n")
395
+ self.addParam('numberOfMpi', IntParam, default=mpi,
396
+ label='MPIs', help=mpiHelp)
397
+ if binThreads:
398
+ if self._protocol.modeParallel():
399
+ self.addParam(BIN_THREADS_PARAM, IntParam, default=binThreads,
400
+ label=binLabel, help=binHelpMsg)
401
+ else:
402
+ logger.warning("binThreads can't be used when stepsExecutionMode is not STEPS_PARALLEL. Use threads instead.")
403
+
404
+
405
+ class StringParam(Param):
406
+ """Param with underlying String value"""
407
+ def __init__(self, **args):
408
+ Param.__init__(self, paramClass=String, **args)
409
+
410
+
411
+ class TextParam(StringParam):
412
+ """Long string params"""
413
+ def __init__(self, **args):
414
+ StringParam.__init__(self, **args)
415
+ self.height = args.get('height', 5)
416
+ self.width = args.get('width', 30)
417
+
418
+
419
+ class RegexParam(StringParam):
420
+ """Regex based string param"""
421
+ pass
422
+
423
+
424
+ class PathParam(StringParam):
425
+ """Param for path strings"""
426
+ pass
427
+
428
+
429
+ # TODO: Handle filter pattern
430
+ class FileParam(PathParam):
431
+ """Filename path"""
432
+ pass
433
+
434
+
435
+ class FolderParam(PathParam):
436
+ """Folder path"""
437
+ pass
438
+
439
+
440
+ class LabelParam(StringParam):
441
+ """ Just the same as StringParam, but to be rendered
442
+ as a label and can not be directly edited by the user
443
+ in the Protocol Form.
444
+ """
445
+ pass
446
+
447
+
448
+ class IntParam(Param):
449
+ def __init__(self, **args):
450
+ Param.__init__(self, paramClass=Integer, **args)
451
+ self.addValidator(Format(int, error="should be an integer",
452
+ allowsNull=args.get('allowsNull', False)))
453
+
454
+
455
+ class EnumParam(IntParam):
456
+ """Select from a list of values, separated by comma"""
457
+ # Possible values for display
458
+ DISPLAY_LIST = 0
459
+ DISPLAY_COMBO = 1
460
+ DISPLAY_HLIST = 2 # horizontal list, save space
461
+
462
+ def __init__(self, **args):
463
+ IntParam.__init__(self, **args)
464
+ self.choices = args.get('choices', [])
465
+ self.display = Integer(args.get('display', EnumParam.DISPLAY_COMBO))
466
+
467
+
468
+ class FloatParam(Param):
469
+ def __init__(self, **args):
470
+ Param.__init__(self, paramClass=Float, **args)
471
+ self.addValidator(Format(float, error="should be a float",
472
+ allowsNull=args.get('allowsNull', False)))
473
+
474
+
475
+ class BooleanParam(Param):
476
+ """ Param to store boolean values. By default it will be displayed as 2 radio buttons with Yes/no labels.
477
+ Alternatively, if you pass checkbox it will be displayed as a checkbox.
478
+
479
+ :param display: (Optional) default DISPLAY_YES_NO. (Yes /no)
480
+ Alternatively use BooleanParam.DISPLAY_CHECKBOX to use checkboxes """
481
+ DISPLAY_YES_NO = 1
482
+ DISPLAY_CHECKBOX = 2
483
+
484
+ def __init__(self, display=DISPLAY_YES_NO, **args):
485
+ Param.__init__(self, paramClass=Boolean, **args)
486
+ self.display = display
487
+ self.addValidator(NonEmptyBool)
488
+
489
+
490
+ class HiddenBooleanParam(BooleanParam):
491
+ def __init__(self, **args):
492
+ Param.__init__(self, paramClass=Boolean, **args)
493
+
494
+
495
+ class PointerParam(Param):
496
+ """ This type of Param will serve to select existing objects
497
+ in the database that will be input for some protocol.
498
+ """
499
+ def __init__(self, paramClass=Pointer, **args):
500
+ Param.__init__(self, paramClass=paramClass, **args)
501
+ # This will be the class to be pointed
502
+ self.setPointerClass(args['pointerClass'])
503
+ # Some conditions on the pointed candidates
504
+ self.pointerCondition = String(args.get('pointerCondition', None))
505
+ self.allowsNull = Boolean(args.get('allowsNull', False))
506
+
507
+ def setPointerClass(self, newPointerClass):
508
+
509
+ # Tolerate passing classes instead of their names
510
+ if isinstance(newPointerClass, list):
511
+ self.pointerClass = CsvList()
512
+ self.pointerClass.set(",". join([clazz.__name__ for clazz in newPointerClass]))
513
+
514
+ elif(isinstance(newPointerClass, str)):
515
+ if ',' in newPointerClass:
516
+ self.pointerClass = CsvList()
517
+ self.pointerClass.set(newPointerClass)
518
+ else:
519
+ self.pointerClass = String(newPointerClass)
520
+
521
+ # Single class item, not the string
522
+ else:
523
+ self.pointerClass = String(newPointerClass.__name__)
524
+
525
+
526
+ class MultiPointerParam(PointerParam):
527
+ """ This type of Param will serve to select objects
528
+ with DIFFERENT types from the database to be input for some protocol.
529
+ """
530
+ def __init__(self, **args):
531
+ PointerParam.__init__(self, paramClass=PointerList, **args)
532
+ self.maxNumObjects = Integer(args.get('maxNumObjects', 100))
533
+ self.minNumObjects = Integer(args.get('minNumObjects', 2))
534
+
535
+
536
+ class RelationParam(Param):
537
+ """ This type of Param is very similar to PointerParam, since it will
538
+ hold a pointer to another object. But, in the PointerParam, we search
539
+ for objects of some Class (maybe with some conditions).
540
+ Here, we search for objects related to a given attribute of a protocol
541
+ by a given relation.
542
+ """
543
+ def __init__(self, **args):
544
+ Param.__init__(self, paramClass=Pointer, **args)
545
+ # This will be the name of the relation
546
+ self._relationName = String(args.get('relationName'))
547
+ # We will store the attribute name in the protocol to be
548
+ # used as the object for which relations will be search
549
+ self._attributeName = String(args.get('attributeName'))
550
+ # This specify if we want to search for childs or parents
551
+ # of the given attribute of the protocol
552
+ self._direction = Integer(args.get('direction', RELATION_CHILDS))
553
+ self.allowsNull = Boolean(args.get('allowsNull', False))
554
+
555
+ def getName(self):
556
+ return self._relationName.get()
557
+
558
+ def getAttributeName(self):
559
+ return self._attributeName.get()
560
+
561
+ def getDirection(self):
562
+ return self._direction.get()
563
+
564
+
565
+ class ProtocolClassParam(StringParam):
566
+ def __init__(self, **args):
567
+ StringParam.__init__(self, **args)
568
+ self.protocolClassName = String(args.get('protocolClassName'))
569
+ self.allowSubclasses = Boolean(args.get('allowSubclasses', False))
570
+
571
+
572
+ class DigFreqParam(FloatParam):
573
+ """ Digital frequency param. """
574
+ def __init__(self, **args):
575
+ FloatParam.__init__(self, **args)
576
+ self.addValidator(FreqValidator)
577
+
578
+
579
+ class NumericListParam(StringParam):
580
+ """ This class will serve to have list representations as strings.
581
+ Possible notation are:
582
+ 1000 10 1 1 -> to define a list with 4 values [1000, 10, 1, 1], or
583
+ 10x2 5x3 -> to define a list with 5 values [10, 10, 5, 5, 5]
584
+ If you ask for more elements than in the list, the last one is repeated
585
+ """
586
+ def __init__(self, **args):
587
+ StringParam.__init__(self, **args)
588
+ self.addValidator(NumericListValidator())
589
+
590
+
591
+ class NumericRangeParam(StringParam):
592
+ """ This class will serve to specify range of numbers with a string representation.
593
+ Possible notation are::
594
+
595
+ "1,5-8,10" -> [1,5,6,7,8,10]
596
+ "2,6,9-11" -> [2,6,9,10,11]
597
+ "2 5, 6-8" -> [2,5,6,7,8]
598
+
599
+ """
600
+ def __init__(self, **args):
601
+ StringParam.__init__(self, **args)
602
+ self.addValidator(NumericRangeValidator())
603
+
604
+
605
+ class TupleParam(Param):
606
+ """ This class will condense a tuple of several
607
+ other params of the same type and related concept.
608
+ For example: min and max, low and high.
609
+ """
610
+ def __init__(self, **args):
611
+ Param.__init__(self, **args)
612
+
613
+
614
+ class DeprecatedParam:
615
+ """ Deprecated param. To be used when you want to rename an existing param
616
+ and still be able to recover old param value. It acts like a redirector, passing the
617
+ value received when its value is set to the new renamed parameter
618
+
619
+ usage: In defineParams method, before the renamed param definition line add the following:
620
+
621
+ self.oldName = DeprecatedParam("newName", self)
622
+ form.addParam('newName', ...)
623
+
624
+ """
625
+ def __init__(self, newParamName, prot):
626
+ """
627
+
628
+ :param newParamName: Name of the renamed param
629
+ :param prot: Protocol hosting this and the renamed param
630
+
631
+ """
632
+ self._newParamName = newParamName
633
+ self.prot = prot
634
+ # Need to fake being a Object at loading time
635
+ self._objId = None
636
+ self._objIsPointer = False
637
+
638
+ def set(self, value, cleanExtended=False):
639
+ if hasattr(self.prot, self._newParamName):
640
+ newParam = self._getNewParam()
641
+ if newParam.isPointer():
642
+ newParam.set(value, cleanExtended)
643
+ self._extended = newParam._extended
644
+ else:
645
+ newParam.set(value)
646
+
647
+ def isPointer(self):
648
+ return self._getNewParam().isPointer()
649
+
650
+ def getObjValue(self):
651
+ return None
652
+
653
+ def _getNewParam(self):
654
+ return getattr(self.prot, self._newParamName)
655
+
656
+ # -----------------------------------------------------------------------------
657
+ # Validators
658
+ # -----------------------------------------------------------------------------
659
+ class Validator(object):
660
+ pass
661
+
662
+
663
+ class Conditional(Validator):
664
+ """ Simple validation based on a condition.
665
+ If the value doesn't meet the condition,
666
+ the error will be returned.
667
+ """
668
+ def __init__(self, error, allowsNull=False):
669
+ self.error = error
670
+ self._allowsNull = allowsNull
671
+
672
+ def __call__(self, value):
673
+ errors = []
674
+ if value is not None or not self._allowsNull:
675
+ if not self._condition(value):
676
+ errors.append(self.error)
677
+ return errors
678
+
679
+
680
+ class Format(Conditional):
681
+ """ Check if the format is right. """
682
+ def __init__(self, valueType, error='Value have not a correct format',
683
+ allowsNull=False):
684
+ Conditional.__init__(self, error, allowsNull)
685
+ self.valueType = valueType
686
+
687
+ def _condition(self, value):
688
+ try:
689
+ self.valueType(value)
690
+ return True
691
+ except Exception:
692
+ return False
693
+
694
+
695
+ class NonEmptyCondition(Conditional):
696
+ def __init__(self, error='Value cannot be empty'):
697
+ Conditional.__init__(self, error)
698
+ self._condition = lambda value: len(value) > 0
699
+
700
+
701
+ class LT(Conditional):
702
+ def __init__(self, threshold,
703
+ error='Value should be less than the threshold'):
704
+ Conditional.__init__(self, error)
705
+ self._condition = lambda value: value < threshold
706
+
707
+
708
+ class LE(Conditional):
709
+ def __init__(self, threshold,
710
+ error='Value should be less or equal than the threshold'):
711
+ Conditional.__init__(self, error)
712
+ self._condition = lambda value: value <= threshold
713
+
714
+
715
+ class GT(Conditional):
716
+ def __init__(self, threshold,
717
+ error='Value should be greater than the threshold'):
718
+ Conditional.__init__(self, error)
719
+ self._condition = lambda value: value > threshold
720
+
721
+
722
+ class GE(Conditional):
723
+ def __init__(self, thresold, error='Value should be greater or equal than the threshold'):
724
+ Conditional.__init__(self, error)
725
+ self._condition = lambda value: value is not None and value >= thresold
726
+
727
+
728
+ class Range(Conditional):
729
+ def __init__(self, minValue, maxValue, error='Value is outside range'):
730
+ Conditional.__init__(self, error)
731
+ self._condition = lambda value: minValue <= value <= maxValue
732
+
733
+
734
+ class NumericListValidator(Conditional):
735
+ """ Validator for ListParam. See ListParam. """
736
+ def __init__(self, error='Incorrect format for numeric list param'):
737
+ Conditional.__init__(self, error)
738
+
739
+ def _condition(self, value):
740
+ try:
741
+ parts = re.split(r"[x\s]", value)
742
+ parts = list(filter(None, parts))
743
+ for p in parts:
744
+ float(p)
745
+ return True
746
+ except Exception:
747
+ return False
748
+
749
+
750
+ class NumericRangeValidator(Conditional):
751
+ """ Validator for RangeParam. See RangeParam. """
752
+
753
+ def __init__(self, error='Incorrect format for numeric range param'):
754
+ Conditional.__init__(self, error)
755
+
756
+ def _condition(self, value):
757
+ try:
758
+ parts = re.split(r"[-,\s]", value)
759
+ parts = list(filter(None, parts))
760
+ for p in parts:
761
+ float(p)
762
+ return True
763
+ except Exception:
764
+ return False
765
+
766
+
767
+ class NonEmptyBoolCondition(Conditional):
768
+ def __init__(self, error='Boolean param needs to be set.'):
769
+ Conditional.__init__(self, error)
770
+ self._condition = lambda value: value is not None
771
+
772
+
773
+ # --------- Some constants validators ---------------------
774
+
775
+ Positive = GT(0.0, error='Value should be greater than zero')
776
+
777
+ FreqValidator = Range(0.001, 0.5,
778
+ error="Digital frequencies should be between 0.001 and 0.5")
779
+
780
+ NonEmpty = NonEmptyCondition()
781
+ NonEmptyBool = NonEmptyBoolCondition()