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
@@ -1,560 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # **************************************************************************
3
- # *
4
- # * Authors: Pablo Conesa [1]
5
- # *
6
- # * [1] Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
7
- # *
8
- # * This program is free software: you can redistribute it and/or modify
9
- # * it under the terms of the GNU General Public License as published by
10
- # * the Free Software Foundation, either version 3 of the License, or
11
- # * (at your option) any later version.
12
- # *
13
- # * This program is distributed in the hope that it will be useful,
14
- # * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # * GNU General Public License for more details.
17
- # *
18
- # * You should have received a copy of the GNU General Public License
19
- # * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
- # *
21
- # * All comments concerning this program package may be sent to the
22
- # * e-mail address 'scipion@cnb.csic.es'
23
- # *
24
- # **************************************************************************
25
- """ This modules hosts most of the accessory code that is used in view protocols"""
26
- import json
27
- import os
28
- from configparser import ConfigParser
29
-
30
- from pyworkflow import Config
31
- import pyworkflow.gui as pwgui
32
- import pyworkflow.object as pwobj
33
- import pyworkflow.utils as pwutils
34
- from pyworkflow.gui.project.utils import isAFinalProtocol
35
- from pyworkflow.project import MenuConfig
36
- from pyworkflow.utils import Message, Icon
37
- from pyworkflow.viewer import DESKTOP_TKINTER
38
-
39
-
40
-
41
- class RunIOTreeProvider(pwgui.tree.TreeProvider):
42
- """Create the tree elements from a Protocol Run input/output children"""
43
-
44
- def __init__(self, parent, protocol, mapper, loggerCallback):
45
- """
46
-
47
- :param parent:
48
- :param protocol:
49
- :param mapper:
50
- :param loggerCallback: method to call to log events in the gui.
51
- """
52
-
53
- self.parent = parent
54
- self.protocol = protocol
55
- self.mapper = mapper
56
- self._loggerCallback = loggerCallback
57
-
58
- @staticmethod
59
- def getColumns():
60
- return [('Attribute', 200), ('Info', 100)]
61
-
62
- def getObjects(self):
63
- objs = []
64
- if self.protocol is not None:
65
- # Store a dict with input parents (input, PointerList)
66
- self.inputParentDict = pwobj.OrderedDict()
67
- inputs = []
68
- inputObj = pwobj.String(Message.LABEL_INPUT)
69
- inputObj._icon = Icon.ACTION_IN
70
- self.inputParentDict['_input'] = inputObj
71
- inputParents = [inputObj]
72
-
73
- for key, attr in self.protocol.iterInputAttributes():
74
- attr._parentKey = key
75
- # Repeated keys means there are inside a pointerList
76
- # since the same key is yielded for all items inside
77
- # so update the parent dict with a new object
78
- if key in self.inputParentDict:
79
- if self.inputParentDict[key] == inputObj:
80
- parentObj = pwobj.String(key)
81
- parentObj._icon = Icon.ACTION_IN
82
- parentObj._parentKey = '_input'
83
- inputParents.append(parentObj)
84
- self.inputParentDict[key] = parentObj
85
- else:
86
- self.inputParentDict[key] = inputObj
87
- inputs.append(attr)
88
-
89
- outputs = [attr for _, attr in
90
- self.protocol.iterOutputAttributes()]
91
- self.outputStr = pwobj.String(Message.LABEL_OUTPUT)
92
- objs = inputParents + inputs + [self.outputStr] + outputs
93
- return objs
94
-
95
- def _visualizeObject(self, ViewerClass, obj):
96
- viewer = ViewerClass(project=self.protocol.getProject(),
97
- protocol=self.protocol,
98
- parent=self.parent.window)
99
- viewer.visualize(obj, windows=self.parent.window)
100
-
101
- def _editObject(self, obj):
102
- """Open the Edit GUI Form given an instance"""
103
- pwgui.dialog.EditObjectDialog(self.parent, Message.TITLE_EDIT_OBJECT,
104
- obj, self.mapper)
105
-
106
- def _deleteObject(self, obj):
107
- """ Remove unnecessary output, specially for Coordinates. """
108
- prot = self.protocol
109
- try:
110
- objLabel = self.getObjectLabel(obj, prot)
111
- if self.parent.window.askYesNo("Delete object",
112
- "Are you sure to delete *%s* object?"
113
- % objLabel):
114
- prot.getProject().deleteProtocolOutput(prot, obj)
115
- self.parent._fillSummary()
116
- self.parent.window.showInfo("Object *%s* successfully deleted."
117
- % objLabel)
118
- except Exception as ex:
119
- self.parent.window.showError(str(ex))
120
-
121
- @staticmethod
122
- def getObjectPreview(obj):
123
- desc = "<name>: " + obj.getName()
124
- return None, desc
125
-
126
- def getObjectActions(self, obj):
127
- if isinstance(obj, pwobj.Pointer):
128
- obj = obj.get()
129
- isPointer = True
130
- else:
131
- isPointer = False
132
- actions = []
133
-
134
- # If viewers not loaded yet (firstime)
135
- domain = Config.getDomain()
136
-
137
- if not domain.viewersLoaded():
138
- self._loggerCallback("Discovering viewers for the first time across all the plugins.")
139
-
140
-
141
- viewers = Config.getDomain().findViewers(obj.getClassName(), DESKTOP_TKINTER)
142
-
143
- def viewerCallback(viewer):
144
- return lambda: self._visualizeObject(viewer, obj)
145
-
146
- for v in viewers:
147
- actions.append((v.getName(),
148
- viewerCallback(v),
149
- Icon.ACTION_VISUALIZE))
150
- # EDIT
151
- actions.append((Message.LABEL_EDIT,
152
- lambda: self._editObject(obj),
153
- Icon.ACTION_EDIT))
154
- # DELETE
155
- # Special case to allow delete outputCoordinates
156
- # since we can end up with several outputs and
157
- # we may want to clean up
158
- if self.protocol.allowsDelete(obj) and not isPointer:
159
- actions.append((Message.LABEL_DELETE,
160
- lambda: self._deleteObject(obj),
161
- Icon.ACTION_DELETE))
162
- return actions
163
-
164
- @staticmethod
165
- def getObjectLabel(obj, parent):
166
- """ We will try to show in the list the string representation
167
- that is more readable for the user to pick the desired object.
168
- """
169
- label = 'None'
170
- if obj:
171
- label = obj.getObjLabel()
172
- if not len(label.strip()):
173
- parentLabel = parent.getObjLabel() if parent else 'None'
174
- label = "%s -> %s" % (parentLabel, obj.getLastName())
175
- return label
176
-
177
- def getObjectInfo(self, obj):
178
-
179
- def stringToInfo():
180
- """ String objects converted to info dictionary for the tree"""
181
-
182
- value = obj.get()
183
- infoStr = {'key': value, 'text': value, 'values': '', 'open': True}
184
- if hasattr(obj, '_parentKey'):
185
- infoStr['parent'] = self.inputParentDict[obj._parentKey]
186
- return infoStr
187
-
188
- def labelToValue(label, key, name):
189
- """ To tolerate str(labelObj) in case xmippLib is missing, but
190
- still being able to open a project."""
191
- try:
192
- value = str(label)
193
- except Exception as e:
194
- print("Can not convert object %s - %s to string." % (key, name))
195
- value = str(e)
196
-
197
- return value
198
-
199
- def pointerToInfo():
200
- """ Converts a Pointer into an info dictionary for the tree"""
201
-
202
- namePtr = obj.getLastName()
203
- # Remove ugly item notations inside lists
204
- namePtr = namePtr.replace('__item__000', '')
205
- # Consider Pointer as inputs
206
- imagePtr = getattr(obj, '_icon', '')
207
- parentPtr = self.inputParentDict[obj._parentKey]
208
-
209
- suffix = ''
210
- if obj.hasExtended():
211
- # getExtended method remove old attributes conventions.
212
- extendedValue = obj.getExtended()
213
- if obj.hasExtended():
214
- suffix = '[%s]' % extendedValue
215
- # else:
216
- # suffix = '[Item %s]' % extendedValue
217
-
218
- # Tolerate loading projects:
219
- # When having only the project sqlite..an obj.get() will
220
- # the load of the set...and if it is missing this whole
221
- # "thread" fails.
222
- try:
223
- labelObjPtr = obj.get()
224
- if labelObjPtr is None:
225
- labelObjPtr = obj.getObjValue()
226
- suffix = ''
227
-
228
- except Exception:
229
- return {'parent': parentPtr, 'image': imagePtr, 'text': namePtr,
230
- 'values': ("Couldn't read object attributes.",)}
231
- else:
232
- labelObjPtr = obj.get()
233
-
234
- objKeyPtr = obj._parentKey + str(labelObjPtr.getObjId())
235
- labelPtr = self.getObjectLabel(labelObjPtr,
236
- self.mapper.getParent(labelObjPtr))
237
- namePtr += ' (from %s %s)' % (labelPtr, suffix)
238
- valuePtr = labelToValue(labelObjPtr, objKeyPtr, namePtr)
239
- infoPtr = {'key': objKeyPtr, 'parent': parentPtr, 'image': imagePtr,
240
- 'text': namePtr, 'values': (valuePtr,)}
241
-
242
- return infoPtr
243
-
244
-
245
- if obj is None or not obj.hasValue():
246
- return None
247
-
248
- if isinstance(obj, pwobj.String) and not obj.getName():
249
- info = stringToInfo()
250
- else:
251
- # All attributes are considered output, unless they are pointers
252
- image = Icon.ACTION_OUT
253
- parent = self.outputStr
254
-
255
- if isinstance(obj, pwobj.Pointer):
256
- info = pointerToInfo()
257
- else:
258
- name = self.getObjectLabel(obj, self.protocol)
259
- objKey = str(obj.getObjId())
260
- labelObj = obj
261
- value = labelToValue(labelObj, objKey, name)
262
- info = {'key': objKey, 'parent': parent, 'image': image,
263
- 'text': name, 'values': (value,)}
264
- return info
265
-
266
- class ProtocolTreeConfig:
267
- """ Handler class that groups functions and constants
268
- related to the protocols tree configuration.
269
- """
270
- ALL_PROTOCOLS = "All"
271
- TAG_PROTOCOL_DISABLED = 'protocol-disabled'
272
- TAG_PROTOCOL = 'protocol'
273
- TAG_SECTION = 'section'
274
- TAG_PROTOCOL_GROUP = 'protocol_group'
275
- TAG_PROTOCOL_BETA = 'protocol_beta'
276
- TAG_PROTOCOL_NEW = 'protocol_new'
277
- TAG_PROTOCOL_UPDATED = 'protocol_updated'
278
- PLUGIN_CONFIG_PROTOCOLS = 'protocols.conf'
279
-
280
- @classmethod
281
- def getProtocolTag(cls, isInstalled, isBeta=False, isNew=False, isUpdated=False):
282
- """ Return the proper tag depending if the protocol is installed or not.
283
- """
284
- if isInstalled:
285
- if isBeta:
286
- return cls.TAG_PROTOCOL_BETA
287
- elif isNew:
288
- return cls.TAG_PROTOCOL_NEW
289
- elif isUpdated:
290
- return cls.TAG_PROTOCOL_UPDATED
291
- return cls.TAG_PROTOCOL
292
- else:
293
- return cls.TAG_PROTOCOL_DISABLED
294
-
295
- @classmethod
296
- def __addToTree(cls, menu, item, checkFunction=None):
297
- """ Helper function to recursively add items to a menu.
298
- Add item (a dictionary that can contain more dictionaries) to menu
299
- If check function is added will use it to check if the value must be added.
300
- """
301
- children = item.pop('children', [])
302
-
303
- if checkFunction is not None:
304
- add = checkFunction(item)
305
- if not add:
306
- return
307
- subMenu = menu.addSubMenu(**item) # we expect item={'text': ...}
308
- for child in children:
309
- cls.__addToTree(subMenu, child, checkFunction) # add recursively to sub-menu
310
-
311
- return subMenu
312
-
313
- @classmethod
314
- def __inSubMenu(cls, child, subMenu):
315
- """
316
- Return True if child belongs to subMenu
317
- """
318
- for ch in subMenu:
319
- if cls.__isProtocol(child):
320
- if ch.value is not None and ch.value == child['value']:
321
- return ch
322
- elif ch.text == child['text']:
323
- return ch
324
- return None
325
-
326
- @classmethod
327
- def _orderSubMenu(cls, session):
328
- """
329
- Sort all children of a given section:
330
- The protocols first, then the sections (the 'more' section at the end)
331
- """
332
-
333
- def sortWhenLastIsAProtocol():
334
- """ Sorts children when the last is a protocol"""
335
- for i in range(lastChildPos - 1, -1, -1):
336
- if childs[i].tag == cls.TAG_PROTOCOL:
337
- break
338
- else:
339
- tmp = childs[i + 1]
340
- childs[i + 1] = childs[i]
341
- childs[i] = tmp
342
-
343
- def sortWhenLastIsNotAProtocol():
344
- """ Sorts children when the last is NOT a protocol"""
345
- for i in range(lastChildPos - 1, -1, -1):
346
- if childs[i].tag == cls.TAG_PROTOCOL:
347
- break
348
- elif 'more' in str(childs[i].text).lower():
349
- tmp = childs[i + 1]
350
- childs[i + 1] = childs[i]
351
- childs[i] = tmp
352
-
353
- lengthSession = len(session.childs)
354
- if lengthSession > 1:
355
- childs = session.childs
356
- lastChildPos = lengthSession - 1
357
- if childs[lastChildPos].tag == cls.TAG_PROTOCOL:
358
- sortWhenLastIsAProtocol()
359
- else:
360
- sortWhenLastIsNotAProtocol()
361
-
362
- @classmethod
363
- def __findTreeLocation(cls, subMenu, children, parent):
364
- """
365
- Locate the protocol position in the given view
366
- """
367
- for child in children:
368
- sm = cls.__inSubMenu(child, subMenu)
369
- if sm is None:
370
- cls.__addToTree(parent, child, cls.__checkItem)
371
- cls._orderSubMenu(parent)
372
- elif child['tag'] == cls.TAG_PROTOCOL_GROUP or child['tag'] == cls.TAG_SECTION:
373
- cls.__findTreeLocation(sm.childs, child['children'], sm)
374
- @classmethod
375
- def __isProtocol(cls, dict):
376
- """ True inf the item has a key named tag with protocol as value"""
377
- return dict["tag"] == cls.TAG_PROTOCOL
378
-
379
- @classmethod
380
- def __isProtocolNode(cls, node):
381
- """ True if tag attribute is protocol"""
382
- return node.tag == cls.TAG_PROTOCOL
383
-
384
-
385
- @classmethod
386
- def __checkItem(cls, item):
387
- """ Function to check if the protocol has to be added or not.
388
- Params:
389
- item: {"tag": "protocol", "value": "ProtImportMovies",
390
- "text": "import movies"}
391
- """
392
- if not cls.__isProtocol(item):
393
- return True
394
-
395
- # It is a protocol as this point, get the class name and
396
- # check if it is disabled
397
- protClassName = item["value"]
398
- protClass = Config.getDomain().getProtocols().get(protClassName)
399
- icon = Icon.PRODUCTION
400
- if protClass is not None:
401
- if protClass.isBeta():
402
- icon = Icon.BETA
403
- elif protClass.isNewDev():
404
- icon = Icon.NEW
405
- elif protClass.isUpdated():
406
- icon = Icon.UPDATED
407
- item['icon'] = icon
408
- return False if protClass is None else not protClass.isDisabled()
409
-
410
- @classmethod
411
- def __addAllProtocols(cls, domain, protocols):
412
- # Add all protocols
413
- allProts = domain.getProtocols()
414
-
415
- # Sort the list
416
- allProtsSorted = sorted(allProts.items(), key=lambda e: e[1].getClassLabel())
417
-
418
- allProtMenu = ProtocolConfig(cls.ALL_PROTOCOLS)
419
- packages = {}
420
-
421
- # Group protocols by package name
422
- for k, v in allProtsSorted:
423
- if isAFinalProtocol(v, k):
424
- packageName = v.getPlugin().getName()
425
-
426
- # Get the package submenu
427
- packageMenu = packages.get(packageName)
428
-
429
- # If no package menu available
430
- if packageMenu is None:
431
- # Add it to the menu ...
432
- packageLine = {"tag": "package", "value": packageName,
433
- "text": packageName}
434
- packageMenu = cls.__addToTree(allProtMenu, packageLine)
435
-
436
- # Store it in the dict
437
- packages[packageName] = packageMenu
438
-
439
- # Add the protocol
440
- tag = cls.getProtocolTag(v.isInstalled(), v.isBeta(), v.isNewDev(), v.isUpdated())
441
-
442
- protLine = {"tag": tag, "value": k,
443
- "text": v.getClassLabel(prependPackageName=False)}
444
-
445
- cls.__addToTree(packageMenu, protLine)
446
-
447
- protocols[cls.ALL_PROTOCOLS] = allProtMenu
448
-
449
- @classmethod
450
- def __addProtocolsFromConf(cls, protocols, protocolsConfPath):
451
- """
452
- Load the protocols in the tree from a given protocols.conf file,
453
- either the global one in Scipion or defined in a plugin.
454
- """
455
-
456
- def addProtocols():
457
- """ Adds protocols defined in the "PROTOCOLS" section of the config file. """
458
- for menuName in cp.options('PROTOCOLS'):
459
- if menuName not in protocols: # The view has not been inserted
460
- menu = ProtocolConfig(menuName)
461
- children = json.loads(cp.get('PROTOCOLS', menuName))
462
- for child in children:
463
- cls.__addToTree(menu, child, cls.__checkItem)
464
- protocols[menuName] = menu
465
- else: # The view has been inserted
466
- menu = protocols.get(menuName)
467
- children = json.loads(cp.get('PROTOCOLS',
468
- menuName))
469
- cls.__findTreeLocation(menu.childs, children, menu)
470
-
471
- # Populate the protocols menu from the plugin config file.
472
- if os.path.exists(protocolsConfPath):
473
- cp = ConfigParser()
474
- cp.optionxform = str # keep case
475
- cp.read(protocolsConfPath)
476
- # Ensure that the protocols section exists
477
- if cp.has_section('PROTOCOLS'):
478
- addProtocols()
479
-
480
- @classmethod
481
- def load(cls, domain, protocolsConf):
482
- """ Read the protocol configuration from a .conf file similar to the
483
- one in scipion/config/protocols.conf,
484
- which is the default one when no file is passed.
485
- """
486
- protocols = dict()
487
- # Read the protocols.conf from Scipion (base) and create an initial
488
- # tree view
489
- cls.__addProtocolsFromConf(protocols, protocolsConf)
490
-
491
- # Read the protocols.conf of any installed plugin
492
- pluginDict = domain.getPlugins()
493
-
494
- for pluginName in pluginDict.keys():
495
- try:
496
-
497
- # if the plugin has a path
498
- if pwutils.isModuleLoaded(pluginName) and pwutils.isModuleAFolder(pluginName):
499
- # Locate the plugin protocols.conf file
500
- protocolsConfPath = os.path.join(
501
- pluginDict[pluginName].__path__[0],
502
- cls.PLUGIN_CONFIG_PROTOCOLS)
503
- cls.__addProtocolsFromConf(protocols, protocolsConfPath)
504
-
505
- except Exception as e:
506
- print('Failed to read settings. The reported error was:\n %s\n'
507
- 'To solve it, fix %s and run again.' % (e, pluginName))
508
-
509
- # Clean empty sections
510
- cls._hideEmptySections(protocols)
511
-
512
- # Add all protocols to All view
513
- cls.__addAllProtocols(domain, protocols)
514
-
515
- return protocols
516
-
517
- @classmethod
518
- def _hideEmptySections(cls, protocols):
519
- """ Cleans all empty sections in the tree"""
520
-
521
- for protConf in protocols.values():
522
- cls._setVisibility(protConf)
523
-
524
- @classmethod
525
- def _setVisibility(cls, node):
526
- """ Sets the visibility of a node based on the presence of a leaf hanging form it"""
527
- if cls.__isProtocolNode(node):
528
- # Default visibility value is true. No need to set it again
529
- return True
530
-
531
- anyLeaf = False
532
-
533
- for child in node.childs:
534
- # NOTE: since python short circuits this, _setVisibility must be called always. So not swap!!
535
- anyLeaf = cls._setVisibility(child) or anyLeaf
536
-
537
- node.visible = anyLeaf
538
-
539
- return anyLeaf
540
-
541
-
542
- class ProtocolConfig(MenuConfig):
543
- """Store protocols configuration """
544
-
545
- def __init__(self, text=None, value=None, **args):
546
- MenuConfig.__init__(self, text, value, **args)
547
- if 'openItem' not in args:
548
- self.openItem = self.tag != 'protocol_base'
549
-
550
- def addSubMenu(self, text, value=None, shortCut=None, **args):
551
- if 'icon' not in args:
552
- tag = args.get('tag', None)
553
- if tag == 'protocol_base':
554
- args['icon'] = Icon.GROUP
555
-
556
- args['shortCut'] = shortCut
557
- return MenuConfig.addSubMenu(self, text, value, **args)
558
-
559
- def __str__(self):
560
- return self.text