scipion-pyworkflow 3.10.6__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 +2 -1
- pyworkflow/plugin.py +93 -44
- 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/protocols.py +1 -3
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +13 -27
- scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
- 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 -559
- 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 -1581
- 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 -2007
- pyworkflow/protocol/__init__.py +0 -38
- pyworkflow/protocol/bibtex.py +0 -48
- pyworkflow/protocol/constants.py +0 -87
- pyworkflow/protocol/executor.py +0 -471
- pyworkflow/protocol/hosts.py +0 -314
- pyworkflow/protocol/launch.py +0 -270
- pyworkflow/protocol/package.py +0 -42
- pyworkflow/protocol/params.py +0 -741
- pyworkflow/protocol/protocol.py +0 -2641
- 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 -153
- 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 -142
- 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.6.dist-info/RECORD +0 -140
- scipion_pyworkflow-3.10.6.dist-info/dependency_links.txt +0 -1
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/licenses/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/top_level.txt +0 -0
pyworkflow/gui/tree.py
DELETED
@@ -1,684 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# **************************************************************************
|
3
|
-
# *
|
4
|
-
# * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
|
5
|
-
# *
|
6
|
-
# * 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, write to the Free Software
|
20
|
-
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
21
|
-
# * 02111-1307 USA
|
22
|
-
# *
|
23
|
-
# * All comments concerning this program package may be sent to the
|
24
|
-
# * e-mail address 'scipion@cnb.csic.es'
|
25
|
-
# *
|
26
|
-
# **************************************************************************
|
27
|
-
|
28
|
-
|
29
|
-
import os
|
30
|
-
import tkinter as tk
|
31
|
-
import tkinter.ttk as ttk
|
32
|
-
|
33
|
-
from pyworkflow.mapper import SqliteMapper
|
34
|
-
from pyworkflow.utils import prettyDelta
|
35
|
-
from . import gui
|
36
|
-
from .widgets import Scrollable
|
37
|
-
|
38
|
-
FIRST_TREE_COLUMN = '#0'
|
39
|
-
|
40
|
-
|
41
|
-
class Tree(ttk.Treeview, Scrollable):
|
42
|
-
""" This widget acts as a wrapper around the ttk.Treeview"""
|
43
|
-
_images = {}
|
44
|
-
|
45
|
-
def __init__(self, master, frame=True, **opts):
|
46
|
-
"""Create a new Tree, if frame=True, a container
|
47
|
-
frame will be created and an scrollbar will be added"""
|
48
|
-
Scrollable.__init__(self, master, ttk.Treeview, frame, **opts)
|
49
|
-
|
50
|
-
def getImage(self, img):
|
51
|
-
return gui.getImage(img)
|
52
|
-
|
53
|
-
def getFirst(self):
|
54
|
-
""" Return first selected item or None if selection empty"""
|
55
|
-
selection = self.selection()
|
56
|
-
if len(selection):
|
57
|
-
return selection[0]
|
58
|
-
return None
|
59
|
-
|
60
|
-
def _moveSelection(self, moveFunc):
|
61
|
-
item = self.selection_first()
|
62
|
-
if item:
|
63
|
-
item = moveFunc(item)
|
64
|
-
if item != '':
|
65
|
-
self.selection_set(item)
|
66
|
-
|
67
|
-
def moveSelectionUp(self, e=None):
|
68
|
-
""" change selection to previous item """
|
69
|
-
self._moveSelection(self.prev)
|
70
|
-
|
71
|
-
def moveSelectionDown(self, e=None):
|
72
|
-
""" change selection to to next item """
|
73
|
-
self._moveSelection(self.next)
|
74
|
-
|
75
|
-
def moveItemUp(self, e=None):
|
76
|
-
"""if selected item is not the first move up one position"""
|
77
|
-
item = self.selection_first()
|
78
|
-
if item:
|
79
|
-
index = self.index(item)
|
80
|
-
if index > 0:
|
81
|
-
self.move(item, '', index - 1)
|
82
|
-
|
83
|
-
def moveItemDown(self, e=None):
|
84
|
-
"""if selected item is not the first move up one position"""
|
85
|
-
item = self.selection_first()
|
86
|
-
if item:
|
87
|
-
index = self.index(item)
|
88
|
-
if self.next(item) != '':
|
89
|
-
self.move(item, '', index + 1)
|
90
|
-
|
91
|
-
def clear(self):
|
92
|
-
""" remove all items """
|
93
|
-
childs = self.get_children('')
|
94
|
-
for c in childs:
|
95
|
-
self.delete(c)
|
96
|
-
|
97
|
-
def selectChildByIndex(self, index):
|
98
|
-
""" Select the item at the position index """
|
99
|
-
child = self.get_children('')[index]
|
100
|
-
self.selection_set(child)
|
101
|
-
|
102
|
-
def selectChild(self, child):
|
103
|
-
self.selection_set(child)
|
104
|
-
|
105
|
-
def search(self, initial, fromSelected=True):
|
106
|
-
""" Search the first item starting with "start"
|
107
|
-
Implemented for Flat tree like FileBrowser...TODO: consider
|
108
|
-
a proper tree with branches and leaves..
|
109
|
-
|
110
|
-
Parameters
|
111
|
-
----------
|
112
|
-
initial: String - text to look for in the items. Usually the first initial letter
|
113
|
-
fromSelected : Boolean, start looking from the selected item"""
|
114
|
-
|
115
|
-
# Validate search string, do not allow empty chars
|
116
|
-
if len(initial) == 0:
|
117
|
-
return False
|
118
|
-
|
119
|
-
# Get all
|
120
|
-
children = self.get_children('')
|
121
|
-
|
122
|
-
# Get the selected item
|
123
|
-
searchAfter = self.getFirst() if fromSelected else None
|
124
|
-
|
125
|
-
# Loop ...
|
126
|
-
for child in children:
|
127
|
-
|
128
|
-
text = self.item(child, 'text')
|
129
|
-
|
130
|
-
if searchAfter is not None and searchAfter == text:
|
131
|
-
searchAfter = None
|
132
|
-
continue
|
133
|
-
|
134
|
-
if searchAfter is None:
|
135
|
-
|
136
|
-
# Do a lower case search
|
137
|
-
text = text.lower()
|
138
|
-
|
139
|
-
if text.startswith(initial.lower()):
|
140
|
-
|
141
|
-
# Enclose text in "" due to bug
|
142
|
-
# https://stackoverflow.com/questions/10691257/ttk-treeview-selection-set-cant-accept-spaces
|
143
|
-
searchText = '"' + child + '"'
|
144
|
-
self.focus(child)
|
145
|
-
self.selection_set(searchText)
|
146
|
-
return True
|
147
|
-
else:
|
148
|
-
continue
|
149
|
-
|
150
|
-
# If we started from a selected item...start again without selection
|
151
|
-
if fromSelected:
|
152
|
-
return self.search(initial, False)
|
153
|
-
|
154
|
-
# From: https://stackoverflow.com/questions/1966929/tk-treeview-column-sort
|
155
|
-
def sortByColumn(self, col, reverse, casting=str):
|
156
|
-
"""
|
157
|
-
Function to sort a treeview
|
158
|
-
:param self: treview
|
159
|
-
:param col: column to apply the sorting on
|
160
|
-
:param reverse: sorting direction
|
161
|
-
:param casting: optional - casting operation to apply on the column value: str is default. int, float are other options
|
162
|
-
:return:
|
163
|
-
"""
|
164
|
-
l = [(casting(self.set(k, col)), k) for k in self.get_children('')]
|
165
|
-
l.sort(reverse=reverse)
|
166
|
-
|
167
|
-
# rearrange items in sorted positions
|
168
|
-
for index, (val, k) in enumerate(l):
|
169
|
-
self.move(k, '', index)
|
170
|
-
|
171
|
-
# reverse sort next time
|
172
|
-
self.heading(col, command=lambda: self.sortByColumn(col, not reverse, casting))
|
173
|
-
|
174
|
-
|
175
|
-
class TreeProvider:
|
176
|
-
""" Class class will serve to separate the logic of feed data
|
177
|
-
from the graphical Tree build. Subclasses should implement
|
178
|
-
the abstract methods """
|
179
|
-
|
180
|
-
def __init__(self, sortingColumnName=None, sortingAscending=True):
|
181
|
-
self._sortingColumnName = sortingColumnName
|
182
|
-
self._sortingAscending = sortingAscending
|
183
|
-
self._sortEnabled = (sortingColumnName is not None)
|
184
|
-
|
185
|
-
def getColumns(self):
|
186
|
-
"""Return a list of tuples (c, w) where:
|
187
|
-
c: is the column name and index
|
188
|
-
w: is the column width
|
189
|
-
"""
|
190
|
-
pass
|
191
|
-
|
192
|
-
def getObjects(self):
|
193
|
-
"""Return the objects that will be inserted in the Tree"""
|
194
|
-
pass
|
195
|
-
|
196
|
-
def getObjectInfo(self, obj):
|
197
|
-
""" This function will be called by the Tree with each
|
198
|
-
object that will be inserted. A dictionary should be
|
199
|
-
returned with the possible following entries:
|
200
|
-
'key': the key value to insert in the Tree
|
201
|
-
'text': text of the object to be displayed
|
202
|
-
(if not passed the 'key' will be used)
|
203
|
-
'image': image path to be displayed as icon (optional)
|
204
|
-
'parent': the object's parent in which insert this object (optional)
|
205
|
-
'tags': list of tags names (optional)
|
206
|
-
"""
|
207
|
-
pass
|
208
|
-
|
209
|
-
def getObjectPreview(self, obj):
|
210
|
-
""" Should return a tuple (img, desc),
|
211
|
-
where img is the preview image and
|
212
|
-
desc the description string.
|
213
|
-
"""
|
214
|
-
return None, None
|
215
|
-
|
216
|
-
def getObjectActions(self, obj):
|
217
|
-
""" Return a list of tuples (key, action)
|
218
|
-
were keys are the string
|
219
|
-
options that will be display in the context menu
|
220
|
-
and the actions are the functions to call when
|
221
|
-
the specific action is selected.
|
222
|
-
The first action in the list will be taken
|
223
|
-
as the default one when the element is double-clicked.
|
224
|
-
"""
|
225
|
-
return []
|
226
|
-
|
227
|
-
def configureTags(self, tree):
|
228
|
-
"""
|
229
|
-
Configure the available tags in the tree that
|
230
|
-
will be setup later for each item in the tree.
|
231
|
-
Returns
|
232
|
-
-------
|
233
|
-
Nothing
|
234
|
-
|
235
|
-
Adds tags to the tree for customizing
|
236
|
-
"""
|
237
|
-
pass
|
238
|
-
|
239
|
-
def setSortingParams(self, columnName, ascending):
|
240
|
-
"""
|
241
|
-
Column name to sort by it and sorting direction
|
242
|
-
Parameters
|
243
|
-
----------
|
244
|
-
columnName: Name of the column
|
245
|
-
ascending: If true sorting will be ascending.
|
246
|
-
|
247
|
-
Returns
|
248
|
-
-------
|
249
|
-
Nothing
|
250
|
-
"""
|
251
|
-
self._sortingColumnName = columnName
|
252
|
-
self._sortingAscending = ascending
|
253
|
-
|
254
|
-
def getSortingColumnName(self):
|
255
|
-
return self._sortingColumnName
|
256
|
-
|
257
|
-
def isSortingAscending(self):
|
258
|
-
return self._sortingAscending
|
259
|
-
|
260
|
-
def sortEnabled(self):
|
261
|
-
# return self._sortingColumnName is not None
|
262
|
-
return self._sortEnabled
|
263
|
-
|
264
|
-
|
265
|
-
class BoundTree(Tree):
|
266
|
-
""" This class is base on Tree but fetch the
|
267
|
-
items from a TreeProvider, which provides columns
|
268
|
-
values for each item and items info to insert into the Tree """
|
269
|
-
|
270
|
-
def __init__(self, master, provider, frame=True, **opts):
|
271
|
-
"""Create a new Tree, if frame=True, a container
|
272
|
-
frame will be created and a scrollbar will be added"""
|
273
|
-
# Get columns to display and width
|
274
|
-
cols = provider.getColumns()
|
275
|
-
colsTuple = tuple([c[0] for c in cols[1:]])
|
276
|
-
Tree.__init__(self, master, frame, columns=colsTuple, **opts)
|
277
|
-
# Set the special case of first tree column
|
278
|
-
self.heading(FIRST_TREE_COLUMN, text=cols[0][0],
|
279
|
-
command=lambda: self.sortTree(FIRST_TREE_COLUMN, cols[0][0]))
|
280
|
-
self.column(FIRST_TREE_COLUMN, width=cols[0][1])
|
281
|
-
# Set other columns
|
282
|
-
for c, w in cols[1:]:
|
283
|
-
self.column(c, width=w)
|
284
|
-
self.heading(c, text=c, command=lambda _c=c: self.sortTree(_c, _c))
|
285
|
-
|
286
|
-
self.grid(row=0, column=0, sticky='news')
|
287
|
-
|
288
|
-
self.menu = tk.Menu(self, tearoff=0)
|
289
|
-
|
290
|
-
self.setProvider(provider)
|
291
|
-
|
292
|
-
self.bind("<Button-3>", self._onRightClick)
|
293
|
-
# Hide the right-click menu
|
294
|
-
self.bind('<FocusOut>', self._unpostMenu)
|
295
|
-
self.bind("<Key>", self._onKeyPress)
|
296
|
-
self.bind('<Button-1>', self._onClick)
|
297
|
-
self.bind('<Double-1>', self._onDoubleClick)
|
298
|
-
self.bind('<<TreeviewSelect>>', self._onSelect)
|
299
|
-
|
300
|
-
def setProvider(self, provider):
|
301
|
-
""" Set new provider and updated items. """
|
302
|
-
self.provider = provider
|
303
|
-
self.update()
|
304
|
-
|
305
|
-
def _onClick(self, e=None):
|
306
|
-
self._unpostMenu()
|
307
|
-
if hasattr(self, 'itemOnClick'):
|
308
|
-
self.itemOnClick(e)
|
309
|
-
|
310
|
-
def _onKeyPress(self, e=None):
|
311
|
-
self._unpostMenu()
|
312
|
-
|
313
|
-
if hasattr(self, 'itemKeyPressed'):
|
314
|
-
selected = self.getFirst()
|
315
|
-
if selected:
|
316
|
-
obj = self._objDict[selected]
|
317
|
-
self.itemKeyPressed(obj, e)
|
318
|
-
|
319
|
-
def _unpostMenu(self, e=None):
|
320
|
-
|
321
|
-
self.menu.unpost()
|
322
|
-
|
323
|
-
def _onSelect(self, e=None):
|
324
|
-
if hasattr(self, 'itemClick'):
|
325
|
-
selected = self.getFirst()
|
326
|
-
if selected:
|
327
|
-
obj = self._objDict[selected]
|
328
|
-
self.itemClick(obj)
|
329
|
-
|
330
|
-
def _onDoubleClick(self, e=None):
|
331
|
-
selected = self.getFirst()
|
332
|
-
if selected:
|
333
|
-
obj = self._objDict[selected]
|
334
|
-
if hasattr(self, 'itemDoubleClick'):
|
335
|
-
self.itemDoubleClick(obj)
|
336
|
-
else: # If not callback, use default action
|
337
|
-
actions = self.provider.getObjectActions(obj)
|
338
|
-
if len(actions):
|
339
|
-
# actions[0] = first Action, [1] = the action callback
|
340
|
-
actions[0][1]()
|
341
|
-
|
342
|
-
def _onRightClick(self, e=None):
|
343
|
-
item = self.identify('item', e.x, e.y)
|
344
|
-
unpost = True
|
345
|
-
if len(item):
|
346
|
-
self.selection_set(item)
|
347
|
-
obj = self._objDict[item]
|
348
|
-
actions = self.provider.getObjectActions(obj)
|
349
|
-
if len(actions):
|
350
|
-
self.menu.delete(0, tk.END)
|
351
|
-
for a in actions:
|
352
|
-
if a is None:
|
353
|
-
self.menu.add_separator()
|
354
|
-
else:
|
355
|
-
img = ''
|
356
|
-
if len(a) > 2: # image for the action
|
357
|
-
img = self.getImage(a[2])
|
358
|
-
self.menu.add_command(label=a[0], command=a[1],
|
359
|
-
image=img, compound=tk.LEFT)
|
360
|
-
self.menu.post(e.x_root, e.y_root)
|
361
|
-
unpost = False
|
362
|
-
if unpost:
|
363
|
-
self._unpostMenu()
|
364
|
-
|
365
|
-
def update(self):
|
366
|
-
self.clear()
|
367
|
-
self.provider.configureTags(self)
|
368
|
-
self._objDict = {} # Store the mapping between Tree ids and objects
|
369
|
-
self._objects = self.provider.getObjects()
|
370
|
-
|
371
|
-
for obj in self._objects:
|
372
|
-
# If the object is a pointer that has a null value do not show
|
373
|
-
# if ((not obj.isPointer()) or (obj.isPointer() and obj.get() is not None)):
|
374
|
-
objDict = self.provider.getObjectInfo(obj)
|
375
|
-
if objDict is not None:
|
376
|
-
key = objDict.get('key')
|
377
|
-
text = objDict.get('text', key)
|
378
|
-
parent = objDict.get('parent', None)
|
379
|
-
|
380
|
-
if parent is None:
|
381
|
-
parentId = ''
|
382
|
-
else:
|
383
|
-
if hasattr(parent, '_treeId'): # This should happens always
|
384
|
-
parentId = parent._treeId # Previously set
|
385
|
-
else:
|
386
|
-
parentId = ''
|
387
|
-
text += '---> Error: parent not Inserted'
|
388
|
-
image = objDict.get('image', '')
|
389
|
-
if len(image):
|
390
|
-
image = self.getImage(image)
|
391
|
-
if image is None:
|
392
|
-
image = ''
|
393
|
-
values = objDict.get('values', ())
|
394
|
-
|
395
|
-
tags = objDict.get('tags', ())
|
396
|
-
|
397
|
-
try:
|
398
|
-
obj._treeId = self.insert(parentId, 'end', key,
|
399
|
-
text=text, image=image, values=values, tags=tags)
|
400
|
-
self._objDict[obj._treeId] = obj
|
401
|
-
|
402
|
-
if objDict.get('open', False):
|
403
|
-
self.itemConfig(obj, open=True)
|
404
|
-
|
405
|
-
if objDict.get('selected', False):
|
406
|
-
self.selectChild(obj._treeId)
|
407
|
-
|
408
|
-
except Exception as ex:
|
409
|
-
print("error: ", ex)
|
410
|
-
if hasattr(obj, "getObjId") and obj.getObjId():
|
411
|
-
print("error object with id=%d (%s) is duplicated!!!"
|
412
|
-
% (obj.getObjId(), str(obj)))
|
413
|
-
else:
|
414
|
-
print("error, object %s does not have an id. This could"
|
415
|
-
" be due to the load of old project that does not"
|
416
|
-
" have recently added attributes "
|
417
|
-
"(e.g.:datastreaming)" % str(obj))
|
418
|
-
|
419
|
-
def sortTree(self, heading, column):
|
420
|
-
|
421
|
-
if not self.provider.sortEnabled():
|
422
|
-
return
|
423
|
-
|
424
|
-
# Calculate the sorting direction. default to true
|
425
|
-
ascending = True
|
426
|
-
|
427
|
-
# Current sorted column
|
428
|
-
currentSortedColumn = self.provider.getSortingColumnName()
|
429
|
-
|
430
|
-
# If its the same column
|
431
|
-
if column == currentSortedColumn:
|
432
|
-
ascending = not self.provider.isSortingAscending()
|
433
|
-
else:
|
434
|
-
# Remove previous arrow
|
435
|
-
previousHeading = self.getColumnKeyByColumnName(currentSortedColumn)
|
436
|
-
self.heading(previousHeading, text=currentSortedColumn)
|
437
|
-
|
438
|
-
# Visualize column sorted in the header
|
439
|
-
if ascending:
|
440
|
-
self.heading(heading, text=column + ' ▲')
|
441
|
-
else:
|
442
|
-
self.heading(heading, text=column + ' ▼')
|
443
|
-
|
444
|
-
self.provider.setSortingParams(column, ascending)
|
445
|
-
|
446
|
-
self.update()
|
447
|
-
|
448
|
-
def getColumnKeyByColumnName(self, columnName):
|
449
|
-
try:
|
450
|
-
self.column(columnName)
|
451
|
-
return columnName
|
452
|
-
except Exception as e:
|
453
|
-
return FIRST_TREE_COLUMN
|
454
|
-
|
455
|
-
def itemConfig(self, obj, **args):
|
456
|
-
""" Configure inserted items. """
|
457
|
-
self.item(obj._treeId, **args)
|
458
|
-
|
459
|
-
def iterSelectedObjects(self):
|
460
|
-
for treeId in self.selection():
|
461
|
-
yield self.getObjectFromId(treeId)
|
462
|
-
|
463
|
-
def getSelectedObjects(self):
|
464
|
-
return [obj for obj in self.iterSelectedObjects()]
|
465
|
-
|
466
|
-
def getObjectFromId(self, treeId):
|
467
|
-
""" Return the corresponding object from a given Tree item id. """
|
468
|
-
return self._objDict[treeId]
|
469
|
-
|
470
|
-
|
471
|
-
class ObjectTreeProvider(TreeProvider):
|
472
|
-
""" Populate Tree from Objects. """
|
473
|
-
|
474
|
-
def __init__(self, objList=None):
|
475
|
-
TreeProvider.__init__(self)
|
476
|
-
self.objList = objList
|
477
|
-
self.getColumns = lambda: [('Object', 300), ('Id', 70), ('Class', 150)]
|
478
|
-
self._parentDict = {}
|
479
|
-
|
480
|
-
def getObjectInfo(self, obj):
|
481
|
-
# if obj.isPointer() and not obj.hasValue():
|
482
|
-
# return None
|
483
|
-
cls = obj.getClassName()
|
484
|
-
if obj.getName() is None:
|
485
|
-
t = cls
|
486
|
-
else:
|
487
|
-
t = obj.getName().split('.')[-1]
|
488
|
-
if t.startswith('__item__'):
|
489
|
-
t = "%s [%s]" % (cls, t.replace('__item__', ''))
|
490
|
-
|
491
|
-
value = obj.get()
|
492
|
-
if value is None:
|
493
|
-
if obj.isPointer():
|
494
|
-
t += " = %s" % str(obj.getObjValue())
|
495
|
-
else:
|
496
|
-
t += " = None"
|
497
|
-
else:
|
498
|
-
t += " = %s" % str(obj)
|
499
|
-
|
500
|
-
info = {'key': obj.getObjId(),
|
501
|
-
'parent': self._parentDict.get(obj.getObjId(), None),
|
502
|
-
'text': t, 'values': (obj.strId(), cls)}
|
503
|
-
# This image step.gif is missing, I guess we are not showing Scalars
|
504
|
-
# if issubclass(obj.__class__, Scalar):
|
505
|
-
# info['image'] = 'step.gif'
|
506
|
-
|
507
|
-
return info
|
508
|
-
|
509
|
-
def getObjectPreview(self, obj):
|
510
|
-
return None, None
|
511
|
-
|
512
|
-
def getObjectActions(self, obj):
|
513
|
-
return []
|
514
|
-
|
515
|
-
def _getObjectList(self):
|
516
|
-
"""Retrieve the object list"""
|
517
|
-
return self.objList
|
518
|
-
|
519
|
-
def getObjects(self):
|
520
|
-
objList = self._getObjectList()
|
521
|
-
self._parentDict = {}
|
522
|
-
childs = []
|
523
|
-
for obj in objList:
|
524
|
-
childs += self._getChilds(obj)
|
525
|
-
objList += childs
|
526
|
-
return objList
|
527
|
-
|
528
|
-
def _getChilds(self, obj):
|
529
|
-
childs = []
|
530
|
-
grandchilds = []
|
531
|
-
|
532
|
-
for a, v in obj.getAttributesToStore():
|
533
|
-
childs.append(v)
|
534
|
-
self._parentDict[v.getObjId()] = obj
|
535
|
-
grandchilds += self._getChilds(v)
|
536
|
-
childs += grandchilds
|
537
|
-
return childs
|
538
|
-
|
539
|
-
|
540
|
-
class DbTreeProvider(ObjectTreeProvider):
|
541
|
-
"""Retrieve the elements from the database"""
|
542
|
-
|
543
|
-
def __init__(self, dbName, classesDict):
|
544
|
-
ObjectTreeProvider.__init__(self)
|
545
|
-
self.mapper = SqliteMapper(dbName, classesDict)
|
546
|
-
|
547
|
-
def _getObjectList(self):
|
548
|
-
return self.mapper.selectAll()
|
549
|
-
|
550
|
-
|
551
|
-
class ProjectRunsTreeProvider(TreeProvider):
|
552
|
-
""" Provide run list from a project
|
553
|
-
to populate a tree.
|
554
|
-
"""
|
555
|
-
ID_COLUMN = 'Id'
|
556
|
-
RUN_COLUMN = 'Run'
|
557
|
-
STATE_COLUMN = 'State'
|
558
|
-
TIME_COLUMN = 'Time'
|
559
|
-
|
560
|
-
def __init__(self, project, **kwargs):
|
561
|
-
TreeProvider.__init__(self, sortingColumnName=ProjectRunsTreeProvider.ID_COLUMN)
|
562
|
-
self.project = project
|
563
|
-
self._objDict = {}
|
564
|
-
self._refresh = True
|
565
|
-
self._checkPids = False
|
566
|
-
|
567
|
-
def setRefresh(self, value):
|
568
|
-
self._refresh = value
|
569
|
-
|
570
|
-
def getObjects(self):
|
571
|
-
runs = self.project.getRuns(refresh=self._refresh,
|
572
|
-
checkPids=self._checkPids)
|
573
|
-
|
574
|
-
# Sort objects
|
575
|
-
runs.sort(key=self.runsKey, reverse=not self.isSortingAscending())
|
576
|
-
|
577
|
-
return runs
|
578
|
-
|
579
|
-
def runsKey(self, run):
|
580
|
-
sortDict = {ProjectRunsTreeProvider.ID_COLUMN: 'getObjId',
|
581
|
-
ProjectRunsTreeProvider.TIME_COLUMN: 'getElapsedTime',
|
582
|
-
ProjectRunsTreeProvider.RUN_COLUMN: 'getRunName',
|
583
|
-
ProjectRunsTreeProvider.STATE_COLUMN: 'getStatusMessage'}
|
584
|
-
return getattr(run, sortDict.get(self._sortingColumnName))()
|
585
|
-
|
586
|
-
def getColumns(self):
|
587
|
-
return [(ProjectRunsTreeProvider.ID_COLUMN, 5),
|
588
|
-
(ProjectRunsTreeProvider.RUN_COLUMN, 300),
|
589
|
-
(ProjectRunsTreeProvider.STATE_COLUMN, 50),
|
590
|
-
(ProjectRunsTreeProvider.TIME_COLUMN, 50)]
|
591
|
-
|
592
|
-
def getObjectInfo(self, obj):
|
593
|
-
objId = obj.getObjId()
|
594
|
-
self._objDict[objId] = obj
|
595
|
-
info = {'key': objId, 'text': objId,
|
596
|
-
'values': (obj.getRunName(), obj.getStatusMessage(),
|
597
|
-
prettyDelta(obj.getElapsedTime()))
|
598
|
-
}
|
599
|
-
objPid = obj.getObjParentId()
|
600
|
-
if objPid in self._objDict:
|
601
|
-
info['parent'] = self._objDict[objPid]
|
602
|
-
|
603
|
-
return info
|
604
|
-
|
605
|
-
def getObjectFromId(self, objId):
|
606
|
-
return self._objDict[objId]
|
607
|
-
|
608
|
-
|
609
|
-
class ListTreeProvider(TreeProvider):
|
610
|
-
""" Simple list tree provider. """
|
611
|
-
|
612
|
-
def __init__(self, objList=None):
|
613
|
-
TreeProvider.__init__(self)
|
614
|
-
self.objList = objList
|
615
|
-
self.getColumns = lambda: [('Object', 150)]
|
616
|
-
self.getObjects = lambda: self.objList
|
617
|
-
|
618
|
-
def getObjectInfo(self, obj):
|
619
|
-
info = {'key': obj.getObjId(), 'text': self.getText(obj), 'values': ()}
|
620
|
-
return info
|
621
|
-
|
622
|
-
def getText(self, obj):
|
623
|
-
""" Get the text to display for an object. """
|
624
|
-
index, fn = obj.getLocation()
|
625
|
-
name = os.path.basename(fn)
|
626
|
-
if index:
|
627
|
-
name = "%03d@%s" % (index, name)
|
628
|
-
return name
|
629
|
-
|
630
|
-
def getObjs(self):
|
631
|
-
""" Get the objects. """
|
632
|
-
return self.objList
|
633
|
-
|
634
|
-
|
635
|
-
class ListTreeProviderString(ListTreeProvider):
|
636
|
-
def getText(self, obj):
|
637
|
-
return obj.get()
|
638
|
-
|
639
|
-
|
640
|
-
class ListTreeProviderTemplate(ListTreeProviderString):
|
641
|
-
def __init__(self, objList=None):
|
642
|
-
TreeProvider.__init__(self)
|
643
|
-
self.objList = objList
|
644
|
-
self.getColumns = lambda: [('Template', 250),
|
645
|
-
('Description', 800)]
|
646
|
-
self.getObjects = lambda: self.objList
|
647
|
-
|
648
|
-
def getObjectInfo(self, obj):
|
649
|
-
info = {'key': obj.getObjId(), 'text': self.getText(obj), 'values': self.getValues(obj)}
|
650
|
-
return info
|
651
|
-
|
652
|
-
def getText(self, obj):
|
653
|
-
return obj.source + '-' + obj.name
|
654
|
-
|
655
|
-
def getValues(self, obj):
|
656
|
-
return (obj.description,)
|
657
|
-
|
658
|
-
class AttributesTreeProvider(ListTreeProviderString):
|
659
|
-
def __init__(self, item):
|
660
|
-
TreeProvider.__init__(self)
|
661
|
-
self.objList = self._attributesToObjectList(item)
|
662
|
-
self.getColumns = lambda: [('attribute', 250),
|
663
|
-
('value', 125)]
|
664
|
-
self.getObjects = lambda: self.objList
|
665
|
-
|
666
|
-
def _attributesToObjectList(self, item):
|
667
|
-
""" Returns a list of all available attributes ready as a list of objects for the tree"""
|
668
|
-
objList = []
|
669
|
-
|
670
|
-
for key, attr in item.getAttributesToStore():
|
671
|
-
clone = attr.clone()
|
672
|
-
clone.attrName = key
|
673
|
-
objList.append(clone)
|
674
|
-
return objList
|
675
|
-
|
676
|
-
def getObjectInfo(self, obj):
|
677
|
-
info = {'key': obj.attrName, 'text': self.getText(obj), 'values': self.getValues(obj)}
|
678
|
-
return info
|
679
|
-
|
680
|
-
def getText(self, obj):
|
681
|
-
return obj.attrName
|
682
|
-
|
683
|
-
def getValues(self, obj):
|
684
|
-
return (obj.get(),)
|