scipion-pyworkflow 3.10.5__py3-none-any.whl → 3.11.0__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 (127) hide show
  1. pyworkflow/config.py +131 -67
  2. pyworkflow/constants.py +12 -2
  3. pyworkflow/object.py +3 -2
  4. pyworkflow/plugin.py +93 -44
  5. pyworkflow/project/scripts/fix_links.py +4 -1
  6. pyworkflow/resources/showj/arrowDown.png +0 -0
  7. pyworkflow/resources/showj/arrowUp.png +0 -0
  8. pyworkflow/resources/showj/background_section.png +0 -0
  9. pyworkflow/resources/showj/colRowModeOff.png +0 -0
  10. pyworkflow/resources/showj/colRowModeOn.png +0 -0
  11. pyworkflow/resources/showj/delete.png +0 -0
  12. pyworkflow/resources/showj/doc_icon.png +0 -0
  13. pyworkflow/resources/showj/download_icon.png +0 -0
  14. pyworkflow/resources/showj/enabled_gallery.png +0 -0
  15. pyworkflow/resources/showj/galleryViewOff.png +0 -0
  16. pyworkflow/resources/showj/galleryViewOn.png +0 -0
  17. pyworkflow/resources/showj/goto.png +0 -0
  18. pyworkflow/resources/showj/menu.png +0 -0
  19. pyworkflow/resources/showj/separator.png +0 -0
  20. pyworkflow/resources/showj/tableViewOff.png +0 -0
  21. pyworkflow/resources/showj/tableViewOn.png +0 -0
  22. pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  23. pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  24. pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  25. pyworkflow/resources/showj/volumeOff.png +0 -0
  26. pyworkflow/resources/showj/volumeOn.png +0 -0
  27. pyworkflow/viewer.py +23 -1
  28. pyworkflowtests/objects.py +2 -2
  29. pyworkflowtests/protocols.py +1 -3
  30. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +21 -25
  31. scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
  32. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
  33. scipion_pyworkflow-3.11.0.dist-info/entry_points.txt +2 -0
  34. pyworkflow/apps/__init__.py +0 -29
  35. pyworkflow/apps/pw_manager.py +0 -37
  36. pyworkflow/apps/pw_plot.py +0 -51
  37. pyworkflow/apps/pw_project.py +0 -113
  38. pyworkflow/apps/pw_protocol_list.py +0 -143
  39. pyworkflow/apps/pw_protocol_run.py +0 -51
  40. pyworkflow/apps/pw_run_tests.py +0 -267
  41. pyworkflow/apps/pw_schedule_run.py +0 -322
  42. pyworkflow/apps/pw_sleep.py +0 -37
  43. pyworkflow/apps/pw_sync_data.py +0 -439
  44. pyworkflow/apps/pw_viewer.py +0 -78
  45. pyworkflow/gui/__init__.py +0 -36
  46. pyworkflow/gui/browser.py +0 -726
  47. pyworkflow/gui/canvas.py +0 -1190
  48. pyworkflow/gui/dialog.py +0 -977
  49. pyworkflow/gui/form.py +0 -2637
  50. pyworkflow/gui/graph.py +0 -247
  51. pyworkflow/gui/graph_layout.py +0 -271
  52. pyworkflow/gui/gui.py +0 -566
  53. pyworkflow/gui/matplotlib_image.py +0 -233
  54. pyworkflow/gui/plotter.py +0 -247
  55. pyworkflow/gui/project/__init__.py +0 -25
  56. pyworkflow/gui/project/base.py +0 -192
  57. pyworkflow/gui/project/constants.py +0 -139
  58. pyworkflow/gui/project/labels.py +0 -205
  59. pyworkflow/gui/project/project.py +0 -492
  60. pyworkflow/gui/project/searchprotocol.py +0 -154
  61. pyworkflow/gui/project/searchrun.py +0 -181
  62. pyworkflow/gui/project/steps.py +0 -171
  63. pyworkflow/gui/project/utils.py +0 -332
  64. pyworkflow/gui/project/variables.py +0 -179
  65. pyworkflow/gui/project/viewdata.py +0 -472
  66. pyworkflow/gui/project/viewprojects.py +0 -510
  67. pyworkflow/gui/project/viewprotocols.py +0 -2093
  68. pyworkflow/gui/project/viewprotocols_extra.py +0 -560
  69. pyworkflow/gui/text.py +0 -771
  70. pyworkflow/gui/tooltip.py +0 -185
  71. pyworkflow/gui/tree.py +0 -684
  72. pyworkflow/gui/widgets.py +0 -307
  73. pyworkflow/mapper/__init__.py +0 -26
  74. pyworkflow/mapper/mapper.py +0 -222
  75. pyworkflow/mapper/sqlite.py +0 -1578
  76. pyworkflow/mapper/sqlite_db.py +0 -145
  77. pyworkflow/project/__init__.py +0 -31
  78. pyworkflow/project/config.py +0 -454
  79. pyworkflow/project/manager.py +0 -180
  80. pyworkflow/project/project.py +0 -2010
  81. pyworkflow/protocol/__init__.py +0 -38
  82. pyworkflow/protocol/bibtex.py +0 -48
  83. pyworkflow/protocol/constants.py +0 -87
  84. pyworkflow/protocol/executor.py +0 -455
  85. pyworkflow/protocol/hosts.py +0 -313
  86. pyworkflow/protocol/launch.py +0 -270
  87. pyworkflow/protocol/package.py +0 -42
  88. pyworkflow/protocol/params.py +0 -741
  89. pyworkflow/protocol/protocol.py +0 -2582
  90. pyworkflow/tests/__init__.py +0 -29
  91. pyworkflow/tests/test_utils.py +0 -25
  92. pyworkflow/tests/tests.py +0 -341
  93. pyworkflow/utils/__init__.py +0 -38
  94. pyworkflow/utils/dataset.py +0 -414
  95. pyworkflow/utils/echo.py +0 -104
  96. pyworkflow/utils/graph.py +0 -169
  97. pyworkflow/utils/log.py +0 -284
  98. pyworkflow/utils/path.py +0 -528
  99. pyworkflow/utils/process.py +0 -132
  100. pyworkflow/utils/profiler.py +0 -92
  101. pyworkflow/utils/progressbar.py +0 -154
  102. pyworkflow/utils/properties.py +0 -631
  103. pyworkflow/utils/reflection.py +0 -129
  104. pyworkflow/utils/utils.py +0 -879
  105. pyworkflow/utils/which.py +0 -229
  106. pyworkflow/webservices/__init__.py +0 -8
  107. pyworkflow/webservices/config.py +0 -11
  108. pyworkflow/webservices/notifier.py +0 -162
  109. pyworkflow/webservices/repository.py +0 -59
  110. pyworkflow/webservices/workflowhub.py +0 -74
  111. pyworkflowtests/tests/__init__.py +0 -0
  112. pyworkflowtests/tests/test_canvas.py +0 -72
  113. pyworkflowtests/tests/test_domain.py +0 -45
  114. pyworkflowtests/tests/test_logs.py +0 -74
  115. pyworkflowtests/tests/test_mappers.py +0 -392
  116. pyworkflowtests/tests/test_object.py +0 -507
  117. pyworkflowtests/tests/test_project.py +0 -42
  118. pyworkflowtests/tests/test_protocol_execution.py +0 -135
  119. pyworkflowtests/tests/test_protocol_export.py +0 -78
  120. pyworkflowtests/tests/test_protocol_output.py +0 -158
  121. pyworkflowtests/tests/test_streaming.py +0 -47
  122. pyworkflowtests/tests/test_utils.py +0 -210
  123. scipion_pyworkflow-3.10.5.dist-info/RECORD +0 -140
  124. scipion_pyworkflow-3.10.5.dist-info/dependency_links.txt +0 -1
  125. scipion_pyworkflow-3.10.5.dist-info/entry_points.txt +0 -5
  126. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info/licenses}/LICENSE.txt +0 -0
  127. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/top_level.txt +0 -0
pyworkflow/gui/form.py DELETED
@@ -1,2637 +0,0 @@
1
- # **************************************************************************
2
- # *
3
- # * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
4
- # * Jose Gutierrez (jose.gutierrez@cnb.csic.es) [2]
5
- # *
6
- # * [1] SciLifeLab, Stockholm University
7
- # * [2] Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
8
- # *
9
- # * This program is free software: you can redistribute it and/or modify
10
- # * it under the terms of the GNU General Public License as published by
11
- # * the Free Software Foundation, either version 3 of the License, or
12
- # * (at your option) any later version.
13
- # *
14
- # * This program is distributed in the hope that it will be useful,
15
- # * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
- # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
- # * GNU General Public License for more details.
18
- # *
19
- # * You should have received a copy of the GNU General Public License
20
- # * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
- # *
22
- # * All comments concerning this program package may be sent to the
23
- # * e-mail address 'scipion@cnb.csic.es'
24
- # *
25
- # **************************************************************************
26
- """
27
- This modules implements the automatic
28
- creation of protocol form GUI from its
29
- params definition.
30
- """
31
- import logging
32
- logger = logging.getLogger(__name__)
33
- import os
34
- import tkinter as tk
35
- import tkinter.ttk as ttk
36
- from collections import OrderedDict
37
- from datetime import datetime
38
-
39
- import pyworkflow as pw
40
- import pyworkflow.utils as pwutils
41
- import pyworkflow.object as pwobj
42
- import pyworkflow.protocol as pwprot
43
- from pyworkflow.mapper import Mapper
44
- from pyworkflow.viewer import DESKTOP_TKINTER
45
- from pyworkflow.protocol.constants import MODE_RESTART, MODE_RESUME
46
-
47
- from . import gui, RESULT_RUN_SINGLE
48
- from pyworkflow.gui.project.utils import getStatusColorFromRun
49
- from .gui import configureWeigths, Window
50
- from .browser import FileBrowserWindow
51
- from .widgets import Button, HotButton, IconButton
52
- from .dialog import (showInfo, showError, showWarning, EditObjectDialog,
53
- ListDialog, Dialog, RESULT_CANCEL, RESULT_RUN_ALL)
54
- from .canvas import Canvas
55
- from .tree import TreeProvider, BoundTree
56
- from .text import Text
57
- from ..project.project import ModificationNotAllowedException
58
-
59
- THREADS = 'Threads'
60
- MPI = 'MPI'
61
-
62
-
63
- # ----------------- Variables wrappers around more complex objects ------------
64
- class BoolVar:
65
- """Wrapper around tk.IntVar"""
66
-
67
- def __init__(self, value=None):
68
- self.tkVar = tk.IntVar()
69
- self.set(value)
70
- self.trace = self.tkVar.trace
71
-
72
- def set(self, value):
73
- if value is None:
74
- self.tkVar.set(-1)
75
- elif value:
76
- self.tkVar.set(1)
77
- else:
78
- self.tkVar.set(0)
79
-
80
- def get(self):
81
- if self.tkVar.get() == -1:
82
- return None
83
-
84
- return self.tkVar.get() == 1
85
-
86
-
87
- class PointerVar:
88
- """ Wrapper around tk.StringVar to hold object pointers. """
89
-
90
- def __init__(self, protocol):
91
- self.tkVar = tk.StringVar()
92
- self._pointer = pwobj.Pointer()
93
- self.trace = self.tkVar.trace
94
- self._protocol = protocol
95
-
96
- def set(self, value):
97
- if value is None:
98
- value = pwobj.Pointer(None)
99
- if not isinstance(value, pwobj.Pointer):
100
- raise Exception('Pointer var should be used with pointers!!!\n'
101
- ' Passing: %s, type: %s' % (value, type(value)))
102
- self._pointer.copy(value)
103
-
104
- label, _ = getPointerLabelAndInfo(self._pointer,
105
- self._protocol.getMapper())
106
- self.tkVar.set(label)
107
-
108
- def get(self):
109
- return self._pointer
110
-
111
- def getPointer(self):
112
- return self._pointer
113
-
114
- def remove(self):
115
- self.set(None)
116
-
117
-
118
- class ScalarWithPointerVar(tk.StringVar):
119
- """ tk.StringVar to hold object pointers and scalars. """
120
-
121
- def __init__(self, protocol, changeListener):
122
-
123
- self._pointer = None
124
- self._protocol = protocol
125
- self.inInit = True
126
- tk.StringVar.__init__(self)
127
- self.inInit = False
128
- self.insideSet = False
129
- self.listeners = []
130
-
131
- # Register inner listener
132
- tk.StringVar.trace(self, 'w', self._listenDirectEntryChanges)
133
- self.trace('w', changeListener)
134
-
135
- def trace(self, mode, callback):
136
-
137
- # let's ignore the mode for now all are "w"
138
- self.listeners.append(callback)
139
-
140
- def _listenDirectEntryChanges(self, *args):
141
- """ We need to be aware of any change done in the entry.
142
- When the user type anything, the set is not invoked.
143
- We need to distinguish when this is invoked from the set(),
144
- in this case we do nothing"""
145
- if not self.insideSet:
146
- self._pointer = None
147
-
148
- # Call the listeners
149
- for callback in self.listeners:
150
- callback(*args)
151
-
152
- def set(self, value):
153
- # Flag we are inside the set method to avoid
154
- # triggering _listenDirectEntryChanges
155
- self.insideSet = True
156
- if self.inInit:
157
- return
158
-
159
- # If a scalar is being set
160
- if not isinstance(value, pwobj.Pointer):
161
-
162
- # Reset the pointer
163
- self._pointer = None
164
- label = value
165
- # it's a pointer
166
- else:
167
-
168
- self._pointer = value
169
- label, _ = getPointerLabelAndInfo(self._pointer,
170
- self._protocol.getMapper())
171
-
172
- tk.StringVar.set(self, label)
173
-
174
- # Cancel the flag.
175
- self.insideSet = False
176
-
177
- def get(self):
178
-
179
- if self.hasPointer():
180
- return self._pointer
181
- else:
182
- return tk.StringVar.get(self)
183
-
184
- def hasPointer(self):
185
- return self._pointer is not None
186
-
187
- def getPointer(self):
188
- return self._pointer if self.hasPointer() else None
189
-
190
-
191
- class MultiPointerVar:
192
- """
193
- Wrapper around tk.StringVar to hold object pointers.
194
- This class is related with MultiPointerTreeProvider, which
195
- stores the list of pointed objects and have the logic to
196
- add and remove from the list.
197
- """
198
-
199
- def __init__(self, provider, tree):
200
- # keep a reference to tree provider to add or remove objects
201
- self.provider = provider
202
- self.tree = tree
203
- self.tkVar = tk.StringVar()
204
- self.trace = self.tkVar.trace
205
-
206
- def _updateObjectsList(self):
207
- self.tkVar.set(str(datetime.now())) # cause a trace to notify changes
208
- self.tree.update() # Update the tkinter tree gui
209
-
210
- def set(self, value):
211
- if isinstance(value, pwobj.Object) or isinstance(value, list):
212
- self.provider.addObject(value)
213
- self._updateObjectsList()
214
-
215
- def remove(self):
216
- """ Remove first element selected. """
217
- values = self.getSelectedObjects()
218
- for v in values:
219
- self.provider.removeObject(v)
220
- self._updateObjectsList()
221
-
222
- def clear(self):
223
- self.provider.clear()
224
- self._updateObjectsList()
225
-
226
-
227
- def getSelectedObjects(self):
228
- return self.tree.getSelectedObjects()
229
-
230
- def get(self):
231
- return self.provider.getObjects()
232
-
233
-
234
- class MultiPointerTreeProvider(TreeProvider):
235
- """
236
- Store several pointers to objects to be used in a BoundTree and as
237
- storage from MultiPointerVar.
238
- """
239
-
240
- def __init__(self, mapper):
241
- TreeProvider.__init__(self)
242
- self._objectDict = OrderedDict()
243
- self._mapper = mapper
244
-
245
- def _getObjKey(self, obj):
246
- """
247
- This method will create an unique key to
248
- identify the pointed object. The objId is not
249
- enough because of pointers and extended values
250
- to items inside a set or properties.
251
- """
252
- strId = None
253
-
254
- if isinstance(obj, pwobj.Pointer):
255
-
256
- if obj.hasValue():
257
- strId = obj.getObjValue().strId()
258
-
259
- if obj.hasExtended():
260
- strId += obj.getExtended()
261
-
262
- else:
263
- strId = obj.strId()
264
-
265
- if strId is None:
266
- raise Exception('ERROR: strId is None for MultiPointerTreeProvider!!!')
267
-
268
- return strId
269
-
270
- def _getObjPointer(self, obj):
271
- """ If obj is a pointer return obj. If not
272
- create a pointer and return it.
273
- """
274
- if isinstance(obj, pwobj.Pointer):
275
- ptr = obj
276
- else:
277
- ptr = pwobj.Pointer(value=obj)
278
-
279
- return ptr
280
-
281
- def _addObject(self, obj):
282
- strId = self._getObjKey(obj)
283
- ptr = self._getObjPointer(obj)
284
- ptr._strId = strId
285
-
286
- self._objectDict[strId] = ptr
287
-
288
- def addObject(self, obj):
289
- if isinstance(obj, list):
290
- for o in obj:
291
- self._addObject(o)
292
- else:
293
- self._addObject(obj)
294
-
295
- def removeObject(self, obj):
296
- strId = self._getObjKey(obj)
297
- if strId in self._objectDict:
298
- del self._objectDict[strId]
299
-
300
- def getObjects(self):
301
- return list(self._objectDict.values())
302
-
303
- def getColumns(self):
304
- return [('Object', 250), ('Info', 150)]
305
-
306
- def getObjectInfo(self, obj):
307
- label, info = getPointerLabelAndInfo(obj, self._mapper)
308
- return {'key': obj._strId, 'text': label, 'values': (' ' + info,)}
309
-
310
- def clear(self):
311
- self._objectDict.clear()
312
-
313
-
314
- class ComboVar:
315
- """ Create a variable that display strings (for combobox)
316
- but the values are integers (for the underlying EnumParam).
317
- """
318
-
319
- def __init__(self, enum):
320
- self.tkVar = tk.StringVar()
321
- self.enum = enum
322
- self.value = None
323
- self.trace = self.tkVar.trace
324
-
325
- def set(self, value):
326
- self.value = value
327
- if isinstance(value, int):
328
- # self.enum.choices is an object of type odict_values, which
329
- # cannot be indexed, so a type cast to list is required
330
- self.tkVar.set(list(self.enum.choices)[value])
331
- else:
332
- self.tkVar.set(value) # also support string values
333
-
334
- def get(self):
335
- v = self.tkVar.get()
336
- self.value = None
337
- for i, c in enumerate(list(self.enum.choices)):
338
- if c == v:
339
- self.value = i
340
-
341
- return self.value
342
-
343
-
344
- class TextVar:
345
- """Wrapper around tk.StringVar to bind the value of a Text widget. """
346
-
347
- def __init__(self, text, value=''):
348
- """
349
- Params:
350
- text: Text widget associated with this variable.
351
- value: initial value for the widget.
352
- """
353
- self.text = text
354
- text.bind('<KeyRelease>', self._onTextChanged)
355
- self.tkVar = tk.StringVar()
356
- self.set(value)
357
- self.trace = self.tkVar.trace
358
-
359
- def set(self, value):
360
- self.tkVar.set(value)
361
- if value is None:
362
- value = ''
363
- self.text.setText(value)
364
-
365
- def get(self):
366
- return self.tkVar.get()
367
-
368
- def _onTextChanged(self, e=None):
369
- self.tkVar.set(self.text.getText().strip())
370
-
371
- # ---------------- Some used providers for the TREES --------------------------
372
-
373
-
374
- class ProtocolClassTreeProvider(TreeProvider):
375
- """Will implement the methods to provide the object info
376
- of subclasses objects(of className) found by mapper"""
377
-
378
- def __init__(self, protocolClassName):
379
- TreeProvider.__init__(self)
380
- self.protocolClassName = protocolClassName
381
-
382
- def getObjects(self):
383
- # FIXME: Maybe find a way to pass the current domain?
384
- # FIXME: Or we just rely on the one defined in pw.Config?
385
- domain = pw.Config.getDomain()
386
- return [pwobj.String(s)
387
- for s in domain.findSubClasses(domain.getProtocols(),
388
- self.protocolClassName).keys()]
389
-
390
- def getColumns(self):
391
- return [('Protocol', 250)]
392
-
393
- def getObjectInfo(self, obj):
394
- return {'key': obj.get(),
395
- 'values': (obj.get(),)}
396
-
397
-
398
- def getPointerLabelAndInfo(pobj, mapper):
399
- """
400
- Return a string to represent selected objects
401
- that are stored by pointers.
402
- This function will be used from PointerVar and MultiPointerVar.
403
- """
404
- label = getObjectLabel(pobj, mapper)
405
- obj = pobj.get()
406
- info = str(obj) if obj is not None else ''
407
-
408
- return label, info
409
-
410
-
411
- def getObjectLabel(pobj, mapper):
412
- """ We will try to show in the list the string representation
413
- that is more readable for the user to pick the desired object.
414
- """
415
- # FIXME: maybe we can remove this function
416
- obj = pobj.get()
417
- prot = pobj.getObjValue()
418
-
419
- if prot is None:
420
- label = ''
421
- elif obj is None:
422
- label = '%s.%s' % (prot.getRunName(), pobj.getExtended())
423
- else:
424
- # This is for backward compatibility
425
- # Now always the pobj.getObjValue() should
426
- # be the protocol
427
- extended = pobj.getExtended() if isinstance(prot, pwprot.Protocol) else ''
428
- while not isinstance(prot, pwprot.Protocol):
429
- extended = '%s.%s' % (prot.getLastName(), extended)
430
- prot = mapper.getParent(prot)
431
- label = obj.getObjLabel().strip()
432
- if not len(label):
433
- label = '%s.%s' % (prot.getRunName(), extended)
434
-
435
- label = label.replace("\n", " ")
436
- # if obj is not None:
437
- # return label + " (%d)" % obj.getObjId()
438
- return label
439
-
440
-
441
- class SubclassesTreeProvider(TreeProvider):
442
- """Will implement the methods to provide the object info
443
- of subclasses objects(of className) found by mapper"""
444
- CREATION_COLUMN = 'Creation'
445
- INFO_COLUMN = 'Info'
446
- ID_COLUMN = 'Protocol Id'
447
-
448
- def __init__(self, protocol, pointerParam, selected=None):
449
- TreeProvider.__init__(self, sortingColumnName=self.CREATION_COLUMN,
450
- sortingAscending=False)
451
-
452
- self.param = pointerParam
453
- self.selected = selected # FIXME
454
- self.selectedDict = {}
455
- self.protocol = protocol
456
- self.mapper = protocol.mapper
457
- self.maxNum = 200
458
-
459
- def getObjects(self):
460
- # Retrieve all objects of type className
461
- project = self.protocol.getProject()
462
- className = self.param.pointerClass.get()
463
- condition = self.param.pointerCondition.get()
464
- # Get the classes that are valid as input object in this Domain
465
- domain = pw.Config.getDomain()
466
- classes = [domain.findClass(c.strip()) for c in className.split(",")]
467
- # Obtaining only the outputs of the protocols that do not violate the sense of processing,
468
- # thus avoiding circular references between protocols
469
- objects = project.getProtocolCompatibleOutputs(self.protocol, classes, condition)
470
-
471
- # Sort objects before returning them
472
- self._sortObjects(objects)
473
- return objects
474
-
475
- def _sortObjects(self, objects):
476
- objects.sort(key=self.objectKey, reverse=not self.isSortingAscending())
477
-
478
- def objectKey(self, pobj):
479
- """ Returns the value to be evaluated during sorting based on _sortingColumnName"""
480
- obj = self._getParentObject(pobj, pobj)
481
-
482
- if self._sortingColumnName == SubclassesTreeProvider.CREATION_COLUMN:
483
- return self._getObjectCreation(obj.get())
484
- elif self._sortingColumnName == SubclassesTreeProvider.INFO_COLUMN:
485
- return self._getObjectInfoValue(obj.get())
486
- elif self._sortingColumnName == SubclassesTreeProvider.ID_COLUMN:
487
- return self._getObjectId(pobj)
488
- else:
489
- return self._getPointerLabel(obj)
490
-
491
- def getColumns(self):
492
- return [('Object', 300), (SubclassesTreeProvider.INFO_COLUMN, 250),
493
- (SubclassesTreeProvider.CREATION_COLUMN, 150),
494
- (SubclassesTreeProvider.ID_COLUMN, 100)]
495
-
496
- def isSelected(self, obj):
497
- """ Check if an object is selected or not. """
498
- if self.selected:
499
- for s in self.selected:
500
- if s and s.getObjId() == obj.getObjId():
501
- return True
502
- return False
503
-
504
- @staticmethod
505
- def _getParentObject(pobj, default=None):
506
- return getattr(pobj, '_parentObject', default)
507
-
508
- def getObjectInfo(self, pobj):
509
- parent = self._getParentObject(pobj)
510
-
511
- # Get the label
512
- label = self._getPointerLabel(pobj, parent)
513
-
514
- obj = pobj.get()
515
- objId = pobj.getUniqueId()
516
- isSelected = objId in self.selectedDict
517
- self.selectedDict[objId] = True
518
-
519
- return {'key': objId, 'text': label,
520
- 'values': (self._getObjectInfoValue(obj),
521
- self._getObjectCreation(obj),
522
- self._getObjectId(pobj)),
523
- 'selected': isSelected, 'parent': parent}
524
-
525
- @staticmethod
526
- def _getObjectId(pobj):
527
- return pobj.getObjValue().getObjId()
528
-
529
- @staticmethod
530
- def _getObjectCreation(obj):
531
- """ Returns the Object creation time stamp or 'Not ready' for those not yet ready or possibleOutputs"""
532
- return obj.getObjCreation() if obj is not None and obj.getObjCreation() else "Not ready"
533
-
534
- @staticmethod
535
- def _getObjectInfoValue(obj):
536
- """ Returns the best summary of the object in a string."""
537
- if obj is not None:
538
- return str(obj).replace(obj.getClassName(), '')
539
- else: # possible Outputs are not output already so here comes None
540
- return "Possible output"
541
- def _getPointerLabel(self, pobj, parent=None):
542
-
543
- # If parent is not provided, try to get it, it might have none.
544
- if parent is None:
545
- parent = self._getParentObject(pobj)
546
-
547
- # If there is no parent
548
- if parent is None:
549
- return getObjectLabel(pobj, self.mapper)
550
- else: # This is an item coming from a set
551
- # If the object has label include the label
552
- if pobj.get().getObjLabel():
553
- return 'item %s - %s' % (pobj.get().strId(), pobj.get().getObjLabel())
554
- else:
555
- return 'item %s' % pobj.get().strId()
556
-
557
- def getObjectActions(self, pobj):
558
- obj = pobj.get()
559
- actions = []
560
- domain = pw.Config.getDomain()
561
- viewers = domain.findViewers(obj.getClassName(), DESKTOP_TKINTER)
562
- proj = self.protocol.getProject()
563
- for v in viewers:
564
- actions.append((v.getName(),
565
- lambda: v(project=proj).visualize(obj)))
566
- return actions
567
-
568
-
569
- # TODO: check if need to inherit from SubclassesTreeProvider
570
- class RelationsTreeProvider(SubclassesTreeProvider):
571
- """Will implement the methods to provide the object info
572
- of subclasses objects(of className) found by mapper"""
573
-
574
- def __init__(self, protocol, relationParam, selected=None):
575
- SubclassesTreeProvider.__init__(self, protocol, relationParam, selected)
576
- self.item = protocol.getAttributeValue(relationParam.getAttributeName())
577
- self.direction = relationParam.getDirection()
578
- self.relationParam = relationParam
579
-
580
- def getObjects(self):
581
- objects = []
582
- if self.item is not None:
583
- project = self.protocol.getProject()
584
- for pobj in project.getRelatedObjects(self.relationParam.getName(),
585
- self.item, self.direction,
586
- refresh=True):
587
- objects.append(pobj.clone())
588
-
589
- # Sort objects
590
- self._sortObjects(objects)
591
-
592
- return objects
593
-
594
-
595
- class ScalarTreeProvider(TreeProvider):
596
- """Will implement the methods to provide the object info
597
- of scalar outputs"""
598
- CREATION_COLUMN = 'Creation'
599
- INFO_COLUMN = 'Info'
600
-
601
- def __init__(self, protocol, scalarParam, selected=None):
602
- TreeProvider.__init__(self, sortingColumnName=self.CREATION_COLUMN,
603
- sortingAscending=False)
604
-
605
- self.param = scalarParam
606
- self.selected = selected
607
- self.selectedDict = {}
608
- self.protocol = protocol
609
- self.mapper = protocol.mapper
610
- self.maxNum = 200
611
-
612
- def getObjects(self):
613
- # Retrieve all objects of type className
614
- project = self.protocol.getProject()
615
- className = self.param.paramClass
616
- # Get the classes that are valid as input object
617
- # em.findClass is very tight to the EMObjects...Since scalars are not
618
- # EM object can't be used unless we do something
619
- # For now this will work with exact class.
620
- classes = [className]
621
- objects = []
622
-
623
- # Do no refresh again and take the runs that are loaded
624
- # already in the project. We will prefer to save time
625
- # here than have the 'very last' version of the runs and objects
626
- runs = project.getRuns(refresh=False)
627
-
628
- for prot in runs:
629
- # Make sure we don't include previous output of the same
630
- # protocol, it will cause a recursive loop
631
- if prot.getObjId() != self.protocol.getObjId():
632
-
633
- for paramName, attr in prot.iterOutputAttributes():
634
- def _checkParam(paramName, attr):
635
- # If attr is a sub-classes of any desired one, add it to the list
636
- # we should also check if there is a condition, the object
637
- # must comply with the condition
638
- p = None
639
- if any(isinstance(attr, c) for c in classes):
640
- p = pwobj.Pointer(prot, extended=paramName)
641
- p._allowsSelection = True
642
- objects.append(p)
643
-
644
- _checkParam(paramName, attr)
645
-
646
- # Sort objects before returning them
647
- self._sortObjects(objects)
648
-
649
- return objects
650
-
651
- def _sortObjects(self, objects):
652
- objects.sort(key=self.objectKey, reverse=not self.isSortingAscending())
653
-
654
- def objectKey(self, pobj):
655
-
656
- obj = self._getParentObject(pobj, pobj)
657
-
658
- if self._sortingColumnName == ScalarTreeProvider.CREATION_COLUMN:
659
- return self._getObjectCreation(obj.get())
660
- elif self._sortingColumnName == ScalarTreeProvider.INFO_COLUMN:
661
- return self._getObjectInfoValue(obj.get())
662
- else:
663
- return self._getPointerLabel(obj)
664
-
665
- def getColumns(self):
666
- return [('Object', 300), (ScalarTreeProvider.INFO_COLUMN, 250),
667
- (ScalarTreeProvider.CREATION_COLUMN, 150)]
668
-
669
- def isSelected(self, obj):
670
- """ Check if an object is selected or not. """
671
- if self.selected:
672
- for s in self.selected:
673
- if s and s.getObjId() == obj.getObjId():
674
- return True
675
- return False
676
-
677
- @staticmethod
678
- def _getParentObject(pobj, default=None):
679
- return getattr(pobj, '_parentObject', default)
680
-
681
- def getObjectInfo(self, pobj):
682
- parent = self._getParentObject(pobj)
683
-
684
- # Get the label
685
- label = self._getPointerLabel(pobj, parent)
686
-
687
- obj = pobj.get()
688
- objId = pobj.getUniqueId()
689
-
690
- isSelected = objId in self.selectedDict
691
- self.selectedDict[objId] = True
692
-
693
- return {'key': objId, 'text': label,
694
- 'values': (self._getObjectInfoValue(obj),
695
- self._getObjectCreation(obj)),
696
- 'selected': isSelected, 'parent': parent}
697
-
698
- @staticmethod
699
- def _getObjectCreation(obj):
700
-
701
- return obj.getObjCreation()
702
-
703
- @staticmethod
704
- def _getObjectInfoValue(obj):
705
-
706
- return str(obj).replace(obj.getClassName(), '')
707
-
708
- def _getPointerLabel(self, pobj, parent=None):
709
-
710
- # If parent is not provided, try to get it, it might have none.
711
- if parent is None:
712
- parent = self._getParentObject(pobj)
713
-
714
- # If there is no parent
715
- if parent is None:
716
- return getObjectLabel(pobj, self.mapper)
717
- else: # This is an item coming from a set
718
- # If the object has label include the label
719
- if pobj.get().getObjLabel():
720
- return 'item %s - %s' % (
721
- pobj.get().strId(), pobj.get().getObjLabel())
722
- else:
723
- return 'item %s' % pobj.get().strId()
724
-
725
-
726
- # --------------------- Other widgets ----------------------------------------
727
- # http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
728
-
729
-
730
- class VerticalScrolledFrame(tk.Frame):
731
- """A pure Tkinter scrollable frame that actually works!
732
- * Use the 'interior' attribute to place widgets inside the scrollable frame
733
- * Construct and pack/place/grid normally
734
- * This frame only allows vertical scrolling
735
-
736
- """
737
-
738
- def __init__(self, parent, *args, **kw):
739
- tk.Frame.__init__(self, parent, *args, **kw)
740
-
741
- # create a canvas object and a vertical scrollbar for scrolling it
742
- vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
743
- vscrollbar.pack(fill=tk.Y, side=tk.RIGHT, expand=tk.FALSE)
744
- canvas = Canvas(self, bd=0, highlightthickness=0,
745
- yscrollcommand=vscrollbar.set)
746
- canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
747
- vscrollbar.config(command=canvas.yview)
748
-
749
- # reset the view
750
- canvas.xview_moveto(0)
751
- canvas.yview_moveto(0)
752
-
753
- # create a frame inside the canvas which will be scrolled with it
754
- self.interior = interior = tk.Frame(canvas)
755
- interior_id = canvas.create_window(0, 0, window=interior,
756
- anchor=tk.NW)
757
-
758
- # track changes to the canvas and frame width and sync them,
759
- # also updating the scrollbar
760
- def _configure_interior(event):
761
- # update the scrollbars to match the size of the inner frame
762
- size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
763
- canvas.config(scrollregion="0 0 %s %s" % size)
764
- if interior.winfo_reqwidth() != canvas.winfo_width():
765
- # update the canvas's width to fit the inner frame
766
- canvas.config(width=interior.winfo_reqwidth())
767
-
768
- interior.bind('<Configure>', _configure_interior)
769
-
770
- def _configure_canvas(event):
771
- if interior.winfo_reqwidth() != canvas.winfo_width():
772
- # update the inner frame's width to fill the canvas
773
- canvas.itemconfigure(interior_id, width=canvas.winfo_width())
774
-
775
- canvas.bind('<Configure>', _configure_canvas)
776
-
777
-
778
- class SectionFrame(tk.Frame):
779
- """This class will be used to create a frame for the Section
780
- That will have a header with red color and a content frame
781
- with white background
782
- """
783
-
784
- def __init__(self, master, label, callback=None, height=15, **args):
785
- headerBgColor = args.get('headerBgColor', gui.cfgButtonBgColor)
786
- if 'headerBgColor' in args:
787
- del args['headerBgColor']
788
- self.height = height
789
- tk.Frame.__init__(self, master, **args)
790
- configureWeigths(self, row=1)
791
- self._createHeader(label, headerBgColor)
792
- self._createContent()
793
- tk.Frame.grid(self, row=0, column=0, sticky="new")
794
-
795
- def _createHeader(self, label, bgColor):
796
- self.headerFrame = tk.Frame(self, bd=2, relief=tk.RAISED, bg=bgColor,
797
- name="sectionheaderframe")
798
- self.headerFrame.grid(row=0, column=0, sticky='new')
799
- configureWeigths(self.headerFrame)
800
- self.headerFrame.columnconfigure(1, weight=1)
801
- self.headerLabel = tk.Label(self.headerFrame, text=label, fg='white',
802
- bg=bgColor, name="sectionheaderlabel")
803
- self.headerLabel.grid(row=0, column=0, sticky='nw')
804
-
805
- def _createContent(self):
806
- self.canvasFrame = tk.Frame(self, name="sectioncontentframe")
807
- configureWeigths(self.canvasFrame)
808
- self.canvas = Canvas(self.canvasFrame, width=625, height=self.height,
809
- bg=pw.Config.SCIPION_BG_COLOR, highlightthickness=0, name="sectioncanvas")
810
- self.canvas.grid(row=0, column=0, sticky='news')
811
- self.canvasFrame.grid(row=1, column=0, sticky='news')
812
-
813
- configureWeigths(self.canvas)
814
-
815
- self.contentFrame = tk.Frame(self.canvas, bg=pw.Config.SCIPION_BG_COLOR, bd=0,
816
- name="sectioncanvasframe")
817
- self.contentFrame.grid(row=1, column=0, sticky='news')
818
- self.contentId = self.canvas.create_window(0, 0, anchor=tk.NW,
819
- window=self.contentFrame)
820
-
821
- self.contentFrame.bind('<Configure>', self._configure_interior)
822
- self.canvas.bind('<Configure>', self._configure_canvas)
823
-
824
- self.contentFrame.columnconfigure(1, weight=1)
825
- self.columnconfigure(0, weight=1)
826
-
827
- def _getReqSize(self, widget):
828
- return widget.winfo_reqwidth(), widget.winfo_reqheight()
829
-
830
- def _getSize(self, widget):
831
- return widget.winfo_width(), widget.winfo_height()
832
-
833
- # track changes to the canvas and frame width and sync them,
834
- # also updating the scrollbar
835
- def _configure_interior(self, event=None):
836
-
837
- # update the scrollbars to match the size of the inner frame
838
- fsize = self._getReqSize(self.contentFrame)
839
- csize = self._getSize(self.canvas)
840
- if fsize != csize:
841
- # update the canvas's width to fit the inner frame
842
- self.canvas.config(width=fsize[0], height=fsize[1])
843
- self.canvas.config(scrollregion="0 0 %s %s" % fsize)
844
-
845
- def _configure_canvas(self, event=None):
846
- fsize = self._getReqSize(self.contentFrame)
847
- csize = self._getContentSize()
848
-
849
- # update the inner frame's width to fill the canvas
850
- self.canvas.itemconfigure(self.contentId, width=csize[0],height=csize[1])
851
- self.canvas.config(scrollregion="0 0 %s %s" % fsize)
852
-
853
- def _getContentSize(self):
854
- fsize = self._getReqSize(self.contentFrame)
855
- cFrame = self._getSize(self.canvasFrame)
856
- return (max(fsize[0], cFrame[0]), max(fsize[1], cFrame[1]))
857
-
858
- def adjustContent(self):
859
- self._configure_interior()
860
- self.update_idletasks()
861
- self._configure_canvas()
862
-
863
-
864
- class SectionWidget(SectionFrame):
865
- """This class will be used to create a section in FormWindow"""
866
-
867
- def __init__(self, form, master, section, height, callback=None, **args):
868
- self.form = form
869
- self.section = section
870
- self.callback = callback
871
- SectionFrame.__init__(self, master, self.section.label.get(),
872
- height=height, **args)
873
-
874
- def _createHeader(self, label, bgColor):
875
- SectionFrame._createHeader(self, label, bgColor)
876
-
877
- if self.section.hasQuestion():
878
- question = self.section.getQuestion()
879
- self.paramName = self.section.getQuestionName()
880
- self.var = BoolVar()
881
- self.var.set(question.get())
882
- self.var.trace('w', self._onVarChanged)
883
-
884
- self.chbLabel = tk.Label(self.headerFrame, text=question.label.get(),
885
- fg='white', bg=bgColor)
886
- self.chbLabel.grid(row=0, column=1, sticky='e', padx=2)
887
-
888
- self.chb = tk.Checkbutton(self.headerFrame, variable=self.var.tkVar,
889
- bg=bgColor,
890
- activebackground=gui.cfgButtonActiveBgColor)
891
- self.chb.grid(row=0, column=2, sticky='e')
892
-
893
- def show(self):
894
- self.contentFrame.grid(row=1, column=0, sticky='news', padx=5, pady=5)
895
-
896
- def hide(self):
897
- self.contentFrame.grid_remove()
898
-
899
- def _onVarChanged(self, *args):
900
- if self.get():
901
- self.show()
902
- else:
903
- self.hide()
904
-
905
- if self.callback is not None:
906
- self.callback(self.paramName)
907
-
908
- def get(self):
909
- """Return boolean value if is selected"""
910
- return self.var.get()
911
-
912
- def set(self, value):
913
- self.var.set(value)
914
-
915
-
916
- class ParamWidget:
917
- """For each one in the Protocol parameters, there will be
918
- one of this in the Form GUI.
919
- It is mainly composed by:
920
- A Label: put in the left column
921
- A Frame(content): in the middle column and container
922
- of the specific components for this parameter
923
- A Frame(buttons): a container for available actions buttons
924
- It will also have a Variable that should be set when creating
925
- the specific components"""
926
-
927
- def __init__(self, row, paramName, param, window, parent, value,
928
- callback=None, visualizeCallback=None, column=0,
929
- showButtons=True):
930
- self.window = window
931
- self._protocol = self.window.protocol
932
- if self._protocol.getProject() is None:
933
- logger.error(">>> ERROR: Project is None for protocol: %s, "
934
- "start winpdb to debug it" % self._protocol)
935
-
936
- self.row = row
937
- self.column = column
938
- self.paramName = paramName
939
- self.param = param
940
- self.parent = parent
941
- self.visualizeCallback = visualizeCallback
942
- self.var = None
943
-
944
- self._btnCol = 0
945
- self._labelFont = self.window.font
946
-
947
- self._initialize(showButtons)
948
- self._createLabel() # self.label should be set after this
949
- self._createContent() # self.content and self.var should be set after this
950
-
951
- if self.var: # Groups have not self.var
952
- self.set(value)
953
- self.callback = callback
954
- self.var.trace('w', self._onVarChanged)
955
-
956
- def _initialize(self, showButtons):
957
- # Show buttons = False means the widget is inside a Line group
958
- # then, some of the properties change accordingly
959
- if showButtons:
960
- self._labelSticky = 'ne'
961
- self._padx, self._pady = 2, 2
962
- self._entryWidth = 10
963
- if self.param.isImportant():
964
- self._labelFont = self.window.fontBold
965
- self.parent.columnconfigure(0, minsize=250)
966
- self.parent.columnconfigure(1, minsize=250)
967
- self.btnFrame = tk.Frame(self.parent, bg=pw.Config.SCIPION_BG_COLOR)
968
- else:
969
- self.btnFrame = None
970
- self._labelSticky = 'ne'
971
- self._padx, self._pady = 2, 0
972
- self._labelFont = self.window.fontItalic
973
- self._entryWidth = 8
974
- self._onlyLabel = False
975
-
976
- def _getParamLabel(self):
977
- return self.param.label.get()
978
-
979
- def _createLabel(self):
980
- bgColor = pw.Config.SCIPION_BG_COLOR
981
-
982
- if self.param.isExpert():
983
- bgColor = 'lightgrey'
984
-
985
- self.label = tk.Label(self.parent, text=self._getParamLabel(),
986
- bg=bgColor, font=self._labelFont, wraplength=500)
987
-
988
- def _createContent(self):
989
- self.content = tk.Frame(self.parent, bg=pw.Config.SCIPION_BG_COLOR)
990
- gui.configureWeigths(self.content)
991
- # self.var should be set after this
992
- self._createContentWidgets(self.param, self.content)
993
-
994
- def _addButton(self, text, imgPath, cmd):
995
- if self.btnFrame:
996
- btn = IconButton(self.btnFrame, text, imgPath,
997
- highlightthickness=0, command=cmd)
998
- btn.grid(row=0, column=self._btnCol, sticky='nes', padx=1, pady=4)
999
- self.btnFrame.columnconfigure(self._btnCol, weight=1)
1000
- self._btnCol += 1
1001
-
1002
- def _showHelpMessage(self, e=None):
1003
- showInfo("Help", self.param.help.get(), self.parent)
1004
-
1005
- def _showInfo(self, msg):
1006
- showInfo("Info", msg, self.parent)
1007
-
1008
- def _showError(self, msg):
1009
- showError("Error", msg, self.parent)
1010
-
1011
- def _showWarning(self, msg):
1012
- showWarning("Warning", msg, self.parent)
1013
-
1014
- def _showWizard(self, e=None):
1015
- wizClass = self.window.wizards[self.wizParamName]
1016
- wizard = wizClass()
1017
- # wizParamName: form attribute, the wizard object can check from which parameter it was called
1018
- # Used into VariableWizard objects (scipion-chem), where input and output parameters used for each wizard are defined
1019
- self.window.wizParamName = self.wizParamName
1020
- wizard.show(self.window)
1021
-
1022
- def _findParamWizard(self):
1023
- """ Search if there are registered wizards for this param
1024
- or any of its subparams (for the case of Line groups)
1025
- """
1026
- if self.paramName in self.window.wizards:
1027
- self.wizParamName = self.paramName
1028
- return True
1029
-
1030
- if isinstance(self.param, pwprot.Line):
1031
- for name, _ in self.param.iterParams():
1032
- if name in self.window.wizards:
1033
- self.wizParamName = name
1034
- return True
1035
- # Search in sub-params
1036
- return False
1037
-
1038
- @staticmethod
1039
- def createBoolWidget(parent, display=pwprot.BooleanParam.DISPLAY_YES_NO, **args):
1040
- """ Return a BoolVar associated with a yes/no selection.
1041
- **args: extra arguments passed to tk.Radiobutton and tk.Frame
1042
- constructors.
1043
-
1044
- :param checkbox: will use a Checkbutton instead.
1045
- """
1046
- var = BoolVar()
1047
- frameArgs = dict(args)
1048
- if 'font' in frameArgs:
1049
- del frameArgs['font']
1050
- frame = tk.Frame(parent, **frameArgs)
1051
-
1052
- if display == pwprot.BooleanParam.DISPLAY_CHECKBOX:
1053
- chk = tk.Checkbutton(frame, variable=var.tkVar, **args)
1054
- chk.grid(row=0, column=0, padx=2, sticky="w")
1055
- else:
1056
- rb1 = tk.Radiobutton(frame, text='Yes', variable=var.tkVar,
1057
- highlightthickness=0, value=1, **args)
1058
- rb1.grid(row=0, column=0, padx=2, sticky='w')
1059
- rb2 = tk.Radiobutton(frame, text='No', variable=var.tkVar,
1060
- highlightthickness=0, value=0, **args)
1061
- rb2.grid(row=0, column=1, padx=2, sticky='w')
1062
-
1063
- return var, frame
1064
-
1065
- def _createContentWidgets(self, param, content):
1066
- """Create the specific widgets inside the content frame"""
1067
- # Create widgets for each type of param
1068
- t = type(param)
1069
- entryWidth = 30
1070
- sticky = "we"
1071
-
1072
- # functions to select and remove
1073
- selectFunc = None
1074
- removeFunc = None
1075
-
1076
- if t is pwprot.HiddenBooleanParam:
1077
- var = 0
1078
-
1079
- elif t is pwprot.BooleanParam:
1080
- var, frame = ParamWidget.createBoolWidget(content, display=param.display,
1081
- bg=pw.Config.SCIPION_BG_COLOR,
1082
- font=self.window.font)
1083
- frame.grid(row=0, column=0, sticky='w')
1084
-
1085
- elif t is pwprot.EnumParam:
1086
- var = ComboVar(param)
1087
- if param.display == pwprot.EnumParam.DISPLAY_COMBO:
1088
- combo = ttk.Combobox(content, textvariable=var.tkVar,
1089
- state='readonly', font=self.window.font)
1090
- combo['values'] = param.choices
1091
- combo.grid(row=0, column=0, sticky='we')
1092
- elif param.display == pwprot.EnumParam.DISPLAY_LIST:
1093
- for i, opt in enumerate(param.choices):
1094
- rb = tk.Radiobutton(content, text=opt, variable=var.tkVar,
1095
- value=opt, font=self.window.font,
1096
- bg=pw.Config.SCIPION_BG_COLOR, highlightthickness=0)
1097
- rb.grid(row=i, column=0, sticky='w')
1098
- elif param.display == pwprot.EnumParam.DISPLAY_HLIST:
1099
- rbFrame = tk.Frame(content, bg=pw.Config.SCIPION_BG_COLOR)
1100
- rbFrame.grid(row=0, column=0, sticky='w')
1101
- for i, opt in enumerate(param.choices):
1102
- rb = tk.Radiobutton(rbFrame, text=opt, variable=var.tkVar,
1103
- value=opt, font=self.window.font,
1104
- bg=pw.Config.SCIPION_BG_COLOR)
1105
- rb.grid(row=0, column=i, sticky='w', padx=(0, 5))
1106
- else:
1107
- raise Exception("Invalid display value '%s' for EnumParam"
1108
- % str(param.display))
1109
-
1110
- elif t is pwprot.MultiPointerParam:
1111
- tp = MultiPointerTreeProvider(self._protocol.mapper)
1112
- tree = BoundTree(content, tp, height=5)
1113
- var = MultiPointerVar(tp, tree)
1114
- var.trace('w', self.window._onPointerChanged)
1115
- tree.grid(row=0, column=0, sticky='we')
1116
- self._addButton("Select", pwutils.Icon.ACTION_SEARCH, self._browseObject)
1117
- self._addButton("Remove", pwutils.Icon.ACTION_DELETE, self._removeObject)
1118
- self._selectmode = 'extended' # allows multiple object selection
1119
- self.visualizeCallback = self._visualizeMultiPointerParam
1120
-
1121
- elif t is pwprot.PointerParam or t is pwprot.RelationParam:
1122
- var = PointerVar(self._protocol)
1123
- var.trace('w', self.window._onPointerChanged)
1124
- entry = tk.Label(content, textvariable=var.tkVar,
1125
- font=self.window.font, anchor="w")
1126
- entry.grid(row=0, column=0, sticky='we')
1127
-
1128
- if t is pwprot.RelationParam:
1129
- selectFunc = self._browseRelation
1130
- removeFunc = self._removeRelation
1131
- else:
1132
- selectFunc = self._browseObject
1133
- removeFunc = self._removeObject
1134
-
1135
- self.visualizeCallback = self._visualizePointerParam
1136
- self._selectmode = 'browse' # single object selection
1137
-
1138
- elif t is pwprot.ProtocolClassParam:
1139
- var = tk.StringVar()
1140
- entry = tk.Label(content, textvariable=var, font=self.window.font,
1141
- anchor="w")
1142
- entry.grid(row=0, column=0, sticky='we')
1143
-
1144
- protClassName = self.param.protocolClassName.get()
1145
-
1146
- if self.param.allowSubclasses:
1147
- classes = pw.Config.getDomain().findSubClasses(
1148
- pw.Config.getDomain().getProtocols(), protClassName).keys()
1149
- else:
1150
- classes = [protClassName]
1151
-
1152
- if len(classes) > 1:
1153
- self._addButton("Select", pwutils.Icon.ACTION_SEARCH,
1154
- self._browseProtocolClass)
1155
- else:
1156
- var.set(classes[0])
1157
-
1158
- self._addButton("Edit", pwutils.Icon.ACTION_EDIT, self._openProtocolForm)
1159
-
1160
- elif t is pwprot.Line:
1161
- var = None
1162
-
1163
- elif t is pwprot.LabelParam:
1164
- var = None
1165
- self._onlyLabel = True
1166
-
1167
- elif t is pwprot.TextParam:
1168
- w = max(entryWidth, param.width)
1169
- text = Text(content, font=self.window.font, width=w,
1170
- height=param.height)
1171
- var = TextVar(text)
1172
- text.grid(row=0, column=0, sticky='w')
1173
-
1174
- else:
1175
-
1176
- if not param.allowsPointers:
1177
- var = tk.StringVar()
1178
-
1179
- if issubclass(t, pwprot.FloatParam) or issubclass(t, pwprot.IntParam):
1180
- # Reduce the entry width for numbers entries
1181
- entryWidth = self._entryWidth
1182
- sticky = 'w'
1183
- else:
1184
- selectFunc = self._browseScalar
1185
- var = ScalarWithPointerVar(self._protocol,
1186
- self.window._onPointerChanged)
1187
- self._selectmode = 'browse'
1188
- sticky = 'ew'
1189
- state = tk.DISABLED if param.readOnly else tk.NORMAL
1190
- entry = tk.Entry(content, width=entryWidth, textvariable=var,
1191
- font=self.window.font, state=state)
1192
-
1193
- # Select all content on focus
1194
- entry.bind("<FocusIn>",
1195
- lambda event: entry.selection_range(0, tk.END))
1196
-
1197
- entry.grid(row=0, column=0, sticky=sticky)
1198
-
1199
- if issubclass(t, pwprot.PathParam):
1200
- self._entryPath = entry
1201
- self._addButton('Browse', pwutils.Icon.ACTION_BROWSE,
1202
- self._browsePath)
1203
-
1204
- if selectFunc is not None:
1205
- self._addButton("Select", pwutils.Icon.ACTION_SEARCH, selectFunc)
1206
-
1207
- if removeFunc is not None:
1208
- self._addButton("Remove", pwutils.Icon.ACTION_DELETE, removeFunc)
1209
-
1210
- if self.visualizeCallback is not None:
1211
- self._addButton(pwutils.Message.LABEL_BUTTON_VIS,
1212
- pwutils.Icon.ACTION_VISUALIZE,
1213
- self._visualizeVar)
1214
-
1215
- if self._findParamWizard():
1216
- self._addButton(pwutils.Message.LABEL_BUTTON_WIZ,
1217
- pwutils.Icon.ACTION_WIZ,
1218
- self._showWizard)
1219
-
1220
- if param.help.hasValue():
1221
- self._addButton(pwutils.Message.LABEL_BUTTON_HELP,
1222
- pwutils.Icon.ACTION_HELP,
1223
- self._showHelpMessage)
1224
-
1225
- self.var = var
1226
-
1227
- def _visualizeVar(self, e=None):
1228
- """ Visualize specific variable. """
1229
- self.visualizeCallback(self.paramName)
1230
-
1231
- def _visualizePointer(self, pointer):
1232
- obj = pointer.get()
1233
-
1234
- if obj is None:
1235
- label, _ = getPointerLabelAndInfo(pointer, self._protocol.getMapper())
1236
- self._showInfo('*%s* points to *None*' % label)
1237
- else:
1238
- viewers = pw.Config.getDomain().findViewers(obj.getClassName(), DESKTOP_TKINTER)
1239
- if len(viewers):
1240
- ViewerClass = viewers[0] # Use the first viewer registered
1241
- # Instantiate the viewer and visualize object
1242
- viewer = ViewerClass(project=self._protocol.getProject(),
1243
- protocol=self._protocol,
1244
- parent=self.window)
1245
- viewer.visualize(obj)
1246
- else:
1247
- self._showInfo("There is no viewer registered for "
1248
- "*%s* object class." % obj.getClassName())
1249
-
1250
- def _visualizePointerParam(self, paramName):
1251
- pointer = self.var.get()
1252
- if pointer.hasValue():
1253
- self._visualizePointer(pointer)
1254
- else:
1255
- self._showInfo("Select input first.")
1256
-
1257
- def _visualizeMultiPointerParam(self, paramName):
1258
- selection = self.var.getSelectedObjects()
1259
- for pointer in selection:
1260
- self._visualizePointer(pointer)
1261
-
1262
- def _browseObject(self, e=None):
1263
- """Select an object from DB
1264
- This function is suppose to be used only for PointerParam"""
1265
- value = self.get()
1266
- selected = []
1267
- if isinstance(value, list):
1268
- selected = value
1269
- else:
1270
-
1271
- selected = [value]
1272
- tp = SubclassesTreeProvider(self._protocol, self.param,
1273
- selected=selected)
1274
-
1275
- def validateSelected(selectedItems):
1276
- for item in selectedItems:
1277
- if not getattr(item, '_allowsSelection', True):
1278
- return ("Please select object of types: %s"
1279
- % self.param.pointerClass.get())
1280
-
1281
- title = "Select object of types: %s" % self.param.pointerClass.get()
1282
-
1283
- pointerCond = self.param.pointerCondition.get()
1284
-
1285
- if pointerCond:
1286
- title += " (condition: %s)" % pointerCond
1287
-
1288
- dlg = ListDialog(self.parent, title, tp,
1289
- "Double click selects the item, right-click allows "
1290
- "you to visualize it",
1291
- validateSelectionCallback=validateSelected,
1292
- selectmode=self._selectmode, selectOnDoubleClick=True)
1293
-
1294
- if dlg.values:
1295
- if self.isMultiPointer():
1296
- self.set(dlg.values)
1297
- elif isinstance(self.param, pwprot.PointerParam):
1298
- self.set(dlg.values[0])
1299
- else:
1300
- raise Exception('Invalid param class: %s' % type(self.param))
1301
-
1302
- def isMultiPointer(self):
1303
- """ True if dealing with MultiPointer params """
1304
- return isinstance(self.param, pwprot.MultiPointerParam)
1305
-
1306
- def _browseScalar(self, e=None):
1307
- """Select a scalar from outputs
1308
- This function is supposed to be used only for Scalar Params
1309
- It's a copy of browseObject...so there could be a refactor here."""
1310
- value = self.get()
1311
- selected = []
1312
- if isinstance(value, list):
1313
- selected = value
1314
- else:
1315
- selected = [value]
1316
- tp = ScalarTreeProvider(self._protocol, self.param,
1317
- selected=selected)
1318
-
1319
- def validateSelected(selectedItems):
1320
- for item in selectedItems:
1321
- if not getattr(item, '_allowsSelection', True):
1322
- return ("Please select object of types: %s"
1323
- % self.param.paramClass.get())
1324
-
1325
- title = "Select object of types: %s" % self.param.paramClass.__name__
1326
-
1327
- # Let's ignore conditions so far
1328
- # pointerCond = self.param.pointerCondition.get()
1329
- # if pointerCond:
1330
- # title += " (condition: %s)" % pointerCond
1331
-
1332
- dlg = ListDialog(self.parent, title, tp,
1333
- "Double click selects the item",
1334
- validateSelectionCallback=validateSelected,
1335
- selectOnDoubleClick=True)
1336
-
1337
- if dlg.values:
1338
- self.set(dlg.values[0])
1339
-
1340
- def _removeObject(self, e=None):
1341
- """ Remove an object from a MultiPointer param. """
1342
- self.var.remove()
1343
-
1344
- def clear(self):
1345
-
1346
- # If dealing with Multipointers ...
1347
- if self.isMultiPointer():
1348
- # .. use var clear to remove all eletents since
1349
- # _removeObject() will remove only the selected ones
1350
- self.var.clear()
1351
- else:
1352
- self._removeObject()
1353
-
1354
- def _browseRelation(self, e=None):
1355
- """Select a relation from DB
1356
- This function is suppose to be used only for RelationParam. """
1357
- try:
1358
- tp = RelationsTreeProvider(self._protocol, self.param,
1359
- selected=self.get())
1360
- dlg = ListDialog(self.parent, "Select object", tp,
1361
- "Double click selects the item, right-click "
1362
- "allows you to visualize it",
1363
- selectmoded=self._selectmode,
1364
- selectOnDoubleClick=True)
1365
- if dlg.values:
1366
- self.set(dlg.values[0])
1367
- except AttributeError:
1368
- self._showError("Error loading possible inputs. "
1369
- "This usually happens because the parameter "
1370
- "needs info from other parameters... are "
1371
- "previous mandatory parameters set?")
1372
-
1373
- def _removeRelation(self, e=None):
1374
- self.var.remove()
1375
-
1376
- def _browseProtocolClass(self, e=None):
1377
- tp = ProtocolClassTreeProvider(self.param.protocolClassName.get())
1378
- dlg = ListDialog(self.parent, "Select protocol", tp,
1379
- selectmode=self._selectmode)
1380
- if dlg.value is not None:
1381
- self.set(dlg.value)
1382
- self._openProtocolForm()
1383
-
1384
- def _browsePath(self, e=None):
1385
- def onSelect(obj):
1386
- self.set(obj.getPath())
1387
-
1388
- v = self.get().strip()
1389
- path = None
1390
- if v:
1391
- v = os.path.dirname(v)
1392
- if os.path.exists(v):
1393
- path = v
1394
- if not path:
1395
- path = pwutils.getHomePath()
1396
- browser = FileBrowserWindow("Browsing", self.window, path=path,
1397
- onSelect=onSelect)
1398
- browser.show()
1399
-
1400
- def _openProtocolForm(self, e=None):
1401
- className = self.get().strip()
1402
- if len(className):
1403
- instanceName = self.paramName + "Instance"
1404
- protocol = self._protocol
1405
- # TODO: check if is present and is selected a different
1406
- # class, so we need to delete that and create a new instance
1407
- if not hasattr(protocol, instanceName):
1408
- cls = pw.Config.getDomain().findClass(className)
1409
- protocol._insertChild(instanceName, cls())
1410
-
1411
- prot = getattr(protocol, instanceName)
1412
-
1413
- prot.allowHeader.set(False)
1414
- f = FormWindow("Sub-Protocol: " + instanceName, prot,
1415
- self._protocolFormCallback, self.window,
1416
- childMode=True)
1417
- f.show()
1418
- else:
1419
- self._showInfo("Select the protocol class first")
1420
-
1421
- def _protocolFormCallback(self, e=None):
1422
- pass
1423
-
1424
- def _onVarChanged(self, *args):
1425
- if self.callback is not None:
1426
- self.callback(self.paramName)
1427
-
1428
- def show(self):
1429
- """Grid the label and content in the specified row"""
1430
- c = self.column
1431
- if self._onlyLabel:
1432
- # Use two columns for this case since we are only displaying a label
1433
- self.label.grid(row=self.row, column=c, sticky=self._labelSticky,
1434
- padx=self._padx, pady=self._pady, columnspan=2)
1435
- else:
1436
- self.label.grid(row=self.row, column=c, sticky=self._labelSticky,
1437
- padx=self._padx, pady=self._pady)
1438
-
1439
- # Note: for params without label: 1st param in a line param,
1440
- # label usually but take space and pushes the content, avoid
1441
- # this by using it's column
1442
- offset = 1 if not self._getParamLabel() else 0
1443
-
1444
- self.content.grid(row=self.row, column=c + 1 - offset,
1445
- columnspan=1 + offset, sticky='news',
1446
- padx=self._padx, pady=self._pady)
1447
- if self.btnFrame:
1448
- self.btnFrame.grid(row=self.row, column=c + 2, padx=self._padx,
1449
- pady=self._pady, sticky='nsew')
1450
-
1451
- def hide(self):
1452
- self.label.grid_remove()
1453
- self.content.grid_remove()
1454
- if self.btnFrame:
1455
- self.btnFrame.grid_remove()
1456
-
1457
- def display(self, condition):
1458
- """ show or hide depending on the condition. """
1459
- if condition:
1460
- self.show()
1461
- else:
1462
- self.hide()
1463
-
1464
- def set(self, value):
1465
- if value is not None:
1466
- self.var.set(value)
1467
-
1468
- if hasattr(self, '_entryPath'):
1469
- self._entryPath.xview_moveto(1)
1470
-
1471
- def get(self):
1472
- return self.var.get()
1473
-
1474
-
1475
- class LineWidget(ParamWidget):
1476
- def __init__(self, row, paramName, param, window, parent, value,
1477
- callback=None, visualizeCallback=None, column=0,
1478
- showButtons=True):
1479
- ParamWidget.__init__(self, row, paramName, param, window, parent, None)
1480
- self.show()
1481
-
1482
- def show(self):
1483
- self.label.grid(row=self.row, column=0, sticky=self._labelSticky, padx=2)
1484
- self.content.grid(row=self.row, column=1, sticky='new', columnspan=1,
1485
- padx=2)
1486
- if self.btnFrame:
1487
- self.btnFrame.grid(row=self.row, column=2, padx=2, sticky='new')
1488
-
1489
-
1490
- class GroupWidget(ParamWidget):
1491
- def __init__(self, row, paramName, param, window, parent):
1492
- ParamWidget.__init__(self, row, paramName, param, window, parent, None)
1493
-
1494
- def _initialize(self, showButtons):
1495
- pass
1496
-
1497
- def _createLabel(self):
1498
- pass
1499
-
1500
- def _createContent(self):
1501
- self.content = tk.LabelFrame(self.parent, text=self.param.getLabel(),
1502
- bg=pw.Config.SCIPION_BG_COLOR)
1503
- gui.configureWeigths(self.content, column=1)
1504
-
1505
- def show(self):
1506
- self.content.grid(row=self.row, column=0, sticky='news', columnspan=6,
1507
- padx=5, pady=5)
1508
-
1509
- def hide(self):
1510
- self.content.grid_remove()
1511
-
1512
-
1513
- class Binding:
1514
- def __init__(self, paramName, var, protocol, *callbacks):
1515
- self.paramName = paramName
1516
- self.var = var
1517
- self.var.set(protocol.getAttributeValue(paramName, ''))
1518
- self.var.trace('w', self._onVarChanged)
1519
- self.callbacks = callbacks
1520
-
1521
- def _onVarChanged(self, *args):
1522
- for cb in self.callbacks:
1523
- cb(self.paramName)
1524
-
1525
-
1526
- class FormWindow(Window):
1527
- """ This class will create the Protocol params GUI to fill in the parameters.
1528
- The creation of input parameters will be based on the Protocol Form definition.
1529
- This class will serve as a connection between the GUI variables (tk vars) and
1530
- the Protocol variables.
1531
-
1532
- Layout:
1533
- There are 4 main blocks that goes each one in a different row1.
1534
- 1. Header: will contain the logo, title and some link buttons.
1535
- 2. Common: common execution parameters of each run.
1536
- 3. Params: the expert level and tabs with the Protocol parameters.
1537
- 4. Buttons: buttons at bottom for close, save and execute.
1538
- """
1539
-
1540
- def __init__(self, title, protocol, callback, master=None, position=None, **kwargs):
1541
- """ Constructor of the Form window.
1542
- Params:
1543
- title: title string of the windows.
1544
- protocol: protocol from which the form will be generated.
1545
- callback: callback function to call when Save or Execute are press.
1546
- """
1547
-
1548
- if position:
1549
- title = title + " at %s,%s" % position
1550
- Window.__init__(self, title, master, icon=pwutils.Icon.SCIPION_ICON_PROT,
1551
- weight=False, minsize=(600, 450), **kwargs)
1552
-
1553
- # Some initialization
1554
- self.callback = callback
1555
- self.widgetDict = {} # Store tkVars associated with params
1556
- self.visualizeDict = kwargs.get('visualizeDict', {})
1557
- self.disableRunMode = kwargs.get('disableRunMode', False)
1558
- self.bindings = []
1559
- self.protocol = protocol
1560
- self.position = position
1561
- # This control when to close or not after execute
1562
- self.visualizeMode = kwargs.get('visualizeMode', False)
1563
- self.headerBgColor = pw.Config.SCIPION_MAIN_COLOR
1564
- if self.visualizeMode:
1565
- self.headerBgColor = pwutils.Color.ALT_COLOR_DARK
1566
- # Allow to open child protocols form (for workflows)
1567
- self.childMode = kwargs.get('childMode', False)
1568
- self.updateProtocolCallback = kwargs.get('updateProtocolCallback', None)
1569
- domain = pw.Config.getDomain()
1570
- self.wizards = domain.findWizards(protocol, DESKTOP_TKINTER)
1571
-
1572
- # Call legacy for compatibility on protocol
1573
- protocol.legacyCheck()
1574
- self._createGUI()
1575
-
1576
- def _createGUI(self):
1577
- mainFrame = tk.Frame(self.root, name="main")
1578
- configureWeigths(mainFrame, row=2)
1579
- self.root.rowconfigure(0, weight=1)
1580
- self.root.columnconfigure(0, weight=1)
1581
-
1582
- # "Protocol: XXXXX - Cite Help
1583
- headerFrame = self._createHeader(mainFrame)
1584
- headerFrame.grid(row=0, column=0, sticky='new')
1585
-
1586
- if self.protocol.allowHeader:
1587
- # Run Section with common attributes (parallel,...)
1588
- commonFrame = self._createCommon(mainFrame)
1589
- commonFrame.grid(row=1, column=0, sticky='new')
1590
-
1591
- if self._isLegacyProtocol():
1592
- paramsFrame = self._createLegacyInfo(mainFrame)
1593
- else:
1594
- paramsFrame = self._createParams(mainFrame)
1595
- paramsFrame.grid(row=2, column=0, sticky='news')
1596
-
1597
- buttonsFrame = self._createButtons(mainFrame)
1598
- buttonsFrame.grid(row=3, column=0, sticky='se')
1599
-
1600
- mainFrame.grid(row=0, column=0, sticky='news')
1601
-
1602
- def _createHeader(self, parent):
1603
- """ Fill the header frame with the logo, title and cite-help buttons."""
1604
- headerFrame = tk.Frame(parent, name="header")
1605
- headerFrame.columnconfigure(0, weight=1)
1606
- prot = self.protocol # shortcut
1607
- package = prot.getClassPackage()
1608
-
1609
- # Consider legacy protocols
1610
- if self._isLegacyProtocol():
1611
- t = (' Missing protocol: %s'
1612
- % (Mapper.getObjectPersistingClassName(prot)))
1613
- else:
1614
- t = ' %s' % (prot.getClassLabel())
1615
-
1616
- logoPath = prot.getPluginLogoPath() or getattr(package, '_logo', '')
1617
-
1618
- if logoPath and os.path.exists(logoPath):
1619
- # Tolerate error loading icons
1620
- try:
1621
- img = self.getImage(logoPath, maxheight=40)
1622
- except Exception as e:
1623
- print("Can't load plugin icon (%s): %s" % (logoPath, e))
1624
- img = None
1625
-
1626
- headerLabel = tk.Label(headerFrame, text=t, font=self.fontBig,
1627
- image=img,
1628
- compound=tk.LEFT)
1629
- else:
1630
- headerLabel = tk.Label(headerFrame, text=t, font=self.fontBig)
1631
- headerLabel.grid(row=0, column=0, padx=5, pady=(5, 0), sticky='nw')
1632
-
1633
- # Add status label
1634
- status = prot.status.get()
1635
- # For viewers and new protocols (status less object): skip this
1636
- if status is not None:
1637
- color = getStatusColorFromRun(prot)
1638
- stLabel = tk.Label(headerFrame, text=status, background=color)
1639
- stLabel.grid(row=0, column=1, padx=5, pady=5, sticky='e')
1640
-
1641
- def _addButton(text, icon, command, col):
1642
- btn = tk.Label(headerFrame, text=text, image=self.getImage(icon),
1643
- compound=tk.LEFT, cursor='hand2', name=text.lower())
1644
- btn.bind('<Button-1>', command)
1645
- btn.grid(row=0, column=col, padx=5, sticky='e')
1646
-
1647
- _addButton(pwutils.Message.LABEL_CITE,
1648
- pwutils.Icon.ACTION_REFERENCES,
1649
- self._showReferences, 2)
1650
- _addButton(pwutils.Message.LABEL_HELP,
1651
- pwutils.Icon.ACTION_HELP, self._showHelp, 3)
1652
-
1653
- return headerFrame
1654
-
1655
- def _showReferences(self, e=None):
1656
- """ Show the list of references of the protocol. """
1657
- self.showInfo('\n'.join(self.protocol.citations()), "References")
1658
-
1659
- def _showHelp(self, e=None):
1660
- """ Show the protocol help. """
1661
- prot = self.protocol
1662
- text = prot.getHelpText()
1663
-
1664
- # Add protocol url
1665
- url = prot.getUrl()
1666
-
1667
- # If not empty...
1668
- if url:
1669
- text += "\nDocumentation or forum url for this protocol:\n" + url
1670
-
1671
- self.showInfo(text, "Help")
1672
-
1673
- def _createParallel(self, runFrame, r):
1674
- """ Create the section for MPI, threads and GPU. """
1675
-
1676
- # Legacy protocols retrieved from the DB may not have this param
1677
- # and legacy mode will fail. Thus the try block
1678
- try:
1679
-
1680
- # some short notation
1681
- prot = self.protocol # shortcut notation
1682
- allowThreads = prot.allowThreads # short notation
1683
- allowMpi = prot.allowMpi # short notation
1684
- allowGpu = prot.allowsGpu()
1685
- numberOfMpi = prot.numberOfMpi.get()
1686
- numberOfThreads = prot.numberOfThreads.get()
1687
- mode = prot.stepsExecutionMode
1688
-
1689
- if not (allowThreads or allowMpi or allowGpu):
1690
- return
1691
-
1692
- self._createHeaderLabel(runFrame, pwutils.Message.LABEL_PARALLEL, bold=True,
1693
- sticky='e', row=r, pady=0)
1694
-
1695
- if allowThreads or allowMpi:
1696
- procFrame = tk.Frame(runFrame, bg=pw.Config.SCIPION_BG_COLOR)
1697
- r2 = 0
1698
- c2 = 0
1699
- sticky = 'e'
1700
-
1701
- helpMessage = pwutils.Message.HELP_PARALLEL_HEADER
1702
-
1703
- if mode == pwprot.STEPS_PARALLEL:
1704
- if allowThreads and numberOfThreads > 0:
1705
- prot.numberOfMpi.set(1)
1706
- self._createHeaderLabel(procFrame, pwutils.Message.LABEL_SCIPION_THREADS,
1707
- sticky=sticky, row=r2, column=c2,
1708
- pady=0)
1709
- entry = self._createBoundEntry(procFrame,
1710
- pwutils.Message.VAR_THREADS)
1711
-
1712
- helpMessage += pwutils.Message.HELP_SCIPION_THREADS
1713
-
1714
- entry.grid(row=r2, column=c2 + 1, padx=(0, 5), sticky='w')
1715
- elif allowMpi and numberOfMpi > 1:
1716
- self.showError("MPI parameter is deprecated for protocols "
1717
- "with execution is set to STEPS_PARALLEL. "
1718
- "Please use threads instead.")
1719
- else:
1720
- self.showError("If protocol execution is set to "
1721
- "STEPS_PARALLEL number of threads "
1722
- "should not be set to zero.")
1723
-
1724
- else:
1725
- # ---- THREADS----
1726
- if allowThreads:
1727
- self._createHeaderLabel(procFrame, pwutils.Message.LABEL_THREADS,
1728
- sticky=sticky, row=r2, column=c2,
1729
- pady=0)
1730
- entry = self._createBoundEntry(procFrame,
1731
- pwutils.Message.VAR_THREADS)
1732
- entry.grid(row=r2, column=c2 + 1, padx=(0, 5), sticky='w')
1733
- # Modify values to be used in MPI entry
1734
- c2 += 2
1735
- sticky = 'w'
1736
-
1737
- helpMessage += pwutils.Message.HELP_PARALLEL_THREADS
1738
- # ---- MPI ----
1739
- if allowMpi:
1740
- self._createHeaderLabel(procFrame, pwutils.Message.LABEL_MPI,
1741
- sticky=sticky, row=r2, column=c2,
1742
- pady=0)
1743
- entry = self._createBoundEntry(procFrame, pwutils.Message.VAR_MPI)
1744
- entry.grid(row=r2, column=c2 + 1, padx=(0, 5), sticky='w')
1745
-
1746
- helpMessage += pwutils.Message.HELP_PARALLEL_MPI
1747
-
1748
-
1749
- btnHelp = IconButton(procFrame, pwutils.Message.TITLE_COMMENT,
1750
- pwutils.Icon.ACTION_HELP,
1751
- highlightthickness=0,
1752
- command=self._createHelpCommand(
1753
- helpMessage))
1754
- btnHelp.grid(row=0, column=4, padx=(5, 0), pady=2, sticky='e')
1755
-
1756
- procFrame.columnconfigure(0, minsize=60)
1757
- procFrame.grid(row=r, column=1, sticky='ew', columnspan=2)
1758
-
1759
- r += 1
1760
-
1761
- if allowGpu:
1762
- self._createHeaderLabel(runFrame, "GPU IDs", bold=True,
1763
- sticky='e', row=r, column=0, pady=0)
1764
- gpuFrame = tk.Frame(runFrame, bg=pw.Config.SCIPION_BG_COLOR)
1765
- gpuFrame.grid(row=r, column=1, sticky='ew', columnspan=2)
1766
-
1767
- self.useGpuVar = tk.IntVar()
1768
-
1769
- # For protocols that require GPU, there is not the option to choose
1770
- if not prot.requiresGpu():
1771
- self.useGpuVar.set(int(prot.useGpu.get()))
1772
- for i, opt in enumerate(['Yes', 'No']):
1773
- rb = tk.Radiobutton(gpuFrame, text=opt,
1774
- variable=self.useGpuVar,
1775
- value=1 - i, bg=pw.Config.SCIPION_BG_COLOR,
1776
- highlightthickness=0)
1777
- rb.grid(row=0, column=i, sticky='w', padx=(0, 5), pady=5)
1778
-
1779
- self.gpuListVar = tk.StringVar()
1780
- self.gpuListVar.set(prot.getAttributeValue(pwprot.GPU_LIST, ''))
1781
- gpuEntry = tk.Entry(gpuFrame, width=9, font=self.font,
1782
- textvariable=self.gpuListVar)
1783
- gpuEntry.grid(row=0, column=2, sticky='w',
1784
- padx=(0, 5), pady=(0, 5))
1785
-
1786
- # Legacy protocols retrieved from the DB will not have this param
1787
- # and legacy mode will fail. try added at the top.
1788
- gpuListParam = prot.getParam(pwprot.GPU_LIST)
1789
- btnHelp = IconButton(gpuFrame, pwutils.Message.TITLE_COMMENT,
1790
- pwutils.Icon.ACTION_HELP,
1791
- highlightthickness=0,
1792
- command=self._createHelpCommand(
1793
- gpuListParam.getHelp()))
1794
- btnHelp.grid(row=0, column=3, padx=(5, 0), pady=2, sticky='e')
1795
-
1796
- # Trace changes in GPU related widgets to store values in protocol
1797
- self.useGpuVar.trace('w', self._setGpu)
1798
- self.gpuListVar.trace('w', self._setGpu)
1799
- except Exception as e:
1800
- print("Parallel section couldn't be created. %s" % e)
1801
-
1802
- def _createCommon(self, parent):
1803
- """ Create the second section with some common parameters. """
1804
- commonFrame = tk.Frame(parent, name="commonparams")
1805
- configureWeigths(commonFrame)
1806
-
1807
- # ---------- Run section ---------
1808
- runSection = SectionFrame(commonFrame, label=pwutils.Message.TITLE_RUN,
1809
- headerBgColor=self.headerBgColor,
1810
- name="runsection")
1811
-
1812
- runFrame = tk.Frame(runSection.contentFrame, bg=pw.Config.SCIPION_BG_COLOR, name="runframe")
1813
- runFrame.grid(row=0, column=0, sticky='new')
1814
-
1815
- r = 0 # Run name
1816
- self._createHeaderLabel(runFrame, pwutils.Message.LABEL_RUNNAME, bold=True,
1817
- sticky='ne')
1818
- self.runNameVar = tk.StringVar()
1819
- entry = tk.Label(runFrame, font=self.font, width=25,
1820
- textvariable=self.runNameVar, anchor="w")
1821
- entry.grid(row=r, column=1, padx=(0, 5), pady=5, sticky='ew')
1822
- btn = IconButton(runFrame, pwutils.Message.TITLE_COMMENT, pwutils.Icon.ACTION_EDIT,
1823
- highlightthickness=0, command=self._editObjParams)
1824
- btn.grid(row=r, column=2, padx=(5, 0), pady=5, sticky='w')
1825
-
1826
- c = 3 # Comment
1827
- self._createHeaderLabel(runFrame, pwutils.Message.TITLE_COMMENT, sticky='e',
1828
- column=c)
1829
- self.commentVar = tk.StringVar()
1830
- entry = tk.Label(runFrame, font=self.font, width=25,
1831
- textvariable=self.commentVar, anchor="w")
1832
- entry.grid(row=r, column=c + 1, pady=5, sticky='ew')
1833
- btn = IconButton(runFrame, pwutils.Message.TITLE_COMMENT, pwutils.Icon.ACTION_EDIT,
1834
- highlightthickness=0, command=self._editObjParams)
1835
- btn.grid(row=r, column=c + 2, padx=(5, 0), pady=5, sticky='w')
1836
-
1837
- self.updateLabelAndCommentVars()
1838
-
1839
- r = 1 # Run mode
1840
-
1841
- modeFrame = tk.Frame(runFrame, bg=pw.Config.SCIPION_BG_COLOR)
1842
-
1843
- if not self.disableRunMode:
1844
- self._createHeaderLabel(runFrame, pwutils.Message.LABEL_EXECUTION,
1845
- bold=True,
1846
- sticky='e', row=r, pady=0)
1847
-
1848
- runMode = self._createBoundOptions(modeFrame, pwutils.Message.VAR_RUN_MODE,
1849
- pwprot.MODE_CHOICES,
1850
- self.protocol.runMode.get(),
1851
- self._onRunModeChanged,
1852
- bg=pw.Config.SCIPION_BG_COLOR, font=self.font)
1853
- runMode.grid(row=0, column=0, sticky='e', padx=(0, 5), pady=5)
1854
- btnHelp = IconButton(modeFrame, pwutils.Message.TITLE_COMMENT, pwutils.Icon.ACTION_HELP,
1855
- highlightthickness=0,
1856
- command=self._createHelpCommand(pwutils.Message.HELP_RUNMODE))
1857
- btnHelp.grid(row=0, column=2, padx=(5, 0), pady=2, sticky='e')
1858
- modeFrame.columnconfigure(0, weight=1)
1859
- modeFrame.grid(row=r, column=1, sticky='w', columnspan=2)
1860
-
1861
- # Queue
1862
- self._createHeaderLabel(runFrame, pwutils.Message.LABEL_QUEUE, row=r,
1863
- sticky='e',
1864
- column=c)
1865
-
1866
- var, frame = ParamWidget.createBoolWidget(runFrame, bg=pw.Config.SCIPION_BG_COLOR,
1867
- font=self.font)
1868
- btn = IconButton(frame, pwutils.Message.LABEL_BUTTON_WIZ, pwutils.Icon.ACTION_EDIT,
1869
- highlightthickness=0, command=self._editQueueParams, tooltip="Edit queue parameters")
1870
- btn.grid(row=0, column=2, sticky='nes', padx=1, pady=4)
1871
- frame.columnconfigure(2, weight=1)
1872
-
1873
- self._addVarBinding(pwutils.Message.VAR_QUEUE, var)
1874
- frame.grid(row=r, column=c + 1, pady=5, sticky='ew')
1875
-
1876
- btnHelp = IconButton(runFrame, pwutils.Message.TITLE_COMMENT, pwutils.Icon.ACTION_HELP,
1877
- highlightthickness=0,
1878
- command=self._createHelpCommand(pwutils.Message.HELP_USEQUEUE %
1879
- (pw.Config.SCIPION_HOSTS, pw.DOCSITEURLS.HOST_CONFIG)))
1880
-
1881
- btnHelp.grid(row=r, column=c + 2, padx=(5, 0), pady=5, sticky='w')
1882
-
1883
- r = 2 # Parallel and Wait for other protocols (SCHEDULE)
1884
- self._createParallel(runFrame, r)
1885
-
1886
- self._createHeaderLabel(runFrame, pwutils.Message.LABEL_WAIT_FOR, row=r, sticky='e',
1887
- column=c, padx=(15, 5), pady=0)
1888
- self.waitForVar = tk.StringVar()
1889
- self.waitForVar.set(', '.join(self.protocol.getPrerequisites()))
1890
- entryWf = tk.Entry(runFrame, font=self.font, width=25,
1891
- textvariable=self.waitForVar)
1892
- entryWf.grid(row=r, column=c + 1, padx=(0, 5), pady=5, sticky='ew')
1893
-
1894
- self.waitForVar.trace('w', self._setWaitFor)
1895
-
1896
- btnHelp = IconButton(runFrame, pwutils.Message.TITLE_COMMENT, pwutils.Icon.ACTION_HELP,
1897
- highlightthickness=0,
1898
- command=self._createHelpCommand(pwutils.Message.HELP_WAIT_FOR % pw.DOCSITEURLS.WAIT_FOR))
1899
- btnHelp.grid(row=r, column=c + 2, padx=(5, 0), pady=2, sticky='e')
1900
-
1901
- # Run Name not editable
1902
- # entry.configure(state='readonly')
1903
- # Run mode
1904
- # self._createHeaderLabel(runFrame, pwutils.Message.LABEL_RUNMODE).grid(row=1, column=0, sticky='ne', padx=5, pady=5)
1905
- # runSection.addContent()
1906
- runSection.grid(row=0, column=0, sticky='news', padx=5, pady=5)
1907
-
1908
- return commonFrame
1909
-
1910
- def _createHelpCommand(self, msg):
1911
- """ Show the help of some value of the header. """
1912
- return lambda: showInfo("Help", msg, self.root)
1913
-
1914
- def _editObjParams(self, e=None):
1915
- """ Show a Text area to edit the protocol label and comment. """
1916
- self.updateProtocolLabel()
1917
- d = EditObjectDialog(self.root, pwutils.Message.TITLE_EDIT_OBJECT,
1918
- self.protocol, self.protocol.mapper,
1919
- labelText=pwutils.Message.LABEL_RUNNAME)
1920
-
1921
- if d.resultYes():
1922
- self.updateLabelAndCommentVars()
1923
- if self.updateProtocolCallback:
1924
- self.updateProtocolCallback(self.protocol)
1925
-
1926
- def _getHostConfig(self):
1927
- """ Retrieve the hostConfig object for the select hostname"""
1928
- return self.protocol.getProject().getHostConfig(self.protocol.getHostName())
1929
-
1930
- def _editQueueParams(self, e=None):
1931
- """ Open the dialog to edit the queue parameters. """
1932
- # Grab the host config from the project, since it
1933
- # have not been set in the protocol
1934
- hostConfig = self._getHostConfig()
1935
- queues = hostConfig.queueSystem.queues
1936
- if not queues:
1937
- self.showError("No queues configured!")
1938
- return False
1939
- else:
1940
- queues = OrderedDict(sorted(queues.items()))
1941
- # If there is only one Queue and it has no parameters
1942
- # don't bother to showing the QueueDialog
1943
- noQueueChoices = len(queues) == 1 and len(list(queues.values())[0]) == 0
1944
- if noQueueChoices:
1945
- result = list(queues.keys())[0], {}
1946
- else:
1947
- dlg = QueueDialog(self, queues)
1948
-
1949
- if not dlg.resultYes():
1950
- return False
1951
- result = dlg.value
1952
-
1953
- self.protocol.setQueueParams(result)
1954
- self.protocol.queueShown = True
1955
- return True
1956
-
1957
- def _createParams(self, parent):
1958
- paramsFrame = tk.Frame(parent, name="params")
1959
- configureWeigths(paramsFrame, row=1, column=0)
1960
- # Expert level (only if the protocol has some param with expert level)
1961
- if self.protocol.hasExpert():
1962
- expFrame = tk.Frame(paramsFrame, name="expert")
1963
- expLabel = tk.Label(expFrame, text=pwutils.Message.LABEL_EXPERT, font=self.fontBold)
1964
- expLabel.grid(row=0, column=0, sticky='nw', padx=5)
1965
- expCombo = self._createBoundOptions(expFrame, pwutils.Message.VAR_EXPERT, pwprot.LEVEL_CHOICES,
1966
- self.protocol.expertLevel.get(),
1967
- self._onExpertLevelChanged, font=self.font)
1968
- expCombo.grid(row=0, column=1, sticky='nw', pady=5)
1969
- expFrame.grid(row=0, column=0, sticky='nw')
1970
-
1971
- contentFrame = self._createSections(paramsFrame)
1972
- contentFrame.grid(row=1, column=0, sticky='news')
1973
-
1974
- return paramsFrame
1975
-
1976
- def _isLegacyProtocol(self):
1977
- return isinstance(self.protocol, pwprot.LegacyProtocol)
1978
-
1979
- def _createLegacyInfo(self, parent):
1980
- frame = tk.Frame(parent, name="legacy")
1981
- t = tk.Label(frame,
1982
- text="This protocol is missing in this installation. "
1983
- "\nThis could be because you are opening an old "
1984
- "project and some of \nthe executed protocols do "
1985
- "not exist anymore and were deprecated"
1986
- ",\n or because your scipion installation requires a "
1987
- "plugin where this protocol can be found.\n\n"
1988
- "If you are a developer, it could be the case that "
1989
- "you have changed \nto another branch where the "
1990
- "protocol does not exist.\n\n"
1991
- "Anyway, you can still inspect the parameters by "
1992
- "opening the DB from the toolbar activating the Debug mode."
1993
- )
1994
- t.grid(row=0, column=0, padx=5, pady=5)
1995
-
1996
- return frame
1997
-
1998
- def _createSections(self, parent):
1999
- """Create section widgets"""
2000
- r = 0
2001
- sectionsFrame = tk.Frame(parent)
2002
- configureWeigths(sectionsFrame)
2003
- tab = ttk.Notebook(sectionsFrame)
2004
- tab.grid(row=0, column=0, sticky='news',
2005
- padx=5, pady=5)
2006
- self._sections = []
2007
-
2008
- for section in self.protocol.iterDefinitionSections():
2009
- label = section.getLabel()
2010
- if label != 'General' and label != 'Parallelization':
2011
- frame = SectionWidget(self, tab, section, height=150,
2012
- callback=self._checkChanges,
2013
- headerBgColor=self.headerBgColor)
2014
-
2015
- tab.add(frame, text=section.getLabel())
2016
- frame.columnconfigure(0, minsize=400)
2017
- self._fillSection(section, frame)
2018
- self._sections.append(frame)
2019
- r += 1
2020
- self._checkAllChanges()
2021
-
2022
- return sectionsFrame
2023
-
2024
- def _createButtons(self, parent):
2025
- """ Create the bottom buttons: Close, Save and Execute. """
2026
- btnFrame = tk.Frame(parent)
2027
-
2028
- btnClose = self.createCloseButton(btnFrame)
2029
- btnClose.grid(row=0, column=0, padx=5, pady=5, sticky='se')
2030
- # Save button is not added in VISUALIZE or CHILD modes
2031
- # Neither in the case of a LegacyProtocol
2032
- if (not self.visualizeMode and not self.childMode and
2033
- not self._isLegacyProtocol()):
2034
- # Check editable or not:
2035
- btnState = tk.DISABLED if (self.protocol.isActive()
2036
- and not self.protocol.isInteractive()) \
2037
- else tk.NORMAL
2038
-
2039
- btnSaveState = tk.DISABLED if (btnState == tk.DISABLED or
2040
- self.protocol.getOutputsSize()) \
2041
- else tk.NORMAL
2042
-
2043
- self.btnSave = Button(btnFrame, pwutils.Message.LABEL_BUTTON_RETURN,
2044
- pwutils.Icon.ACTION_SAVE, command=self.save,
2045
- state=btnSaveState)
2046
- self.btnSave.grid(row=0, column=1, padx=5, pady=5, sticky='se')
2047
- self.btnExecute = HotButton(btnFrame, pwutils.Message.LABEL_BUTTON_EXEC,
2048
- pwutils.Icon.ACTION_EXECUTE,
2049
- command=self.execute, state=btnState)
2050
- self.btnExecute.grid(row=0, column=2, padx=(5, 28),
2051
- pady=5, sticky='se')
2052
- self._onPointerChanged()
2053
-
2054
- return btnFrame
2055
-
2056
- def _addVarBinding(self, paramName, var, func=None, *callbacks):
2057
- if func is None:
2058
- func = self.setParamFromVar
2059
- binding = Binding(paramName, var, self.protocol,
2060
- func, *callbacks)
2061
- self.widgetDict[paramName] = var
2062
- self.bindings.append(binding)
2063
-
2064
- def _createBoundEntry(self, parent, paramName, width=5,
2065
- func=None, value=None, **kwargs):
2066
- var = tk.StringVar()
2067
- setattr(self, paramName + 'Var', var)
2068
- self._addVarBinding(paramName, var, func)
2069
- if value is not None:
2070
- var.set(value)
2071
- return tk.Entry(parent, font=self.font, width=width,
2072
- textvariable=var, **kwargs)
2073
-
2074
- def _createEnumBinding(self, paramName, choices, value=None, *callbacks):
2075
- param = pwprot.EnumParam(choices=choices)
2076
- var = ComboVar(param)
2077
- if value is not None:
2078
- var.set(value)
2079
- self._addVarBinding(paramName, var, None, *callbacks)
2080
- return param, var
2081
-
2082
- def _createBoundOptions(self, parent, paramName, choices, value, *callbacks, **kwargs):
2083
- param, var = self._createEnumBinding(paramName, choices, value, *callbacks)
2084
- rbArgs = {}
2085
- frameArgs = dict(kwargs)
2086
- if 'bg' in kwargs:
2087
- rbArgs['bg'] = kwargs['bg']
2088
-
2089
- if 'font' in kwargs:
2090
- rbArgs['font'] = kwargs['font']
2091
- del frameArgs['font']
2092
-
2093
- frame = tk.Frame(parent, **frameArgs)
2094
- for i, opt in enumerate(param.choices):
2095
- rb = tk.Radiobutton(frame, text=opt, variable=var.tkVar, value=opt, highlightthickness=0, **rbArgs)
2096
- rb.grid(row=0, column=i, sticky='nw', padx=(0, 5))
2097
-
2098
- return frame
2099
-
2100
- def _createHeaderLabel(self, parent, text, bold=False, **gridArgs):
2101
- font = self.font
2102
- if bold:
2103
- font = self.fontBold
2104
- label = tk.Label(parent, text=text, font=font, bg=pw.Config.SCIPION_BG_COLOR)
2105
- if gridArgs:
2106
- gridDefaults = {'row': 0, 'column': 0, 'padx': 5, 'pady': 5}
2107
- gridDefaults.update(gridArgs)
2108
- label.grid(**gridDefaults)
2109
- return label
2110
-
2111
- def resize(self, frame):
2112
- self.root.update_idletasks()
2113
- MaxHeight = 1200
2114
- MaxWidth = 1600
2115
- rh = frame.winfo_reqheight()
2116
- rw = frame.winfo_reqwidth()
2117
- height = min(rh + 100, MaxHeight)
2118
- width = min(rw, MaxWidth)
2119
- x = self.root.winfo_x()
2120
- y = self.root.winfo_y()
2121
- self.root.geometry("%dx%d%+d%+d" % (width, height, x, y))
2122
-
2123
- return width, height
2124
-
2125
- def adjustSize(self):
2126
- self.resize(self.root)
2127
-
2128
- def save(self, e=None):
2129
- self._close(onlySave=True)
2130
-
2131
- def schedule(self):
2132
- if self.protocol.useQueue():
2133
- if not self._getQueueReady():
2134
- return
2135
-
2136
- self._close(doSchedule=True)
2137
-
2138
- def _getQueueReady(self):
2139
- """ Check if queue is active, if so ask for params if missing"""
2140
- if self.protocol.hasQueueParams() and self.protocol.queueShown:
2141
- return True
2142
- else:
2143
- return self._editQueueParams()
2144
-
2145
- def execute(self, e=None):
2146
- if self.protocol.useQueue():
2147
- if not self._getQueueReady():
2148
- return
2149
- else: # use queue = No
2150
- hostConfig = self._getHostConfig()
2151
- cores = self.protocol.numberOfMpi.get(1) * self.protocol.numberOfThreads.get(1)
2152
- mandatory = hostConfig.queueSystem.getMandatory()
2153
-
2154
- if mandatory and cores >= mandatory:
2155
- self.showWarning("You need to submit the job to queue since you \n"
2156
- "are requesting a total of *%d* cores (MPI * threads)\n\n"
2157
- "*Note*: Your system is configured with MANDATORY = %d.\n"
2158
- " This value can be changed in Scipion/config/hosts.conf" % (cores, mandatory))
2159
- return
2160
-
2161
- errors = []
2162
- resultAction = RESULT_RUN_SINGLE
2163
- mode = MODE_RESTART if self.protocol.getRunMode() == MODE_RESTART else MODE_RESUME
2164
-
2165
- # we only take into account the protocols that are already part of the workflow
2166
- if not self.protocol.isNew():
2167
- from pyworkflow.gui.project.viewprotocols import ProtocolsView
2168
- errors, resultAction = ProtocolsView._launchSubWorkflow(self.protocol,
2169
- mode, self.root,
2170
- askSingleAll=True)
2171
-
2172
- if errors:
2173
- self.showInfo(errors)
2174
- return
2175
-
2176
- if resultAction == RESULT_CANCEL:
2177
- return
2178
- elif resultAction == RESULT_RUN_ALL:
2179
- if errors:
2180
- self.showInfo(errors)
2181
- self.close()
2182
- return
2183
-
2184
- # This code will happen when protocol is executed alone
2185
- errors += self.protocol.validate()
2186
-
2187
- if errors:
2188
- self.showInfo(errors)
2189
- else:
2190
- warns = self.protocol.warnings()
2191
- if warns and not self.askYesNo("There are some warnings",
2192
- '\n'.join(warns + ['\nDo you want to continue?'])):
2193
- return
2194
- self._close()
2195
-
2196
- def _close(self, onlySave=False, doSchedule=False):
2197
- try:
2198
- # Set the protocol label
2199
- self.updateProtocolLabel()
2200
-
2201
- # Clear parameters that are pointers and do not match the condition
2202
- # to avoid ghost inputs
2203
- self._checkAllChanges(toggleWidgetVisibility=False)
2204
-
2205
- message = self.callback(self.protocol, onlySave, doSchedule, position=self.position)
2206
- if not self.visualizeMode:
2207
- if len(message):
2208
- self.showInfo(message, "Protocol action")
2209
- if not onlySave:
2210
- self.close()
2211
- except ModificationNotAllowedException as ex:
2212
- self.showInfo("Modification not allowed.\n\n %s\n" % ex)
2213
- except Exception as ex:
2214
- action = "EXECUTE"
2215
- if onlySave:
2216
- action = "SAVE"
2217
- self.showError("Error during %s: \n%s" % (action, ex), exception=ex)
2218
-
2219
- def getWidgetValue(self, protVar, param):
2220
- widgetValue = ""
2221
- if (isinstance(param, pwprot.PointerParam) or
2222
- isinstance(param, pwprot.MultiPointerParam) or
2223
- isinstance(param, pwprot.RelationParam)):
2224
- widgetValue = protVar
2225
- # For Scalar params that allowPointers
2226
- elif param.allowsPointers:
2227
- if protVar.hasPointer():
2228
- # Get the pointer
2229
- widgetValue = protVar.getPointer()
2230
- else:
2231
- widgetValue = protVar.get()
2232
- else:
2233
- widgetValue = protVar.get(param.default.get())
2234
- return widgetValue
2235
-
2236
- def _visualize(self, paramName):
2237
- protVar = getattr(self.protocol, paramName)
2238
- if protVar.hasValue():
2239
- obj = protVar.get() # Get the reference to the object
2240
- viewers = pw.Config.getDomain().findViewers(obj.getClassName(), DESKTOP_TKINTER)
2241
- if len(viewers):
2242
- ViewerClass = viewers[0] # Use the first viewer registered
2243
- v = ViewerClass(project=self.protocol.getProject(),
2244
- protocol=self.protocol, parent=self)
2245
- v.visualize(obj) # Instantiate the viewer and visualize object
2246
- else:
2247
- self.showInfo("There is no viewer registered for this object")
2248
- else:
2249
- self.showInfo("Select the object before visualize")
2250
-
2251
- def _fillSection(self, sectionParam, sectionWidget):
2252
- parent = sectionWidget.contentFrame
2253
- r = 0
2254
- for paramName, param in sectionParam.iterParams():
2255
- if isinstance(param, pwprot.Group):
2256
- widget = GroupWidget(r, paramName, param, self, parent)
2257
- self._fillGroup(param, widget)
2258
- elif isinstance(param, pwprot.Line):
2259
- widget = LineWidget(r, paramName, param, self, parent, None)
2260
- self._fillLine(param, widget)
2261
- else:
2262
- protVar = getattr(self.protocol, paramName, None)
2263
-
2264
- if protVar is None:
2265
- raise Exception("_fillSection: param '%s' not found in protocol" % paramName)
2266
-
2267
- if sectionParam.getQuestionName() == paramName:
2268
- widget = sectionWidget
2269
- if not protVar:
2270
- widget.hide() # Show only if question var is True
2271
- else:
2272
- if isinstance(param, pwprot.PointerParam):
2273
- visualizeCallback = self._visualize # Add visualize icon for pointer params
2274
- else:
2275
- visualizeCallback = self.visualizeDict.get(paramName, None)
2276
-
2277
- widget = ParamWidget(r, paramName, param, self, parent,
2278
- value=self.getWidgetValue(protVar, param),
2279
- callback=self._checkChanges,
2280
- visualizeCallback=visualizeCallback)
2281
-
2282
- widget.show() # Show always, conditions will be checked later
2283
- r += 1
2284
- self.widgetDict[paramName] = widget
2285
- # Ensure width and height needed
2286
- w, h = parent.winfo_reqwidth(), parent.winfo_reqheight()
2287
- sectionWidget.columnconfigure(0, minsize=w)
2288
- sectionWidget.rowconfigure(0, minsize=h)
2289
-
2290
- def _fillGroup(self, groupParam, groupWidget):
2291
- parent = groupWidget.content
2292
- r = 0
2293
- for paramName, param in groupParam.iterParams():
2294
- if isinstance(param, pwprot.Line):
2295
- widget = LineWidget(r, paramName, param, self, parent, None)
2296
- self._fillLine(param, widget)
2297
- else:
2298
- protVar = getattr(self.protocol, paramName, None)
2299
-
2300
- if protVar is None:
2301
- raise Exception("_fillSection: param '%s' not found in protocol" % paramName)
2302
-
2303
- if isinstance(param, pwprot.PointerParam):
2304
- visualizeCallback = self._visualize # Add visualize icon for pointer params
2305
- else:
2306
- visualizeCallback = self.visualizeDict.get(paramName, None)
2307
-
2308
- widget = ParamWidget(r, paramName, param, self, parent,
2309
- value=self.getWidgetValue(protVar, param),
2310
- callback=self._checkChanges,
2311
- visualizeCallback=visualizeCallback)
2312
- widget.show() # Show always, conditions will be checked later
2313
- r += 1
2314
- self.widgetDict[paramName] = widget
2315
-
2316
- def _fillLine(self, groupParam, groupWidget):
2317
- parent = groupWidget.content
2318
- c = 0
2319
- for paramName, param in groupParam.iterParams():
2320
- protVar = getattr(self.protocol, paramName, None)
2321
-
2322
- if protVar is None:
2323
- raise Exception("_fillSection: param '%s' not found in protocol" % paramName)
2324
-
2325
- if isinstance(param, pwprot.PointerParam):
2326
- visualizeCallback = self._visualize # Add visualize icon for pointer params
2327
- else:
2328
- visualizeCallback = self.visualizeDict.get(paramName, None)
2329
-
2330
- widget = ParamWidget(0, paramName, param, self, parent,
2331
- value=self.getWidgetValue(protVar, param),
2332
- callback=self._checkChanges, visualizeCallback=visualizeCallback,
2333
- column=c, showButtons=False)
2334
- widget.show() # Show always, conditions will be checked later
2335
- c += 2
2336
- self.widgetDict[paramName] = widget
2337
-
2338
- def _checkCondition(self, paramName, toggleWidgetVisibility=True):
2339
- """Check if the condition of a param is satisfied
2340
- hide or show it depending on the result"""
2341
- widget = self.widgetDict.get(paramName, None)
2342
-
2343
- if isinstance(widget, ParamWidget): # Special vars like MPI, threads or runName are not real widgets
2344
- if isinstance(widget, LineWidget) or isinstance(widget, GroupWidget):
2345
- param = widget.param
2346
- else:
2347
- param = self.protocol.getParam(paramName)
2348
-
2349
- showLevel = self.protocol.evalParamExpertLevel(param)
2350
- showCondition = self.protocol.evalParamCondition(paramName)
2351
- show = showCondition and showLevel
2352
-
2353
- if toggleWidgetVisibility:
2354
- widget.display(show)
2355
- else:
2356
- # If condition is false and param is a pointer, or Multipointer ...
2357
- if (not showCondition) and isinstance(param, pwprot.PointerParam):
2358
- widget.clear()
2359
-
2360
- def _checkChanges(self, paramName):
2361
- """Check the conditions of all params affected
2362
- by this param"""
2363
- self.setParamFromVar(paramName)
2364
- param = self.protocol.getParam(paramName)
2365
-
2366
- for d in param._dependants:
2367
- self._checkCondition(d)
2368
-
2369
- self.adjustSections()
2370
-
2371
- def _checkAllChanges(self, toggleWidgetVisibility=True):
2372
- for paramName in self.widgetDict:
2373
- self._checkCondition(paramName, toggleWidgetVisibility=toggleWidgetVisibility)
2374
-
2375
- def _onExpertLevelChanged(self, *args):
2376
- self._checkAllChanges()
2377
- self.root.update_idletasks()
2378
- self.adjustSections()
2379
-
2380
- def adjustSections(self):
2381
-
2382
- for s in self._sections:
2383
- s.adjustContent()
2384
-
2385
- def _setGpu(self, *args):
2386
- prot = self.protocol # shortcut notation
2387
- if not prot.requiresGpu(): # Only set this if gpu is optional
2388
- prot.useGpu.set(self.useGpuVar.get())
2389
- prot.gpuList.set(self.gpuListVar.get())
2390
-
2391
- def _setWaitFor(self, *args):
2392
- l1 = self.waitForVar.get().split(',')
2393
- idList = []
2394
- for p1 in l1:
2395
- idList.extend([p2.strip() for p2 in p1.split(' ') if p2])
2396
- try:
2397
- idIntList = map(int, idList)
2398
- self.protocol.setPrerequisites(*idIntList)
2399
- except Exception as ex:
2400
- pass
2401
-
2402
- def _setHostName(self, *args):
2403
- self.protocol.setHostName(self.hostVar.get())
2404
-
2405
- def _onRunModeChanged(self, paramName):
2406
- self.setParamFromVar(paramName)
2407
-
2408
- def getVarValue(self, varName):
2409
- """This method should retrieve a value from """
2410
- pass
2411
-
2412
- def setVar(self, paramName, value):
2413
- var = self.widgetDict[paramName]
2414
- var.set(value)
2415
-
2416
- def setVarFromParam(self, paramName):
2417
- var = self.widgetDict[paramName]
2418
- param = getattr(self.protocol, paramName, None)
2419
- if param is not None:
2420
- # Special treatment to pointer params
2421
- if isinstance(param, pwobj.Pointer):
2422
- var.set(param)
2423
- else:
2424
- var.set(param.get(''))
2425
-
2426
- def setParamFromVar(self, paramName):
2427
- param = getattr(self.protocol, paramName, None)
2428
- if param is not None:
2429
- widget = self.widgetDict[paramName]
2430
- try:
2431
- value = widget.get()
2432
-
2433
- # Special treatment for pointer params
2434
- if isinstance(param, pwobj.Pointer):
2435
- param.copy(value)
2436
- # Special treatment for Scalars that allow pointers
2437
- # Combo widgets do not have .param!
2438
- elif hasattr(widget, "param") and widget.param.allowsPointers:
2439
- if isinstance(value, pwobj.Pointer):
2440
- # Copy the pointer, otherwise changes in the
2441
- # widget pointer will be reflected
2442
- pointerCopy = pwobj.Pointer()
2443
- pointerCopy.copy(value)
2444
- param.setPointer(pointerCopy)
2445
- else:
2446
- param.setPointer(None)
2447
- param.set(value)
2448
-
2449
- elif isinstance(param, pwobj.Object):
2450
- param.set(value)
2451
- except ValueError:
2452
- if len(value):
2453
- print(">>> ERROR: setting param for: ", paramName,
2454
- "value: '%s'" % value)
2455
- param.set(None)
2456
-
2457
- def updateLabelAndCommentVars(self):
2458
- """ Read the label and comment first line to update
2459
- the entry boxes in the form.
2460
- """
2461
- self.runNameVar.set(self.protocol.getObjLabel())
2462
- # Get only the first comment line
2463
- comment = self.protocol.getObjComment()
2464
- if comment:
2465
- lines = comment.split('\n')
2466
- if lines:
2467
- comment = lines[0]
2468
- self.commentVar.set(comment)
2469
-
2470
- def updateProtocolLabel(self):
2471
- self.protocol.setObjLabel(self.runNameVar.get())
2472
-
2473
- def updateProtocolParams(self):
2474
- """ This method is only used from WEB, since in Tk all params
2475
- are updated when they are changed.
2476
- """
2477
- for paramName, _ in self.protocol.iterDefinitionAttributes():
2478
- self.setParamFromVar(paramName)
2479
-
2480
- def _onPointerChanged(self, *args):
2481
- btnExecute = getattr(self, 'btnExecute', None)
2482
-
2483
- # This event can be fired even before the button is created
2484
- if btnExecute is None:
2485
- return
2486
- btnState = tk.DISABLED if (self.protocol.isActive() and not self.protocol.isInteractive()) else tk.NORMAL
2487
- emptyInput, openSetPointer, emptyPointers = self.protocol.getInputStatus()
2488
-
2489
- if emptyInput:
2490
- btnState = tk.DISABLED
2491
-
2492
- if openSetPointer or emptyPointers:
2493
- btnText = 'Schedule'
2494
- cmd = self.schedule
2495
- else:
2496
- btnText = pwutils.Message.LABEL_BUTTON_EXEC
2497
- cmd = self.execute
2498
-
2499
- btnExecute.config(text=btnText, command=cmd, state=btnState)
2500
-
2501
-
2502
- def takeScreenShot(self):
2503
- """ Method to take a screenshot of itself.
2504
- The idea is to, in the future, take a screenshot and collect parameter
2505
- to either create a page of the protocol in rst or send it to the
2506
- Scipion site and have there one page per protocol.
2507
-
2508
- For now this is not used."""
2509
- from PIL import ImageGrab
2510
-
2511
- x,y, width, height = (self.root.winfo_x(), self.root.winfo_y(), self.root.winfo_width(), self.root.winfo_height())
2512
- ss_region = (x,y,x+width, y+height)
2513
-
2514
- ss_img = ImageGrab.grab(ss_region)
2515
- ss_img.save("/tmp/form.png")
2516
-
2517
- def editObject(self, title, root, obj, mapper):
2518
- """ Show a Text area to edit the protocol label and comment. """
2519
- return EditObjectDialog(root, title, obj, mapper)
2520
-
2521
-
2522
- class QueueDialog(Dialog):
2523
- """ Dialog to entry the queue parameters. """
2524
-
2525
- def __init__(self, window, queueDict):
2526
- self.value = None
2527
- self.widgets = [] # widget list
2528
- self.vars = []
2529
- self.queueDict = queueDict
2530
- self.window = window
2531
- self.queueName, queueParams = window.protocol.getQueueParams()
2532
- # If there is only one queue and not one selected, use the first one
2533
- if not self.queueName and len(queueDict.keys()) == 1:
2534
- self.queueName = list(queueDict.keys())[0]
2535
- queueParams = {}
2536
- # Store all selected queue parameters to
2537
- # preserve values when temporarily changed
2538
- # from one queue to another
2539
- self.allQueueParams = {self.queueName: queueParams}
2540
-
2541
- Dialog.__init__(self, window.root, "Queue parameters")
2542
-
2543
- def body(self, bodyFrame):
2544
- bodyFrame.config(bg=pw.Config.SCIPION_BG_COLOR)
2545
- self.content = tk.Frame(bodyFrame, bg=pw.Config.SCIPION_BG_COLOR)
2546
- self.content.grid(row=0, column=0, padx=20, pady=20)
2547
-
2548
- label = tk.Label(self.content, text='Submit to queue',
2549
- font=self.window.fontBold, bg=pw.Config.SCIPION_BG_COLOR)
2550
- label.grid(row=0, column=0, sticky='ne', padx=5, pady=5)
2551
- self.queueVar = tk.StringVar()
2552
- self.queueVar.trace('w', self._onQueueChanged)
2553
- combo = ttk.Combobox(self.content, textvariable=self.queueVar,
2554
- state='readonly', width=14)
2555
- combo.grid(row=0, column=1, sticky='nw', padx=5, pady=5)
2556
- queueKeys = list(self.queueDict.keys())
2557
- combo['values'] = queueKeys
2558
- self.queueVar.set(self.queueName) # This will trigger queue params setup
2559
- self.initial_focus = combo
2560
-
2561
- def _onQueueChanged(self, *args):
2562
- for w in self.widgets:
2563
- w.destroy()
2564
-
2565
- selected = self.queueVar.get()
2566
-
2567
- if selected != self.queueName:
2568
- # Store previous selection
2569
- _, previousParams = self._getSelectedParams(self.queueName)
2570
- self.allQueueParams[self.queueName] = previousParams
2571
- self.queueName = selected
2572
-
2573
- # Load default params from the queues
2574
- params = self.queueDict.get(selected, {})
2575
- # Load previous selected params
2576
- selectedParams = self.allQueueParams.get(selected, {})
2577
-
2578
- self.widgets = [] # clear the widget list
2579
- self.vars = []
2580
- r = 1 # starting row to place params
2581
- for p in params:
2582
- if len(p) == 3: # No help provided
2583
- name, value, label = p
2584
- helpMsg = None
2585
- elif len(p) == 4:
2586
- name, value, label, helpMsg = p
2587
- else:
2588
- raise Exception('Incorrect number of params for %s, expected 3 or 4' % p[0])
2589
-
2590
- label = tk.Label(self.content, text=label, bg=pw.Config.SCIPION_BG_COLOR)
2591
- label.grid(row=r, column=0, sticky='ne', padx=5, pady=(0, 5))
2592
- var = tk.StringVar()
2593
- # Set the value coming in the protocol
2594
- var.set(selectedParams.get(name, value))
2595
-
2596
- entry = tk.Entry(self.content, textvariable=var, width=15)
2597
- entry.grid(row=r, column=1, sticky='nw', padx=5, pady=(0, 5))
2598
-
2599
- if helpMsg:
2600
- def addHelpButton(name, helpMsg):
2601
- def showHelp():
2602
- showInfo("Help", helpMsg, self)
2603
-
2604
- btn = IconButton(self.content, pwutils.Message.LABEL_BUTTON_HELP,
2605
- pwutils.Icon.ACTION_HELP,
2606
- command=showHelp)
2607
- btn.grid(row=r, column=2, sticky='ne', padx=5, pady=(0, 5))
2608
- self.widgets.append(btn)
2609
-
2610
- addHelpButton(name, helpMsg)
2611
-
2612
- self.vars.append(var)
2613
- self.widgets.append(label)
2614
- self.widgets.append(entry)
2615
- r += 1
2616
-
2617
- def _getSelectedParams(self, selected):
2618
- if selected in self.queueDict:
2619
- paramsDict = {}
2620
- params = self.queueDict[selected]
2621
- for p, v in zip(params, self.vars):
2622
- if len(p) == 3:
2623
- name, value, label = p
2624
- else:
2625
- name, value, label, _ = p
2626
- paramsDict[name] = v.get() # get the value from the corresponding tk var
2627
- return selected, paramsDict
2628
- return '', {}
2629
-
2630
- def apply(self):
2631
- # Set as value the queue selected and a dictionary
2632
- # with the values of each parameter
2633
- selected = self.queueVar.get()
2634
- self.value = self._getSelectedParams(selected)
2635
-
2636
- def validate(self):
2637
- return True