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,179 +0,0 @@
|
|
1
|
-
# **************************************************************************
|
2
|
-
# *
|
3
|
-
# * Authors: Pablo Conesa (scipion@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
|
-
Module for editing variables
|
28
|
-
"""
|
29
|
-
|
30
|
-
import tkinter as tk
|
31
|
-
|
32
|
-
from pyworkflow import Config, Variable, VariablesRegistry, VarTypes
|
33
|
-
from pyworkflow.gui import Icon, configureWeigths, getDefaultFont, askPath
|
34
|
-
from pyworkflow.gui.tree import TreeProvider
|
35
|
-
import pyworkflow.gui.dialog as dialog
|
36
|
-
|
37
|
-
|
38
|
-
class VariablesTreeProvider(TreeProvider):
|
39
|
-
""" Populate Tree from Labels. """
|
40
|
-
def __init__(self):
|
41
|
-
TreeProvider.__init__(self)
|
42
|
-
self._parentDict = {}
|
43
|
-
|
44
|
-
def getColumns(self):
|
45
|
-
return [('Name', 300), ('Value', 500), ('Default?', 60), ('DefaultV',200),('Description', 400), ('Source', 150)]
|
46
|
-
|
47
|
-
def getObjectInfo(self, variable: Variable):
|
48
|
-
|
49
|
-
return {'key': variable.name, 'parent': None,
|
50
|
-
'text': variable.name, 'values': (variable.value, "is default" if variable.isDefault else "Declared",
|
51
|
-
variable.default, variable.description, variable.source),
|
52
|
-
}
|
53
|
-
|
54
|
-
|
55
|
-
def getObjects(self):
|
56
|
-
return VariablesRegistry.__iter__()
|
57
|
-
|
58
|
-
|
59
|
-
class VariablesDialog(dialog.ToolbarListDialog):
|
60
|
-
"""
|
61
|
-
This dialog will allow editing variables coming either from pyworkflow or the plugins
|
62
|
-
"""
|
63
|
-
def __init__(self, parent, **kwargs):
|
64
|
-
""" From kwargs:
|
65
|
-
message: message tooltip to show when browsing.
|
66
|
-
selected: the item that should be selected.
|
67
|
-
validateSelectionCallback:
|
68
|
-
a callback function to validate selected items.
|
69
|
-
allowSelect: if set to False, the 'Select' button will not
|
70
|
-
be shown.
|
71
|
-
"""
|
72
|
-
toolbarButtons = [
|
73
|
-
# dialog.ToolbarButton('Add', self._addLabel, Icon.ACTION_NEW),
|
74
|
-
dialog.ToolbarButton('Edit', self._editVariable, Icon.ACTION_EDIT),
|
75
|
-
dialog.ToolbarButton('Set to default', self._setToDefault, Icon.ACTION_DELETE)
|
76
|
-
]
|
77
|
-
|
78
|
-
helpMsg =("Use this form to change core and plugin's variables. If \"Default\" is true, it currently is the default value and is not present in the config file. All changes here "
|
79
|
-
"will be persisted in the general %s config file. \"Source\" field tells you where the variable is defined."% Config.SCIPION_CONFIG)
|
80
|
-
|
81
|
-
dialog.ToolbarListDialog.__init__(self, parent,
|
82
|
-
"Edit config variables",
|
83
|
-
VariablesTreeProvider(),
|
84
|
-
helpMsg,
|
85
|
-
toolbarButtons,
|
86
|
-
allowsEmptySelection=True,
|
87
|
-
itemDoubleClick=self._editVariable,
|
88
|
-
cancelButton=False,
|
89
|
-
buttons=[('Save', dialog.RESULT_YES),
|
90
|
-
('Cancel', dialog.RESULT_CANCEL)],
|
91
|
-
**kwargs)
|
92
|
-
|
93
|
-
|
94
|
-
def apply(self):
|
95
|
-
VariablesRegistry.save(Config.SCIPION_CONFIG)
|
96
|
-
def _editVariable(self, e=None):
|
97
|
-
selection = self.tree.getSelectedObjects()
|
98
|
-
if selection:
|
99
|
-
variable = selection[0]
|
100
|
-
dlg = EditVariableDialog(self, "Edit variable", variable)
|
101
|
-
if dlg.resultYes():
|
102
|
-
self.refresh()
|
103
|
-
|
104
|
-
def _setToDefault(self, e=None):
|
105
|
-
selection = self.tree.getSelectedObjects()
|
106
|
-
if selection:
|
107
|
-
varsStr = '\n'.join('- %s' % v.name for v in selection)
|
108
|
-
if dialog.askYesNo(" The variables will be set it to its default value.",
|
109
|
-
"Are you sure to reset the %s"
|
110
|
-
"value?\n" % varsStr, self):
|
111
|
-
for variable in selection:
|
112
|
-
variable.setToDefault()
|
113
|
-
self.tree.update()
|
114
|
-
|
115
|
-
|
116
|
-
class EditVariableDialog(dialog.Dialog):
|
117
|
-
""" Dialog to edit a variable """
|
118
|
-
def __init__(self, parent, title, variable:Variable, **kwargs):
|
119
|
-
self.variable = variable
|
120
|
-
dialog.Dialog.__init__(self, parent, title)
|
121
|
-
|
122
|
-
def body(self, bodyFrame):
|
123
|
-
bodyFrame.config(bg=Config.SCIPION_BG_COLOR)
|
124
|
-
configureWeigths(bodyFrame, 1, 1)
|
125
|
-
|
126
|
-
# Name
|
127
|
-
label_text = tk.Label(bodyFrame, text=self.variable.name, bg=Config.SCIPION_BG_COLOR, bd=0)
|
128
|
-
label_text.grid(row=0, column=0, sticky='nw', padx=(15, 10), pady=15)
|
129
|
-
|
130
|
-
# Description
|
131
|
-
label_text = tk.Label(bodyFrame, text=self.variable.description, bg=Config.SCIPION_BG_COLOR, bd=0)
|
132
|
-
label_text.grid(row=0, column=1, sticky='nw', padx=(15, 10), pady=15)
|
133
|
-
|
134
|
-
# Value
|
135
|
-
label_text = tk.Label(bodyFrame, text="Value", bg=Config.SCIPION_BG_COLOR, bd=0)
|
136
|
-
label_text.grid(row=2, column=0, sticky='nw', padx=(15, 10), pady=15)
|
137
|
-
|
138
|
-
# Value Entry
|
139
|
-
if self.variable.var_type == VarTypes.INTEGER:
|
140
|
-
var=tk.IntVar()
|
141
|
-
else:
|
142
|
-
var = tk.StringVar()
|
143
|
-
|
144
|
-
var.set(self.variable.value)
|
145
|
-
self.valueVar = var
|
146
|
-
self.valueLabel = tk.Entry(bodyFrame, width=30, textvariable=var, font=getDefaultFont())
|
147
|
-
self.valueLabel.grid(row=2, column=1, sticky='news', padx=5, pady=5)
|
148
|
-
|
149
|
-
if self.variable.var_type in [VarTypes.PATH, VarTypes.FOLDER, VarTypes.FILENAME]:
|
150
|
-
self._addButton(bodyFrame, self.buttonClicked, icon=Icon.ACTION_BROWSE, row=2, col=2,tooltip="Click to browse file system for a file or folder")
|
151
|
-
|
152
|
-
def buttonClicked(self, e):
|
153
|
-
""" Callback for when the wizard button has been clicked"""
|
154
|
-
|
155
|
-
var_type = self.variable.var_type
|
156
|
-
|
157
|
-
if var_type in [VarTypes.PATH, VarTypes.FOLDER, VarTypes.FILENAME]:
|
158
|
-
|
159
|
-
onlyFolders = (var_type == VarTypes.FOLDER)
|
160
|
-
returnBaseName = (var_type ==var_type.FILENAME)
|
161
|
-
path = self.valueVar.get() if not returnBaseName else "."
|
162
|
-
|
163
|
-
value =askPath(path=path, master=self, onlyFolders=onlyFolders, returnBaseName=returnBaseName)
|
164
|
-
self.valueVar.set(value)
|
165
|
-
def apply(self):
|
166
|
-
self.variable.setValue(self.valueVar.get())
|
167
|
-
|
168
|
-
def validate(self):
|
169
|
-
|
170
|
-
validationMsg = None
|
171
|
-
|
172
|
-
if len(self.valueVar.get().strip()) == 0:
|
173
|
-
validationMsg = "Value name can't be empty.\n"
|
174
|
-
|
175
|
-
if validationMsg is not None:
|
176
|
-
dialog.showError("Validation error", validationMsg, self)
|
177
|
-
return False
|
178
|
-
|
179
|
-
return True
|
@@ -1,472 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
# **************************************************************************
|
3
|
-
# *
|
4
|
-
# * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
|
5
|
-
# *
|
6
|
-
# * [1] SciLifeLab, Stockholm University
|
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
|
-
"""
|
26
|
-
Data-oriented view of the project objects.
|
27
|
-
"""
|
28
|
-
|
29
|
-
|
30
|
-
import tkinter as tk
|
31
|
-
import tkinter.ttk as ttk
|
32
|
-
import tkinter.font as tkFont
|
33
|
-
|
34
|
-
from pyworkflow import Config
|
35
|
-
import pyworkflow.utils as pwutils
|
36
|
-
import pyworkflow.viewer as pwviewer
|
37
|
-
|
38
|
-
import pyworkflow.gui as gui
|
39
|
-
from pyworkflow.gui.tree import Tree
|
40
|
-
from pyworkflow.gui.dialog import EditObjectDialog
|
41
|
-
from pyworkflow.gui.text import TaggedText
|
42
|
-
from pyworkflow.gui import Canvas, RoundedTextBox
|
43
|
-
from pyworkflow.gui.graph import LevelTree
|
44
|
-
from pyworkflow.gui.form import getObjectLabel
|
45
|
-
from pyworkflow.constants import DATA_TAG
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def populateTree(tree, elements, parentId=''):
|
51
|
-
for node in elements:
|
52
|
-
if hasattr(node, 'count'):
|
53
|
-
t = node.getName()
|
54
|
-
if node.count:
|
55
|
-
t += ' (%d)' % node.count
|
56
|
-
node.nodeId = tree.insert(parentId, 'end', node.getName(),
|
57
|
-
text=t, tags=DATA_TAG)
|
58
|
-
populateTree(tree, node.getChildren(), node.nodeId)
|
59
|
-
if node.count:
|
60
|
-
tree.see(node.nodeId)
|
61
|
-
tree.item(node.nodeId, tags=('non-empty', DATA_TAG))
|
62
|
-
|
63
|
-
|
64
|
-
class ProjectDataView(tk.Frame):
|
65
|
-
def __init__(self, parent, windows, **args):
|
66
|
-
tk.Frame.__init__(self, parent, **args)
|
67
|
-
# Load global configuration
|
68
|
-
self.windows = windows
|
69
|
-
self.project = windows.project
|
70
|
-
self.root = windows.root
|
71
|
-
self.getImage = windows.getImage
|
72
|
-
self.settings = windows.getSettings()
|
73
|
-
self.showGraph = self.settings.getRunsView()
|
74
|
-
self.style = ttk.Style()
|
75
|
-
|
76
|
-
self.root.bind("<F5>", self.refreshData)
|
77
|
-
self.__autoRefresh = None
|
78
|
-
self.__autoRefreshCounter = 3 # start by 3 secs
|
79
|
-
|
80
|
-
self._dataGraph = windows.project.getSourceGraph(True)
|
81
|
-
|
82
|
-
c = self._createContent()
|
83
|
-
|
84
|
-
gui.configureWeigths(self)
|
85
|
-
c.grid(row=0, column=0, sticky='news')
|
86
|
-
|
87
|
-
def _createContent(self):
|
88
|
-
""" Create the Data View for the Project.
|
89
|
-
It has two panes:
|
90
|
-
Left: containing the Protocol classes tree
|
91
|
-
Right: containing the Data list
|
92
|
-
"""
|
93
|
-
p = tk.PanedWindow(self, orient=tk.HORIZONTAL, bg=Config.SCIPION_BG_COLOR)
|
94
|
-
|
95
|
-
# Left pane, contains Data tree
|
96
|
-
leftFrame = tk.Frame(p, bg=Config.SCIPION_BG_COLOR)
|
97
|
-
bgColor = '#eaebec'
|
98
|
-
self._createDataTree(leftFrame, bgColor)
|
99
|
-
gui.configureWeigths(leftFrame)
|
100
|
-
|
101
|
-
# Right pane
|
102
|
-
rightFrame = tk.Frame(p, bg=Config.SCIPION_BG_COLOR)
|
103
|
-
rightFrame.columnconfigure(0, weight=1)
|
104
|
-
rightFrame.rowconfigure(1, weight=1)
|
105
|
-
# rightFrame.rowconfigure(0, minsize=label.winfo_reqheight())
|
106
|
-
self._fillRightPane(rightFrame)
|
107
|
-
|
108
|
-
# Add sub-windows to PanedWindows
|
109
|
-
p.add(leftFrame, padx=5, pady=5)
|
110
|
-
p.add(rightFrame, padx=5, pady=5)
|
111
|
-
p.paneconfig(leftFrame, minsize=300)
|
112
|
-
p.paneconfig(rightFrame, minsize=400)
|
113
|
-
|
114
|
-
return p
|
115
|
-
|
116
|
-
def _createDataTree(self, parent, bgColor):
|
117
|
-
"""Create a tree on the left panel to store how
|
118
|
-
many object are from each type and the hierarchy.
|
119
|
-
"""
|
120
|
-
defaultFont = gui.getDefaultFont()
|
121
|
-
self.style.configure("W.Treeview",
|
122
|
-
background=pwutils.Color.ALT_COLOR,
|
123
|
-
borderwidth=0,
|
124
|
-
rowheight=defaultFont.metrics()['linespace'])
|
125
|
-
self.dataTree = Tree(parent, show='tree', style='W.Treeview')
|
126
|
-
self.dataTree.column('#0', minwidth=300)
|
127
|
-
self.dataTree.tag_configure('protocol',
|
128
|
-
image=self.getImage(pwutils.Icon.PRODUCTION))
|
129
|
-
self.dataTree.tag_configure('protocol_base',
|
130
|
-
image=self.getImage(pwutils.Icon.GROUP))
|
131
|
-
f = tkFont.Font(family='helvetica', size='10', weight='bold')
|
132
|
-
self.dataTree.tag_configure('non-empty', font=f)
|
133
|
-
self.dataTree.grid(row=0, column=0, sticky='news')
|
134
|
-
|
135
|
-
# bind click events
|
136
|
-
self.dataTree.tag_bind(DATA_TAG, '<Double-1>', self._dataItemClick)
|
137
|
-
self.dataTree.tag_bind(DATA_TAG, '<Return>', self._dataItemClick)
|
138
|
-
self.dataTree.tag_bind(DATA_TAG, '<KP_Enter>', self._dataItemClick)
|
139
|
-
|
140
|
-
# Program automatic refresh
|
141
|
-
self.dataTree.after(3000, self._automaticRefreshData)
|
142
|
-
|
143
|
-
self._updateDataTree()
|
144
|
-
|
145
|
-
def _updateDataTree(self):
|
146
|
-
|
147
|
-
def createClassNode(classObj):
|
148
|
-
""" Add the object class to hierarchy and
|
149
|
-
any needed subclass. """
|
150
|
-
className = classObj.__name__
|
151
|
-
classNode = classesGraph.getNode(className)
|
152
|
-
|
153
|
-
if not classNode:
|
154
|
-
classNode = classesGraph.createNode(className)
|
155
|
-
if className != 'EMObject' and classObj.__bases__:
|
156
|
-
baseClass = classObj.__bases__[0]
|
157
|
-
for b in classObj.__bases__:
|
158
|
-
if b.__name__ == 'EMObject':
|
159
|
-
baseClass = b
|
160
|
-
break
|
161
|
-
parent = createClassNode(baseClass)
|
162
|
-
parent.addChild(classNode)
|
163
|
-
classNode.count = 0
|
164
|
-
|
165
|
-
return classNode
|
166
|
-
|
167
|
-
classesGraph = pwutils.Graph()
|
168
|
-
|
169
|
-
self.dataTree.clear()
|
170
|
-
for node in self._dataGraph.getNodes():
|
171
|
-
if node.pointer:
|
172
|
-
classNode = createClassNode(node.pointer.get().getClass())
|
173
|
-
classNode.count += 1
|
174
|
-
|
175
|
-
populateTree(self.dataTree, classesGraph.getRootNodes())
|
176
|
-
|
177
|
-
def _fillRightPane(self, parent):
|
178
|
-
"""
|
179
|
-
# Create the right Pane that will be composed by:
|
180
|
-
# a Action Buttons TOOLBAR in the top
|
181
|
-
# and another vertical Pane with:
|
182
|
-
# Runs History (at Top)
|
183
|
-
# Selected run info (at Bottom)
|
184
|
-
"""
|
185
|
-
# Create the Action Buttons TOOLBAR
|
186
|
-
toolbar = tk.Frame(parent, bg=Config.SCIPION_BG_COLOR)
|
187
|
-
toolbar.grid(row=0, column=0, sticky='news')
|
188
|
-
gui.configureWeigths(toolbar)
|
189
|
-
# toolbar.columnconfigure(0, weight=1)
|
190
|
-
toolbar.columnconfigure(1, weight=1)
|
191
|
-
|
192
|
-
self.runsToolbar = tk.Frame(toolbar, bg=Config.SCIPION_BG_COLOR)
|
193
|
-
self.runsToolbar.grid(row=0, column=0, sticky='sw')
|
194
|
-
# On the left of the toolbar will be other
|
195
|
-
# actions that can be applied to all runs (refresh, graph view...)
|
196
|
-
self.allToolbar = tk.Frame(toolbar, bg=Config.SCIPION_BG_COLOR)
|
197
|
-
self.allToolbar.grid(row=0, column=10, sticky='se')
|
198
|
-
# self.createActionToolbar()
|
199
|
-
|
200
|
-
# Create the Run History tree
|
201
|
-
v = ttk.PanedWindow(parent, orient=tk.VERTICAL)
|
202
|
-
# runsFrame = ttk.Labelframe(v, text=' History ', width=500, height=500)
|
203
|
-
runsFrame = tk.Frame(v, bg=Config.SCIPION_BG_COLOR)
|
204
|
-
self._createDataGraph(runsFrame)
|
205
|
-
gui.configureWeigths(runsFrame)
|
206
|
-
|
207
|
-
# Create the Selected Run Info
|
208
|
-
infoFrame = tk.Frame(v)
|
209
|
-
gui.configureWeigths(infoFrame)
|
210
|
-
self._infoText = TaggedText(infoFrame, bg=Config.SCIPION_BG_COLOR,
|
211
|
-
handlers={'sci-open': self._openProtocolFormFromId})
|
212
|
-
self._infoText.grid(row=0, column=0, sticky='news')
|
213
|
-
|
214
|
-
v.add(runsFrame, weight=3)
|
215
|
-
v.add(infoFrame, weight=1)
|
216
|
-
v.grid(row=1, column=0, sticky='news')
|
217
|
-
|
218
|
-
# TODO(josemi): check that the call to RoundedTextBox is correct
|
219
|
-
# It looks suspicious because RoundedTextBox() needs 5 arguments, not 3
|
220
|
-
def _createNode(self, canvas, node, y):
|
221
|
-
""" Create Data node to be painted in the graph. """
|
222
|
-
node.selected = False
|
223
|
-
node.box = RoundedTextBox(canvas, node, y)
|
224
|
-
return node.box
|
225
|
-
|
226
|
-
def _createDataItem(self, canvas, node, y):
|
227
|
-
if node.pointer is None:
|
228
|
-
nodeText = "Project"
|
229
|
-
else:
|
230
|
-
label = getObjectLabel(node.pointer, self.project.mapper)
|
231
|
-
nodeText = (label[:25]+'...') if len(label) > 25 else label
|
232
|
-
|
233
|
-
textColor = 'black'
|
234
|
-
color = '#ADD8E6' # Lightblue
|
235
|
-
item = self._dataCanvas.createTextbox(nodeText, 100, y, bgColor=color,
|
236
|
-
textColor=textColor)
|
237
|
-
|
238
|
-
# Get the dataId
|
239
|
-
if not node.isRoot():
|
240
|
-
dataId = node.pointer.get().getObjId()
|
241
|
-
|
242
|
-
if dataId in self.settings.dataSelection:
|
243
|
-
item.setSelected(True)
|
244
|
-
|
245
|
-
return item
|
246
|
-
|
247
|
-
def _createDataGraph(self, parent):
|
248
|
-
""" This will be the upper part of the right panel.
|
249
|
-
It will contain the Data Graph with their relations.
|
250
|
-
"""
|
251
|
-
self._dataCanvas = Canvas(parent, width=600, height=500)
|
252
|
-
self._dataCanvas.grid(row=0, column=0, sticky='nsew')
|
253
|
-
self._dataCanvas.onClickCallback = self._onClick
|
254
|
-
self._dataCanvas.onDoubleClickCallback = self._onDoubleClick
|
255
|
-
self._dataCanvas.onRightClickCallback = self._onRightClick
|
256
|
-
|
257
|
-
self._updateDataGraph()
|
258
|
-
|
259
|
-
def _updateDataGraph(self):
|
260
|
-
lt = LevelTree(self._dataGraph)
|
261
|
-
self._dataCanvas.clear()
|
262
|
-
lt.setCanvas(self._dataCanvas)
|
263
|
-
lt.paint(self._createDataItem)
|
264
|
-
self._dataCanvas.updateScrollRegion()
|
265
|
-
|
266
|
-
def _dataItemClick(self, e=None):
|
267
|
-
# Get the tree widget that originated the event
|
268
|
-
# from the left pane data tree
|
269
|
-
tree = e.widget
|
270
|
-
dataClassName = tree.getFirst()
|
271
|
-
|
272
|
-
if dataClassName is not None:
|
273
|
-
self._loopData(lambda item: self._selectItemByClass(item, dataClassName))
|
274
|
-
|
275
|
-
def _selectObject(self, pobj):
|
276
|
-
obj = pobj.get()
|
277
|
-
objValue = pobj.getObjValue()
|
278
|
-
self._selected = obj
|
279
|
-
self._infoText.setReadOnly(False)
|
280
|
-
self._infoText.setText('*Label:* ' + getObjectLabel(pobj, self.project.mapper))
|
281
|
-
self._infoText.addText('*Info:* ' + str(obj))
|
282
|
-
self._infoText.addText('*Created by:*')
|
283
|
-
self._infoText.addText(' - %s (%s)'
|
284
|
-
% (objValue.getObjectTag(objValue),
|
285
|
-
obj.getObjCreation()))
|
286
|
-
|
287
|
-
# Get the consumers (this will get the data!!)
|
288
|
-
derivedData = self.project.getSourceChilds(objValue)
|
289
|
-
|
290
|
-
if derivedData is not None and len(derivedData) > 0:
|
291
|
-
self._infoText.addText('*Consumed by:*')
|
292
|
-
|
293
|
-
for data in derivedData:
|
294
|
-
# Get the protocol
|
295
|
-
protocol = self.project.getObject(data.getObjParentId())
|
296
|
-
self._infoText.addText(' - ' + protocol.getObjectTag(protocol))
|
297
|
-
|
298
|
-
if obj.getObjComment():
|
299
|
-
self._infoText.addText('*Comments:* ' + obj.getObjComment())
|
300
|
-
self._infoText.setReadOnly(True)
|
301
|
-
|
302
|
-
def _onClick(self, e=None):
|
303
|
-
self._deselectAll()
|
304
|
-
|
305
|
-
if e.node.pointer:
|
306
|
-
self.toggleItemSelection(e, True)
|
307
|
-
self._selectObject(e.node.pointer)
|
308
|
-
|
309
|
-
def _invertSelection(self):
|
310
|
-
|
311
|
-
self._loopData(self._invertAction)
|
312
|
-
|
313
|
-
def _deselectAll(self):
|
314
|
-
|
315
|
-
self._loopData(self._deselectItemAction)
|
316
|
-
|
317
|
-
def _selectAll(self):
|
318
|
-
|
319
|
-
self._loopData(self._selectItemAction)
|
320
|
-
|
321
|
-
def _selectItemAction(self, item):
|
322
|
-
self.toggleItemSelection(item, True)
|
323
|
-
|
324
|
-
def _selectItemByClass(self, item, className):
|
325
|
-
|
326
|
-
if not item.node.isRoot():
|
327
|
-
|
328
|
-
data = item.node.pointer.get()
|
329
|
-
self.toggleItemSelection(
|
330
|
-
item, isinstance(data, self.domain.getObjects()[className]))
|
331
|
-
|
332
|
-
def _invertAction(self, item):
|
333
|
-
self.toggleItemSelection(item, not item.getSelected())
|
334
|
-
|
335
|
-
def _deselectItemAction(self, item):
|
336
|
-
self.toggleItemSelection(item, False)
|
337
|
-
|
338
|
-
def toggleItemSelection(self, item, select):
|
339
|
-
if item.node.isRoot():
|
340
|
-
return
|
341
|
-
selection = self.settings.dataSelection
|
342
|
-
runSelection = self.settings.runSelection
|
343
|
-
dataId = item.node.pointer.get().getObjId()
|
344
|
-
protocolId = item.node.pointer.getObjValue().getObjId()
|
345
|
-
|
346
|
-
if not select:
|
347
|
-
try:
|
348
|
-
if dataId in selection:
|
349
|
-
selection.remove(dataId)
|
350
|
-
if protocolId in runSelection:
|
351
|
-
runSelection.remove(protocolId)
|
352
|
-
except ValueError:
|
353
|
-
print("id not in selection")
|
354
|
-
else:
|
355
|
-
selection.append(dataId)
|
356
|
-
runSelection.append(protocolId)
|
357
|
-
|
358
|
-
item.setSelected(select)
|
359
|
-
|
360
|
-
def _loopData(self, action):
|
361
|
-
results = []
|
362
|
-
# Loop through all the items
|
363
|
-
for key, item in self._dataCanvas.items.items():
|
364
|
-
result = action(item)
|
365
|
-
if result is not None:
|
366
|
-
results.append(result)
|
367
|
-
|
368
|
-
return results
|
369
|
-
|
370
|
-
def _onDoubleClick(self, item=None, e=None):
|
371
|
-
if item.node.pointer:
|
372
|
-
self._selectObject(e.node.pointer)
|
373
|
-
self._viewObject(e.node.pointer.get().getObjId())
|
374
|
-
return
|
375
|
-
# self._selectObject(e.node.pointer)
|
376
|
-
# # Graph the first viewer available for this type of object
|
377
|
-
# ViewerClass = findViewers(self._selected.getClassName(), DESKTOP_TKINTER)[0] #
|
378
|
-
# viewer = ViewerClass(project=self.project)
|
379
|
-
# viewer.visualize(self._selected)
|
380
|
-
|
381
|
-
def _viewObject(self, objId):
|
382
|
-
""" Call appropriate viewer for objId. """
|
383
|
-
obj = self.project.getObject(int(objId))
|
384
|
-
viewerClasses = self.domain.findViewers(obj.getClassName(),
|
385
|
-
pwviewer.DESKTOP_TKINTER)
|
386
|
-
if not viewerClasses:
|
387
|
-
return # TODO: protest nicely
|
388
|
-
viewer = viewerClasses[0](project=self.project, parent=self.windows)
|
389
|
-
viewer.visualize(obj)
|
390
|
-
|
391
|
-
def _openScipionLink(self, id):
|
392
|
-
""" So far only protocols are coming through links"""
|
393
|
-
self._openProtocolFormFromId(id)
|
394
|
-
|
395
|
-
def _openProtocolFormFromId(self, protId):
|
396
|
-
|
397
|
-
prot = self.project.getObject(int(protId))
|
398
|
-
self._openProtocolForm(prot)
|
399
|
-
|
400
|
-
def _openProtocolForm(self, prot):
|
401
|
-
"""Open the Protocol GUI Form given a Protocol instance"""
|
402
|
-
title = pwutils.Message.TITLE_NAME_RUN + prot.getClassName()
|
403
|
-
w = gui.form.FormWindow(title, prot, self._executeSaveProtocol,
|
404
|
-
self.windows)
|
405
|
-
w.adjustSize()
|
406
|
-
w.show(center=True)
|
407
|
-
|
408
|
-
def _executeSaveProtocol(self, prot, onlySave=False):
|
409
|
-
if onlySave:
|
410
|
-
self.project.saveProtocol(prot)
|
411
|
-
msg = pwutils.Message.LABEL_SAVED_FORM
|
412
|
-
# msg = "Protocol successfully saved."
|
413
|
-
else:
|
414
|
-
self.project.launchProtocol(prot)
|
415
|
-
# Select the launched protocol to display its summary, methods..etc
|
416
|
-
# self._selection.clear()
|
417
|
-
# self._selection.append(prot.getObjId())
|
418
|
-
# self._updateSelection()
|
419
|
-
# clear lastStatus to force re-load the logs
|
420
|
-
# self._lastStatus = None
|
421
|
-
msg = ""
|
422
|
-
|
423
|
-
return msg
|
424
|
-
|
425
|
-
def _onRightClick(self, item=None, e=None):
|
426
|
-
return []
|
427
|
-
#
|
428
|
-
# (pwutils.Message.LABEL_EDIT, self._editObject, pwutils.Icon.ACTION_EDIT),
|
429
|
-
# ('Go to protocol', self._goToProtocol, pwutils.Icon.ACTION_SEARCH)
|
430
|
-
# ]
|
431
|
-
|
432
|
-
def _editObject(self, e=None):
|
433
|
-
"""Open the Edit GUI Form given an instance"""
|
434
|
-
EditObjectDialog(self, pwutils.Message.TITLE_EDIT_OBJECT,
|
435
|
-
self._selected, self.project.mapper)
|
436
|
-
|
437
|
-
def _goToProtocol(self):
|
438
|
-
"""Switch to protocols view selecting the correspondent protocol"""
|
439
|
-
|
440
|
-
def refreshData(self, e=None, initRefreshCounter=True):
|
441
|
-
""" Refresh the status of displayed data.
|
442
|
-
Params:
|
443
|
-
e: Tk event input
|
444
|
-
initRefreshCounter: if True the refresh counter will be set to 3 secs
|
445
|
-
then only case when False is from _automaticRefreshData where the
|
446
|
-
refresh time is doubled each time to avoid refreshing too often.
|
447
|
-
"""
|
448
|
-
self._dataGraph = self.windows.project.getSourceGraph(True)
|
449
|
-
self._updateDataTree()
|
450
|
-
self._updateDataGraph()
|
451
|
-
|
452
|
-
if initRefreshCounter:
|
453
|
-
self.__autoRefreshCounter = 3 # start by 3 secs
|
454
|
-
if self.__autoRefresh:
|
455
|
-
self.dataTree.after_cancel(self.__autoRefresh)
|
456
|
-
self.__autoRefresh = self.dataTree.after(
|
457
|
-
self.__autoRefreshCounter*1000, self._automaticRefreshData)
|
458
|
-
|
459
|
-
def _automaticRefreshData(self, e=None):
|
460
|
-
""" Schedule automatic refresh increasing the time between refreshes.
|
461
|
-
"""
|
462
|
-
self.refreshData(initRefreshCounter=False)
|
463
|
-
secs = self.__autoRefreshCounter
|
464
|
-
# double the number of seconds up to 30 min
|
465
|
-
self.__autoRefreshCounter = min(2*secs, 1800)
|
466
|
-
self.__autoRefresh = self.dataTree.after(secs*1000,
|
467
|
-
self._automaticRefreshData)
|
468
|
-
|
469
|
-
|
470
|
-
class DataTextBox(RoundedTextBox):
|
471
|
-
def __init__(self, canvas, text, x, y, bgColor, textColor='black'):
|
472
|
-
RoundedTextBox.__init__(self, canvas, text, x, y, bgColor, textColor)
|