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.
- pyworkflow/config.py +131 -67
- pyworkflow/constants.py +12 -2
- pyworkflow/object.py +3 -2
- pyworkflow/plugin.py +93 -44
- pyworkflow/project/scripts/fix_links.py +4 -1
- pyworkflow/resources/showj/arrowDown.png +0 -0
- pyworkflow/resources/showj/arrowUp.png +0 -0
- pyworkflow/resources/showj/background_section.png +0 -0
- pyworkflow/resources/showj/colRowModeOff.png +0 -0
- pyworkflow/resources/showj/colRowModeOn.png +0 -0
- pyworkflow/resources/showj/delete.png +0 -0
- pyworkflow/resources/showj/doc_icon.png +0 -0
- pyworkflow/resources/showj/download_icon.png +0 -0
- pyworkflow/resources/showj/enabled_gallery.png +0 -0
- pyworkflow/resources/showj/galleryViewOff.png +0 -0
- pyworkflow/resources/showj/galleryViewOn.png +0 -0
- pyworkflow/resources/showj/goto.png +0 -0
- pyworkflow/resources/showj/menu.png +0 -0
- pyworkflow/resources/showj/separator.png +0 -0
- pyworkflow/resources/showj/tableViewOff.png +0 -0
- pyworkflow/resources/showj/tableViewOn.png +0 -0
- pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- pyworkflow/resources/showj/volumeOff.png +0 -0
- pyworkflow/resources/showj/volumeOn.png +0 -0
- pyworkflow/viewer.py +23 -1
- pyworkflowtests/objects.py +2 -2
- pyworkflowtests/protocols.py +1 -3
- {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +21 -25
- scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
- {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
- scipion_pyworkflow-3.11.0.dist-info/entry_points.txt +2 -0
- pyworkflow/apps/__init__.py +0 -29
- pyworkflow/apps/pw_manager.py +0 -37
- pyworkflow/apps/pw_plot.py +0 -51
- pyworkflow/apps/pw_project.py +0 -113
- pyworkflow/apps/pw_protocol_list.py +0 -143
- pyworkflow/apps/pw_protocol_run.py +0 -51
- pyworkflow/apps/pw_run_tests.py +0 -267
- pyworkflow/apps/pw_schedule_run.py +0 -322
- pyworkflow/apps/pw_sleep.py +0 -37
- pyworkflow/apps/pw_sync_data.py +0 -439
- pyworkflow/apps/pw_viewer.py +0 -78
- pyworkflow/gui/__init__.py +0 -36
- pyworkflow/gui/browser.py +0 -726
- pyworkflow/gui/canvas.py +0 -1190
- pyworkflow/gui/dialog.py +0 -977
- pyworkflow/gui/form.py +0 -2637
- pyworkflow/gui/graph.py +0 -247
- pyworkflow/gui/graph_layout.py +0 -271
- pyworkflow/gui/gui.py +0 -566
- pyworkflow/gui/matplotlib_image.py +0 -233
- pyworkflow/gui/plotter.py +0 -247
- pyworkflow/gui/project/__init__.py +0 -25
- pyworkflow/gui/project/base.py +0 -192
- pyworkflow/gui/project/constants.py +0 -139
- pyworkflow/gui/project/labels.py +0 -205
- pyworkflow/gui/project/project.py +0 -492
- pyworkflow/gui/project/searchprotocol.py +0 -154
- pyworkflow/gui/project/searchrun.py +0 -181
- pyworkflow/gui/project/steps.py +0 -171
- pyworkflow/gui/project/utils.py +0 -332
- pyworkflow/gui/project/variables.py +0 -179
- pyworkflow/gui/project/viewdata.py +0 -472
- pyworkflow/gui/project/viewprojects.py +0 -510
- pyworkflow/gui/project/viewprotocols.py +0 -2093
- pyworkflow/gui/project/viewprotocols_extra.py +0 -560
- pyworkflow/gui/text.py +0 -771
- pyworkflow/gui/tooltip.py +0 -185
- pyworkflow/gui/tree.py +0 -684
- pyworkflow/gui/widgets.py +0 -307
- pyworkflow/mapper/__init__.py +0 -26
- pyworkflow/mapper/mapper.py +0 -222
- pyworkflow/mapper/sqlite.py +0 -1578
- pyworkflow/mapper/sqlite_db.py +0 -145
- pyworkflow/project/__init__.py +0 -31
- pyworkflow/project/config.py +0 -454
- pyworkflow/project/manager.py +0 -180
- pyworkflow/project/project.py +0 -2010
- pyworkflow/protocol/__init__.py +0 -38
- pyworkflow/protocol/bibtex.py +0 -48
- pyworkflow/protocol/constants.py +0 -87
- pyworkflow/protocol/executor.py +0 -455
- pyworkflow/protocol/hosts.py +0 -313
- pyworkflow/protocol/launch.py +0 -270
- pyworkflow/protocol/package.py +0 -42
- pyworkflow/protocol/params.py +0 -741
- pyworkflow/protocol/protocol.py +0 -2582
- pyworkflow/tests/__init__.py +0 -29
- pyworkflow/tests/test_utils.py +0 -25
- pyworkflow/tests/tests.py +0 -341
- pyworkflow/utils/__init__.py +0 -38
- pyworkflow/utils/dataset.py +0 -414
- pyworkflow/utils/echo.py +0 -104
- pyworkflow/utils/graph.py +0 -169
- pyworkflow/utils/log.py +0 -284
- pyworkflow/utils/path.py +0 -528
- pyworkflow/utils/process.py +0 -132
- pyworkflow/utils/profiler.py +0 -92
- pyworkflow/utils/progressbar.py +0 -154
- pyworkflow/utils/properties.py +0 -631
- pyworkflow/utils/reflection.py +0 -129
- pyworkflow/utils/utils.py +0 -879
- pyworkflow/utils/which.py +0 -229
- pyworkflow/webservices/__init__.py +0 -8
- pyworkflow/webservices/config.py +0 -11
- pyworkflow/webservices/notifier.py +0 -162
- pyworkflow/webservices/repository.py +0 -59
- pyworkflow/webservices/workflowhub.py +0 -74
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +0 -72
- pyworkflowtests/tests/test_domain.py +0 -45
- pyworkflowtests/tests/test_logs.py +0 -74
- pyworkflowtests/tests/test_mappers.py +0 -392
- pyworkflowtests/tests/test_object.py +0 -507
- pyworkflowtests/tests/test_project.py +0 -42
- pyworkflowtests/tests/test_protocol_execution.py +0 -135
- pyworkflowtests/tests/test_protocol_export.py +0 -78
- pyworkflowtests/tests/test_protocol_output.py +0 -158
- pyworkflowtests/tests/test_streaming.py +0 -47
- pyworkflowtests/tests/test_utils.py +0 -210
- scipion_pyworkflow-3.10.5.dist-info/RECORD +0 -140
- scipion_pyworkflow-3.10.5.dist-info/dependency_links.txt +0 -1
- scipion_pyworkflow-3.10.5.dist-info/entry_points.txt +0 -5
- {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info/licenses}/LICENSE.txt +0 -0
- {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
|