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
pyworkflow/gui/graph.py
DELETED
@@ -1,247 +0,0 @@
|
|
1
|
-
# **************************************************************************
|
2
|
-
# *
|
3
|
-
# * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
|
4
|
-
# *
|
5
|
-
# * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
|
6
|
-
# *
|
7
|
-
# * This program is free software; you can redistribute it and/or modify
|
8
|
-
# * it under the terms of the GNU General Public License as published by
|
9
|
-
# * the Free Software Foundation; either version 3 of the License, or
|
10
|
-
# * (at your option) any later version.
|
11
|
-
# *
|
12
|
-
# * This program is distributed in the hope that it will be useful,
|
13
|
-
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
# * GNU General Public License for more details.
|
16
|
-
# *
|
17
|
-
# * You should have received a copy of the GNU General Public License
|
18
|
-
# * along with this program; if not, write to the Free Software
|
19
|
-
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
20
|
-
# * 02111-1307 USA
|
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 module implements a simple algorithm to display a graph(mainly a tree)
|
28
|
-
level by level, using only Tkinter.
|
29
|
-
"""
|
30
|
-
|
31
|
-
|
32
|
-
# TODO: all LevelTree code is DEPRECATED...remove it after cleaning
|
33
|
-
# and include all code from graph_layout
|
34
|
-
|
35
|
-
|
36
|
-
class LevelTree(object):
|
37
|
-
""" Class to render the Graph in a Canvas. """
|
38
|
-
|
39
|
-
def __init__(self, graph):
|
40
|
-
self.DY = 65
|
41
|
-
self.DX = 15
|
42
|
-
self.FONT = "sans-serif"
|
43
|
-
self.FONTSIZE = 9
|
44
|
-
self.graph = graph
|
45
|
-
self.canvas = None
|
46
|
-
|
47
|
-
def setCanvas(self, canvas):
|
48
|
-
self.canvas = canvas
|
49
|
-
|
50
|
-
def paint(self, createNode=None, createEdge=None, maxLevel=9999, usePositions=False):
|
51
|
-
""" Paint the Graph, nodes will be positioned by levels.
|
52
|
-
Params:
|
53
|
-
canvas: the canvas object to paint the graph.
|
54
|
-
createNode: function to build the Item that represents a Node.
|
55
|
-
the Item created should have the following methods:
|
56
|
-
getDimensions: return the width and height
|
57
|
-
moveTo: change the position of the Item
|
58
|
-
createEdge: function to build an Edge connection two Nodes
|
59
|
-
usePositions: if this is True, use the nodes positions without
|
60
|
-
recomputing them.
|
61
|
-
|
62
|
-
If createNode and createEdge are None, the default ones will be used,
|
63
|
-
that requires the setCanvas method had to be called first.
|
64
|
-
"""
|
65
|
-
self.createNode = createNode or self._defaultCreateNode
|
66
|
-
self.createEdge = createEdge or self._defaultCreateEdge
|
67
|
-
self.maxLevel = maxLevel
|
68
|
-
rootNode = self.graph.getRoot()
|
69
|
-
|
70
|
-
if usePositions:
|
71
|
-
self._paintNodeWithPosition(rootNode)
|
72
|
-
self._paintEdges(rootNode)
|
73
|
-
else:
|
74
|
-
self._setLevel(rootNode, 0, None)
|
75
|
-
self._paintNodeWithChilds(rootNode, 1)
|
76
|
-
m = 9999
|
77
|
-
for left, right in rootNode.hLimits:
|
78
|
-
m = min(m, left)
|
79
|
-
self._createEdges(rootNode, -m + self.DY)
|
80
|
-
|
81
|
-
def _setLevel(self, node, level, parent):
|
82
|
-
""" Set the level of the nodes. """
|
83
|
-
node.level = level
|
84
|
-
node.parent = parent
|
85
|
-
nextLevel = level + 1
|
86
|
-
if nextLevel > self.maxLevel:
|
87
|
-
return
|
88
|
-
for child in node.getChildren():
|
89
|
-
if nextLevel > getattr(child, 'level', 0):
|
90
|
-
self._setLevel(child, nextLevel, node)
|
91
|
-
|
92
|
-
def _paintNodeWithChilds(self, node, level):
|
93
|
-
y = level * self.DY
|
94
|
-
|
95
|
-
self._paintNode(node, y)
|
96
|
-
|
97
|
-
if level > self.maxLevel:
|
98
|
-
return
|
99
|
-
|
100
|
-
childs = [c for c in node.getChildren() if c.parent is node]
|
101
|
-
n = len(childs)
|
102
|
-
|
103
|
-
if n > 0:
|
104
|
-
# width = (xmax - xmin) / n
|
105
|
-
for c in childs:
|
106
|
-
self._paintNodeWithChilds(c, level + 1)
|
107
|
-
|
108
|
-
if n > 1:
|
109
|
-
offset = 0
|
110
|
-
for i in range(n - 1):
|
111
|
-
sep = self._getChildsSeparation(childs[i], childs[i + 1])
|
112
|
-
offset += sep
|
113
|
-
c = childs[i + 1]
|
114
|
-
c.offset = offset
|
115
|
-
|
116
|
-
total = childs[0].half + offset + childs[-1].half
|
117
|
-
half = total / 2
|
118
|
-
for c in childs:
|
119
|
-
c.offset -= half - childs[0].half
|
120
|
-
|
121
|
-
else:
|
122
|
-
childs[0].offset = 0
|
123
|
-
self._getHLimits(node)
|
124
|
-
|
125
|
-
def _defaultCreateNode(self, canvas, node, y):
|
126
|
-
""" If not createNode is specified, this one will be used
|
127
|
-
by default.
|
128
|
-
"""
|
129
|
-
if canvas is None:
|
130
|
-
raise Exception("method setCanvas should be called before using _defaultCreateNode")
|
131
|
-
nodeText = node.getLabel()
|
132
|
-
textColor = 'black'
|
133
|
-
|
134
|
-
return canvas.createTextbox(nodeText, 100, y, bgColor='light blue', textColor=textColor, margin=0)
|
135
|
-
|
136
|
-
def _defaultCreateEdge(self, srcItem, dstItem):
|
137
|
-
if self.canvas is None:
|
138
|
-
raise Exception("method setCanvas should be called before using _defaultCreateEdge")
|
139
|
-
self.canvas.createEdge(srcItem, dstItem)
|
140
|
-
|
141
|
-
def _paintNode(self, node, y):
|
142
|
-
""" Paint a node of the graph.
|
143
|
-
Params:
|
144
|
-
canvas: the canvas in which to paint.
|
145
|
-
node: the node of the graph to be painted.
|
146
|
-
y: level in the tree where to paint.
|
147
|
-
Returns:
|
148
|
-
the create item in the canvas.
|
149
|
-
"""
|
150
|
-
item = self.createNode(self.canvas, node, y)
|
151
|
-
node.width, node.height = item.getDimensions()
|
152
|
-
node.half = node.width / 2
|
153
|
-
node.hLimits = [[-node.half, node.half]]
|
154
|
-
node.y = item.y
|
155
|
-
node.offset = 0
|
156
|
-
# Create link from both sides to reach
|
157
|
-
node.item = item
|
158
|
-
item.node = node
|
159
|
-
|
160
|
-
return item
|
161
|
-
|
162
|
-
def _printHLimits(self, node, msg):
|
163
|
-
print("\n=====%s========" % msg)
|
164
|
-
print(" dd: %s" % node.t.text.replace('\n', '_'))
|
165
|
-
print(" offset: %d, width: %d" % (node.offset, node.width))
|
166
|
-
print(" hlimits:")
|
167
|
-
for l, r in node.hLimits:
|
168
|
-
print(" [%d, %d]" % (l, r))
|
169
|
-
|
170
|
-
def _getHLimits(self, node):
|
171
|
-
"""
|
172
|
-
This function will traverse the tree
|
173
|
-
from node to build the left and right profiles(hLimits)
|
174
|
-
for each level of the tree
|
175
|
-
"""
|
176
|
-
node.hLimits = [[-node.half, node.half]]
|
177
|
-
childs = [c for c in node.getChildren() if c.parent is node]
|
178
|
-
for child in childs:
|
179
|
-
count = 1
|
180
|
-
if not hasattr(child, 'hLimits'):
|
181
|
-
print("node %s has no hLimits" % child.label)
|
182
|
-
raise Exception()
|
183
|
-
|
184
|
-
for l, r in child.hLimits:
|
185
|
-
l += child.offset
|
186
|
-
r += child.offset
|
187
|
-
if count < len(node.hLimits):
|
188
|
-
if l < node.hLimits[count][0]:
|
189
|
-
node.hLimits[count][0] = l
|
190
|
-
if r > node.hLimits[count][1]:
|
191
|
-
node.hLimits[count][1] = r
|
192
|
-
else:
|
193
|
-
node.hLimits.append([l, r])
|
194
|
-
count += 1
|
195
|
-
|
196
|
-
def _getChildsSeparation(self, child1, child2):
|
197
|
-
""" Calculate separation between siblings
|
198
|
-
at each height level. """
|
199
|
-
sep = 0
|
200
|
-
hL1 = child1.hLimits
|
201
|
-
hL2 = child2.hLimits
|
202
|
-
n1 = len(hL1)
|
203
|
-
n2 = len(hL2)
|
204
|
-
h = min(n1, n2)
|
205
|
-
|
206
|
-
for i in range(h):
|
207
|
-
right = hL1[i][1]
|
208
|
-
left = hL2[i][0]
|
209
|
-
if left + sep < right:
|
210
|
-
sep = right - left
|
211
|
-
|
212
|
-
return sep + self.DX
|
213
|
-
|
214
|
-
def _createEdges(self, node, x):
|
215
|
-
""" Adjust the position of the nodes
|
216
|
-
and create the edges between them.
|
217
|
-
"""
|
218
|
-
nx = x + node.offset
|
219
|
-
node.item.moveTo(nx, node.y)
|
220
|
-
|
221
|
-
if node.level == self.maxLevel:
|
222
|
-
return
|
223
|
-
|
224
|
-
for c in node.getChildren():
|
225
|
-
if c.parent is node:
|
226
|
-
self._createEdges(c, nx)
|
227
|
-
self.createEdge(node.item, c.item)
|
228
|
-
|
229
|
-
def _paintNodeWithPosition(self, node):
|
230
|
-
""" Paint nodes using its position. """
|
231
|
-
self._paintNode(node, None)
|
232
|
-
|
233
|
-
for child in node.getChildren():
|
234
|
-
# parent = None for nodes that have been not traversed
|
235
|
-
parent = getattr(child, 'parent', None)
|
236
|
-
if parent is None:
|
237
|
-
child.parent = node
|
238
|
-
self._paintNodeWithPosition(child)
|
239
|
-
|
240
|
-
def _paintEdges(self, node):
|
241
|
-
""" Paint only the edges between nodes, assuming they are
|
242
|
-
already well positioned.
|
243
|
-
"""
|
244
|
-
for child in node.getChildren():
|
245
|
-
if child.parent is node:
|
246
|
-
self._paintEdges(child)
|
247
|
-
self.createEdge(node.item, child.item)
|
pyworkflow/gui/graph_layout.py
DELETED
@@ -1,271 +0,0 @@
|
|
1
|
-
# **************************************************************************
|
2
|
-
# *
|
3
|
-
# * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
|
4
|
-
# *
|
5
|
-
# * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
|
6
|
-
# *
|
7
|
-
# * This program is free software; you can redistribute it and/or modify
|
8
|
-
# * it under the terms of the GNU General Public License as published by
|
9
|
-
# * the Free Software Foundation; either version 3 of the License, or
|
10
|
-
# * (at your option) any later version.
|
11
|
-
# *
|
12
|
-
# * This program is distributed in the hope that it will be useful,
|
13
|
-
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
# * GNU General Public License for more details.
|
16
|
-
# *
|
17
|
-
# * You should have received a copy of the GNU General Public License
|
18
|
-
# * along with this program; if not, write to the Free Software
|
19
|
-
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
20
|
-
# * 02111-1307 USA
|
21
|
-
# *
|
22
|
-
# * All comments concerning this program package may be sent to the
|
23
|
-
# * e-mail address 'scipion@cnb.csic.es'
|
24
|
-
# *
|
25
|
-
# **************************************************************************
|
26
|
-
import logging
|
27
|
-
|
28
|
-
logger = logging.getLogger(__name__)
|
29
|
-
|
30
|
-
from pyworkflow import Config, SCIPION_DEFAULT_FONT_SIZE
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
class GraphLayout(object):
|
35
|
-
""" Base class for all algorithm that implement
|
36
|
-
functions to organize a graph in a plane.
|
37
|
-
"""
|
38
|
-
def __init__(self):
|
39
|
-
super().__init__()
|
40
|
-
self.DY = 65
|
41
|
-
self.DX = 15
|
42
|
-
self._fontScaleFactor = None
|
43
|
-
|
44
|
-
def getY(self):
|
45
|
-
"""
|
46
|
-
:return: Y distance affected by the font size
|
47
|
-
|
48
|
-
"""
|
49
|
-
|
50
|
-
return self.DY*self.getFontScaleFactor()
|
51
|
-
|
52
|
-
def getFontScaleFactor(self):
|
53
|
-
"""
|
54
|
-
:return: The scale factor between default font size 10, and current one
|
55
|
-
|
56
|
-
"""
|
57
|
-
if self._fontScaleFactor is None:
|
58
|
-
|
59
|
-
self._fontScaleFactor = Config.SCIPION_FONT_SIZE/SCIPION_DEFAULT_FONT_SIZE
|
60
|
-
|
61
|
-
return self._fontScaleFactor
|
62
|
-
|
63
|
-
def draw(self, graph, **kwargs):
|
64
|
-
""" Setup the nodes position in the plane. """
|
65
|
-
pass
|
66
|
-
|
67
|
-
|
68
|
-
class LevelTreeLayout(GraphLayout):
|
69
|
-
""" Organize the nodes of the graph by levels.
|
70
|
-
It will recursively organize children and then
|
71
|
-
fit the sibling trees. """
|
72
|
-
|
73
|
-
def __init__(self, partial=False):
|
74
|
-
GraphLayout.__init__(self)
|
75
|
-
self.maxLevel = 9999
|
76
|
-
self.partial = partial
|
77
|
-
|
78
|
-
def draw(self, graph, **kwargs):
|
79
|
-
"""
|
80
|
-
Organize nodes of the graph in the plane.
|
81
|
-
Nodes should have x, y, width and height attributes.
|
82
|
-
x and y will be modified.
|
83
|
-
"""
|
84
|
-
rootNode = graph.getRoot()
|
85
|
-
|
86
|
-
# Setup empty layout for each node
|
87
|
-
for node in graph.getNodes():
|
88
|
-
node._layout = {}
|
89
|
-
|
90
|
-
visitDict = dict()
|
91
|
-
# Do some level initialization on each node
|
92
|
-
self._setLayoutLevel(rootNode, 1, visitDict)
|
93
|
-
self._computeNodeOffsets(rootNode, 1)
|
94
|
-
# Compute extreme left limit
|
95
|
-
m = 9999
|
96
|
-
for left, _ in rootNode._layout['hLimits']:
|
97
|
-
m = min(m, left)
|
98
|
-
|
99
|
-
self._applyNodeOffsets(rootNode, -m + self.getY())
|
100
|
-
|
101
|
-
# Clean temporary _layout attributes
|
102
|
-
for node in graph.getNodes():
|
103
|
-
del node._layout
|
104
|
-
|
105
|
-
def _isNewNode(self, node):
|
106
|
-
return node.x == 0 or node.y == 0 or node.isRoot()
|
107
|
-
|
108
|
-
def _setLayoutLevel(self, node, level, parent, ancestors=[]):
|
109
|
-
""" Iterate over all nodes and set _layout dict.
|
110
|
-
Also set the node level, which is defined
|
111
|
-
as the max level of a parent + 1
|
112
|
-
"""
|
113
|
-
if level > self.maxLevel:
|
114
|
-
return
|
115
|
-
|
116
|
-
layout = node._layout
|
117
|
-
|
118
|
-
if level > layout.get('level', 0):
|
119
|
-
# Calculate the y-position depending on the level
|
120
|
-
# and the delta-Y (DY)
|
121
|
-
if not self.partial or self._isNewNode(node):
|
122
|
-
node.y = level * self.getY()
|
123
|
-
layout['level'] = level
|
124
|
-
layout['parent'] = parent
|
125
|
-
if hasattr(node, 'width'):
|
126
|
-
half = node.width / 2
|
127
|
-
else:
|
128
|
-
half = 50
|
129
|
-
layout['half'] = half
|
130
|
-
layout['hLimits'] = [[-half, half]]
|
131
|
-
layout['offset'] = 0
|
132
|
-
|
133
|
-
if self.__isNodeExpanded(node):
|
134
|
-
ancestors.append(node.getName())
|
135
|
-
for child in node.getChildren():
|
136
|
-
if child.getName() in ancestors:
|
137
|
-
logger.warning("WARNING: There might be a cyclic redundancy error in this protocol: %s (%s)" %(child.getLabel(),
|
138
|
-
child.getName()))
|
139
|
-
if Config.debugOn():
|
140
|
-
print("%s: Setting layout for child %s" % ("-" * level, child), flush=True)
|
141
|
-
self._setLayoutLevel(child, level+1, node, ancestors.copy())
|
142
|
-
|
143
|
-
def __isNodeExpanded(self, node):
|
144
|
-
""" Check if the status of the node is expanded or collapsed. """
|
145
|
-
return getattr(node, 'expanded', True)
|
146
|
-
|
147
|
-
def __setNodeOffset(self, node, offset):
|
148
|
-
node._layout['offset'] = offset
|
149
|
-
|
150
|
-
def __getNodeHalf(self, node):
|
151
|
-
return node._layout['half']
|
152
|
-
|
153
|
-
def __getNodeChilds(self, node):
|
154
|
-
""" Return the node's childs that have been
|
155
|
-
visited by this node first (its 'parent')
|
156
|
-
"""
|
157
|
-
if self.__isNodeExpanded(node):
|
158
|
-
return [c for c in node.getChildren() if c._layout['parent'] is node]
|
159
|
-
else:
|
160
|
-
return [] # treat collapsed nodes as if they have no childs
|
161
|
-
|
162
|
-
def _computeNodeOffsets(self, node, level):
|
163
|
-
""" Position a parent node and its childs.
|
164
|
-
Only this sub-tree will be considered at this point.
|
165
|
-
Then it will be adjusted with node siblings.
|
166
|
-
"""
|
167
|
-
if level > self.maxLevel:
|
168
|
-
return
|
169
|
-
|
170
|
-
childs = self.__getNodeChilds(node)
|
171
|
-
n = len(childs)
|
172
|
-
|
173
|
-
if n > 0:
|
174
|
-
for c in childs:
|
175
|
-
self._computeNodeOffsets(c, level + 1)
|
176
|
-
|
177
|
-
if n > 1:
|
178
|
-
offset = 0
|
179
|
-
# Keep right limits to compute the separation between siblings
|
180
|
-
# some times it not enough to compare with the left sibling
|
181
|
-
# for some child levels of the node
|
182
|
-
rightLimits = [r for l, r in childs[0]._layout['hLimits']]
|
183
|
-
|
184
|
-
for i in range(n-1):
|
185
|
-
sep = self._getChildsSeparation(childs[i], childs[i+1], rightLimits)
|
186
|
-
offset += sep
|
187
|
-
c = childs[i+1]
|
188
|
-
self.__setNodeOffset(c, offset)
|
189
|
-
|
190
|
-
half0 = self.__getNodeHalf(childs[0])
|
191
|
-
total = half0 + offset + self.__getNodeHalf(childs[-1])
|
192
|
-
half = total / 2
|
193
|
-
for c in childs:
|
194
|
-
self.__setNodeOffset(c, c._layout['offset'] - half + half0)
|
195
|
-
else:
|
196
|
-
self.__setNodeOffset(childs[0], 0)
|
197
|
-
|
198
|
-
self._computeHLimits(node)
|
199
|
-
|
200
|
-
def _computeHLimits(self, node):
|
201
|
-
""" This function will traverse the tree
|
202
|
-
from node to build the left and right profiles(hLimits)
|
203
|
-
for each level of the tree
|
204
|
-
"""
|
205
|
-
layout = node._layout
|
206
|
-
hLimits = layout['hLimits']
|
207
|
-
|
208
|
-
childs = self.__getNodeChilds(node)
|
209
|
-
|
210
|
-
for child in childs:
|
211
|
-
count = 1
|
212
|
-
layout = child._layout
|
213
|
-
for l, r in layout['hLimits']:
|
214
|
-
l += layout['offset']
|
215
|
-
r += layout['offset']
|
216
|
-
|
217
|
-
if count < len(hLimits):
|
218
|
-
if l < hLimits[count][0]:
|
219
|
-
hLimits[count][0] = l
|
220
|
-
if r > hLimits[count][1]:
|
221
|
-
hLimits[count][1] = r
|
222
|
-
else:
|
223
|
-
hLimits.append([l, r])
|
224
|
-
count += 1
|
225
|
-
|
226
|
-
def _getChildsSeparation(self, child1, child2, rightLimits):
|
227
|
-
"""Calculate separation between siblings
|
228
|
-
at each height level"""
|
229
|
-
sep = 0
|
230
|
-
hL2 = child2._layout['hLimits']
|
231
|
-
n1 = len(rightLimits)
|
232
|
-
n2 = len(hL2)
|
233
|
-
h = min(n1, n2)
|
234
|
-
|
235
|
-
for i in range(h):
|
236
|
-
right = rightLimits[i]
|
237
|
-
left = hL2[i][0]
|
238
|
-
if left + sep < right:
|
239
|
-
sep = right - left
|
240
|
-
rightLimits[i] = hL2[i][1]
|
241
|
-
|
242
|
-
if n1 > n2:
|
243
|
-
# If there are more levels in the rightLimits
|
244
|
-
# updated the last ones like if they belong
|
245
|
-
# to next sibling is is now (sep + self.DX) away
|
246
|
-
for i in range(h, n1):
|
247
|
-
rightLimits[i] -= sep + self.DX
|
248
|
-
else:
|
249
|
-
# If the current right sibling has more levels
|
250
|
-
# just add them to the current rightLimits
|
251
|
-
for i in range(h, n2):
|
252
|
-
rightLimits.append(hL2[i][1])
|
253
|
-
|
254
|
-
return sep + self.DX
|
255
|
-
|
256
|
-
def _applyNodeOffsets(self, node, x):
|
257
|
-
""" Adjust the x-position of the nodes by applying the offsets.
|
258
|
-
"""
|
259
|
-
if node._layout['level'] == self.maxLevel:
|
260
|
-
return
|
261
|
-
|
262
|
-
layout = node._layout
|
263
|
-
|
264
|
-
if not self.partial or self._isNewNode(node):
|
265
|
-
node.x = x + layout['offset']
|
266
|
-
|
267
|
-
childs = self.__getNodeChilds(node)
|
268
|
-
|
269
|
-
for child in childs:
|
270
|
-
self._applyNodeOffsets(child, node.x)
|
271
|
-
|