scipion-pyworkflow 3.8.0__py3-none-any.whl → 3.9.1__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 +8 -2
- pyworkflow/constants.py +6 -1
- pyworkflow/gui/canvas.py +6 -6
- pyworkflow/gui/dialog.py +3 -2
- pyworkflow/gui/form.py +12 -2
- pyworkflow/gui/graph.py +6 -6
- pyworkflow/gui/graph_layout.py +2 -2
- pyworkflow/gui/project/base.py +7 -7
- pyworkflow/gui/project/project.py +36 -28
- pyworkflow/gui/project/steps.py +8 -3
- pyworkflow/gui/project/viewdata.py +1 -1
- pyworkflow/gui/project/viewprotocols.py +9 -9
- pyworkflow/gui/project/viewprotocols_extra.py +3 -3
- pyworkflow/gui/widgets.py +2 -2
- pyworkflow/mapper/sqlite.py +5 -5
- pyworkflow/object.py +1 -1
- pyworkflow/plugin.py +1 -0
- pyworkflow/project/config.py +4 -1
- pyworkflow/project/manager.py +4 -3
- pyworkflow/project/project.py +37 -21
- pyworkflow/project/scripts/create.py +14 -4
- pyworkflow/project/scripts/schedule.py +1 -1
- pyworkflow/protocol/constants.py +1 -0
- pyworkflow/protocol/executor.py +133 -19
- pyworkflow/protocol/params.py +5 -8
- pyworkflow/protocol/protocol.py +36 -36
- pyworkflow/template.py +1 -1
- pyworkflow/utils/graph.py +24 -51
- pyworkflow/utils/properties.py +16 -12
- pyworkflowtests/protocols.py +3 -3
- pyworkflowtests/tests/test_protocol_execution.py +63 -0
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/METADATA +1 -1
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/RECORD +38 -38
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/WHEEL +1 -1
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/dependency_links.txt +0 -0
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.1.dist-info}/top_level.txt +0 -0
pyworkflow/config.py
CHANGED
@@ -198,6 +198,7 @@ class Config:
|
|
198
198
|
|
199
199
|
# Internal cached variables, use __ so they are not returned in getVars
|
200
200
|
__activeColor = None
|
201
|
+
__defaultSpritesFile = _join(getResourcesPath(),'sprites.png')
|
201
202
|
|
202
203
|
CONDA_ACTIVATION_CMD = _get(CONDA_ACTIVATION_CMD_VAR,'',
|
203
204
|
"str: Command to activate/initialize conda itself. Do not confuse it with 'conda activate'. It should be defined at installation time. It looks like this: eval \"$(/extra/miniconda3/bin/conda shell.bash hook)\"")
|
@@ -299,7 +300,7 @@ class Config:
|
|
299
300
|
SCIPION_CONTRAST_COLOR = _get('SCIPION_CONTRAST_COLOR', 'cyan',
|
300
301
|
"Color used to highlight features over grayscaled images.", caster=validColor)
|
301
302
|
|
302
|
-
SCIPION_SPRITES_FILE = _get('SCIPION_SPRITES_FILE',
|
303
|
+
SCIPION_SPRITES_FILE = _get('SCIPION_SPRITES_FILE', __defaultSpritesFile,
|
303
304
|
"File (png) with the icons in a collage. Default is found at pyworkflow/resources/sprites.png. And a GIMP file could be found at the same folder in the github repo.")
|
304
305
|
|
305
306
|
SCIPION_SHOW_TEXT_IN_TOOLBAR = _get('SCIPION_SHOW_TEXT_IN_TOOLBAR', TRUE_STR,
|
@@ -544,7 +545,12 @@ class Config:
|
|
544
545
|
condaExe = os.path.join(envFolder, "bin", "python")
|
545
546
|
return condaExe == getPython()
|
546
547
|
|
547
|
-
|
548
|
+
@classmethod
|
549
|
+
def getSpritesFile(cls):
|
550
|
+
if not os.path.exists(Config.SCIPION_SPRITES_FILE):
|
551
|
+
return cls.__defaultSpritesFile
|
552
|
+
else:
|
553
|
+
return Config.SCIPION_SPRITES_FILE
|
548
554
|
|
549
555
|
|
550
556
|
# Add bindings folder to sys.path
|
pyworkflow/constants.py
CHANGED
@@ -43,7 +43,7 @@ VERSION_1 = '1.0.0'
|
|
43
43
|
VERSION_1_1 = '1.1.0'
|
44
44
|
VERSION_1_2 = '1.2.0'
|
45
45
|
VERSION_2_0 = '2.0.0'
|
46
|
-
VERSION_3_0 = '3.
|
46
|
+
VERSION_3_0 = '3.9.1'
|
47
47
|
|
48
48
|
# For a new release, define a new constant and assign it to LAST_VERSION
|
49
49
|
# The existing one has to be added to OLD_VERSIONS list.
|
@@ -192,6 +192,7 @@ class DOCSITEURLS:
|
|
192
192
|
WAIT_FOR = GUI + '#waiting-for-other-protocols'
|
193
193
|
PLUGIN_MANAGER = USER + 'plugin-manager.html'
|
194
194
|
HOST_CONFIG = DOCS + "scipion-modes/host-configuration.html"
|
195
|
+
THREADS_MPIS_AND_GPUS = USER + 'threads-mpi-gpus.html'
|
195
196
|
|
196
197
|
|
197
198
|
# tkinter bind constants
|
@@ -211,3 +212,7 @@ DEFAULT_EXECUTION_ACTION_ASK = 1
|
|
211
212
|
DEFAULT_EXECUTION_ACTION_SINGLE = 2
|
212
213
|
DEFAULT_EXECUTION_ACTION_ALL = 3
|
213
214
|
|
215
|
+
# Id field/attribute constants
|
216
|
+
ID_COLUMN='id'
|
217
|
+
ID_ATTRIBUTE='_objId'
|
218
|
+
|
pyworkflow/gui/canvas.py
CHANGED
@@ -82,8 +82,8 @@ class Canvas(tk.Canvas, Scrollable):
|
|
82
82
|
self.bind("<Control-1>", self.onControlClick)
|
83
83
|
# self.bind("<MouseWheel>", self.onScroll)
|
84
84
|
# Scroll bindings in Linux
|
85
|
-
self.bind("<
|
86
|
-
self.bind("<
|
85
|
+
self.bind("<Control-Button-4>", self.zoomerP)
|
86
|
+
self.bind("<Control-Button-5>", self.zoomerM)
|
87
87
|
|
88
88
|
self._tooltipId = None
|
89
89
|
self._tooltipOn = False # True if the tooltip is displayed
|
@@ -475,7 +475,7 @@ class Canvas(tk.Canvas, Scrollable):
|
|
475
475
|
self.addItem(item)
|
476
476
|
|
477
477
|
if getattr(node, 'expanded', True):
|
478
|
-
for child in node.
|
478
|
+
for child in node.getChildren():
|
479
479
|
if self.nodeList is None:
|
480
480
|
self._drawNodes(child, visitedDict)
|
481
481
|
elif self.nodeList.getNode(child.run.getObjId()).isVisible():
|
@@ -512,7 +512,7 @@ class Canvas(tk.Canvas, Scrollable):
|
|
512
512
|
"""
|
513
513
|
Return a list with the visible parents of the node's children
|
514
514
|
"""
|
515
|
-
for child in node.
|
515
|
+
for child in node.getChildren():
|
516
516
|
parents = child.getParents()
|
517
517
|
for parent in parents:
|
518
518
|
parentNode = self.nodeList.getNode(parent.run.getObjId())
|
@@ -525,7 +525,7 @@ class Canvas(tk.Canvas, Scrollable):
|
|
525
525
|
the properties (width, height, x and y) is propagated
|
526
526
|
to the hidden childs.
|
527
527
|
"""
|
528
|
-
for child in node.
|
528
|
+
for child in node.getChildren():
|
529
529
|
if child.getName() not in visitedDict:
|
530
530
|
child.width = node.width
|
531
531
|
child.height = node.height
|
@@ -544,7 +544,7 @@ class Canvas(tk.Canvas, Scrollable):
|
|
544
544
|
item.moveTo(node.x, node.y)
|
545
545
|
|
546
546
|
if getattr(node, 'expanded', True):
|
547
|
-
for child in node.
|
547
|
+
for child in node.getChildren():
|
548
548
|
if self.nodeList is None:
|
549
549
|
self.createEdge(item, child.item)
|
550
550
|
self._updatePositions(child, visitedDict, createEdges)
|
pyworkflow/gui/dialog.py
CHANGED
@@ -35,7 +35,7 @@ from tkcolorpicker import askcolor as _askColor
|
|
35
35
|
from pyworkflow import Config
|
36
36
|
from pyworkflow.exceptions import PyworkflowException
|
37
37
|
from pyworkflow.utils import Message, Icon, Color
|
38
|
-
from . import gui, Window, widgets, configureWeigths, LIST_TREEVIEW, defineStyle, ToolTip
|
38
|
+
from . import gui, Window, widgets, configureWeigths, LIST_TREEVIEW, defineStyle, ToolTip, getDefaultFont
|
39
39
|
from .tree import BoundTree, Tree
|
40
40
|
from .text import Text, TaggedText
|
41
41
|
|
@@ -443,7 +443,8 @@ class EntryDialog(Dialog):
|
|
443
443
|
label = tk.Label(bodyFrame, text=self.entryLabel, bg=Config.SCIPION_BG_COLOR, bd=0)
|
444
444
|
label.grid(row=row, column=0, sticky='nw', padx=(15, 10), pady=15)
|
445
445
|
self.entry = tk.Entry(bodyFrame, bg=gui.cfgEntryBgColor,
|
446
|
-
width=self.entryWidth, textvariable=self.tkvalue
|
446
|
+
width=self.entryWidth, textvariable=self.tkvalue,
|
447
|
+
font=getDefaultFont())
|
447
448
|
self.entry.grid(row=row, column=1, sticky='new', padx=(0, 15), pady=15)
|
448
449
|
self.initial_focus = self.entry
|
449
450
|
|
pyworkflow/gui/form.py
CHANGED
@@ -1698,14 +1698,19 @@ class FormWindow(Window):
|
|
1698
1698
|
c2 = 0
|
1699
1699
|
sticky = 'e'
|
1700
1700
|
|
1701
|
+
helpMessage = pwutils.Message.HELP_PARALLEL_HEADER
|
1702
|
+
|
1701
1703
|
if mode == pwprot.STEPS_PARALLEL:
|
1702
1704
|
if allowThreads and numberOfThreads > 0:
|
1703
1705
|
prot.numberOfMpi.set(1)
|
1704
|
-
self._createHeaderLabel(procFrame, pwutils.Message.
|
1706
|
+
self._createHeaderLabel(procFrame, pwutils.Message.LABEL_SCIPION_THREADS,
|
1705
1707
|
sticky=sticky, row=r2, column=c2,
|
1706
1708
|
pady=0)
|
1707
1709
|
entry = self._createBoundEntry(procFrame,
|
1708
1710
|
pwutils.Message.VAR_THREADS)
|
1711
|
+
|
1712
|
+
helpMessage += pwutils.Message.HELP_SCIPION_THREADS
|
1713
|
+
|
1709
1714
|
entry.grid(row=r2, column=c2 + 1, padx=(0, 5), sticky='w')
|
1710
1715
|
elif allowMpi and numberOfMpi > 1:
|
1711
1716
|
self.showError("MPI parameter is deprecated for protocols "
|
@@ -1728,6 +1733,8 @@ class FormWindow(Window):
|
|
1728
1733
|
# Modify values to be used in MPI entry
|
1729
1734
|
c2 += 2
|
1730
1735
|
sticky = 'w'
|
1736
|
+
|
1737
|
+
helpMessage += pwutils.Message.HELP_PARALLEL_THREADS
|
1731
1738
|
# ---- MPI ----
|
1732
1739
|
if allowMpi:
|
1733
1740
|
self._createHeaderLabel(procFrame, pwutils.Message.LABEL_MPI,
|
@@ -1736,11 +1743,14 @@ class FormWindow(Window):
|
|
1736
1743
|
entry = self._createBoundEntry(procFrame, pwutils.Message.VAR_MPI)
|
1737
1744
|
entry.grid(row=r2, column=c2 + 1, padx=(0, 5), sticky='w')
|
1738
1745
|
|
1746
|
+
helpMessage += pwutils.Message.HELP_PARALLEL_MPI
|
1747
|
+
|
1748
|
+
|
1739
1749
|
btnHelp = IconButton(procFrame, pwutils.Message.TITLE_COMMENT,
|
1740
1750
|
pwutils.Icon.ACTION_HELP,
|
1741
1751
|
highlightthickness=0,
|
1742
1752
|
command=self._createHelpCommand(
|
1743
|
-
|
1753
|
+
helpMessage))
|
1744
1754
|
btnHelp.grid(row=0, column=4, padx=(5, 0), pady=2, sticky='e')
|
1745
1755
|
|
1746
1756
|
procFrame.columnconfigure(0, minsize=60)
|
pyworkflow/gui/graph.py
CHANGED
@@ -85,7 +85,7 @@ class LevelTree(object):
|
|
85
85
|
nextLevel = level + 1
|
86
86
|
if nextLevel > self.maxLevel:
|
87
87
|
return
|
88
|
-
for child in node.
|
88
|
+
for child in node.getChildren():
|
89
89
|
if nextLevel > getattr(child, 'level', 0):
|
90
90
|
self._setLevel(child, nextLevel, node)
|
91
91
|
|
@@ -97,7 +97,7 @@ class LevelTree(object):
|
|
97
97
|
if level > self.maxLevel:
|
98
98
|
return
|
99
99
|
|
100
|
-
childs = [c for c in node.
|
100
|
+
childs = [c for c in node.getChildren() if c.parent is node]
|
101
101
|
n = len(childs)
|
102
102
|
|
103
103
|
if n > 0:
|
@@ -174,7 +174,7 @@ class LevelTree(object):
|
|
174
174
|
for each level of the tree
|
175
175
|
"""
|
176
176
|
node.hLimits = [[-node.half, node.half]]
|
177
|
-
childs = [c for c in node.
|
177
|
+
childs = [c for c in node.getChildren() if c.parent is node]
|
178
178
|
for child in childs:
|
179
179
|
count = 1
|
180
180
|
if not hasattr(child, 'hLimits'):
|
@@ -221,7 +221,7 @@ class LevelTree(object):
|
|
221
221
|
if node.level == self.maxLevel:
|
222
222
|
return
|
223
223
|
|
224
|
-
for c in node.
|
224
|
+
for c in node.getChildren():
|
225
225
|
if c.parent is node:
|
226
226
|
self._createEdges(c, nx)
|
227
227
|
self.createEdge(node.item, c.item)
|
@@ -230,7 +230,7 @@ class LevelTree(object):
|
|
230
230
|
""" Paint nodes using its position. """
|
231
231
|
self._paintNode(node, None)
|
232
232
|
|
233
|
-
for child in node.
|
233
|
+
for child in node.getChildren():
|
234
234
|
# parent = None for nodes that have been not traversed
|
235
235
|
parent = getattr(child, 'parent', None)
|
236
236
|
if parent is None:
|
@@ -241,7 +241,7 @@ class LevelTree(object):
|
|
241
241
|
""" Paint only the edges between nodes, assuming they are
|
242
242
|
already well positioned.
|
243
243
|
"""
|
244
|
-
for child in node.
|
244
|
+
for child in node.getChildren():
|
245
245
|
if child.parent is node:
|
246
246
|
self._paintEdges(child)
|
247
247
|
self.createEdge(node.item, child.item)
|
pyworkflow/gui/graph_layout.py
CHANGED
@@ -132,7 +132,7 @@ class LevelTreeLayout(GraphLayout):
|
|
132
132
|
|
133
133
|
if self.__isNodeExpanded(node):
|
134
134
|
ancestors.append(node.getName())
|
135
|
-
for child in node.
|
135
|
+
for child in node.getChildren():
|
136
136
|
if child.getName() in ancestors:
|
137
137
|
logger.warning("WARNING: There might be a cyclic redundancy error in this protocol: %s (%s)" %(child.getLabel(),
|
138
138
|
child.getName()))
|
@@ -155,7 +155,7 @@ class LevelTreeLayout(GraphLayout):
|
|
155
155
|
visited by this node first (its 'parent')
|
156
156
|
"""
|
157
157
|
if self.__isNodeExpanded(node):
|
158
|
-
return [c for c in node.
|
158
|
+
return [c for c in node.getChildren() if c._layout['parent'] is node]
|
159
159
|
else:
|
160
160
|
return [] # treat collapsed nodes as if they have no childs
|
161
161
|
|
pyworkflow/gui/project/base.py
CHANGED
@@ -28,7 +28,7 @@ import webbrowser
|
|
28
28
|
import tkinter as tk
|
29
29
|
|
30
30
|
import pyworkflow as pw
|
31
|
-
from pyworkflow.gui import Window, Message, Color, getBigFont, defineStyle
|
31
|
+
from pyworkflow.gui import Window, Message, Color, getBigFont, defineStyle, ToolTip
|
32
32
|
from pyworkflow.gui.widgets import GradientFrame
|
33
33
|
from pyworkflow.utils.properties import Icon
|
34
34
|
from pyworkflow.gui.project.variables import VariablesDialog
|
@@ -98,18 +98,18 @@ class ProjectBaseWindow(Window):
|
|
98
98
|
bg=pw.Config.SCIPION_BG_COLOR)
|
99
99
|
versionLabel.grid(row=0, column=1, sticky='sw', pady=20)
|
100
100
|
|
101
|
-
|
102
|
-
|
103
|
-
projLabel = tk.Label(header, text=projName, font=getBigFont(),
|
104
|
-
borderwidth=0, anchor='nw', bg=pw.Config.SCIPION_BG_COLOR,
|
105
|
-
fg=Color.ALT_COLOR_DARK)
|
106
|
-
projLabel.grid(row=0, column=2, sticky='sw', padx=(20, 5), pady=10)
|
101
|
+
self.customizeheader(header)
|
102
|
+
|
107
103
|
# Create gradient
|
108
104
|
GradientFrame(header, height=8, borderwidth=0).grid(row=1, column=0,
|
109
105
|
columnspan=3,
|
110
106
|
sticky='new')
|
111
107
|
return header
|
112
108
|
|
109
|
+
def customizeheader(self, headerFrame):
|
110
|
+
""" Implement in children classes to customize it: e.g.: Project name in project window"""
|
111
|
+
pass
|
112
|
+
|
113
113
|
def addViewList(self, header):
|
114
114
|
"""Create the view selection frame (Protocols|Data) in the header.
|
115
115
|
"""
|
@@ -31,6 +31,10 @@ It is composed by three panels:
|
|
31
31
|
"""
|
32
32
|
|
33
33
|
import logging
|
34
|
+
from tkinter import Label
|
35
|
+
|
36
|
+
from .. import askString
|
37
|
+
|
34
38
|
logger = logging.getLogger(__name__)
|
35
39
|
|
36
40
|
import os
|
@@ -43,7 +47,7 @@ import pyworkflow as pw
|
|
43
47
|
import pyworkflow.utils as pwutils
|
44
48
|
from pyworkflow.gui.project.utils import OS
|
45
49
|
from pyworkflow.project import MenuConfig
|
46
|
-
from pyworkflow.gui import Message, Icon
|
50
|
+
from pyworkflow.gui import Message, Icon, getBigFont, ToolTip
|
47
51
|
from pyworkflow.gui.browser import FileBrowserWindow
|
48
52
|
|
49
53
|
from pyworkflow.gui.plotter import Plotter
|
@@ -61,7 +65,10 @@ class ProjectWindow(ProjectBaseWindow):
|
|
61
65
|
|
62
66
|
def __init__(self, path, master=None):
|
63
67
|
# Load global configuration
|
64
|
-
self.
|
68
|
+
self.projPath = path
|
69
|
+
self.project = self.loadProject()
|
70
|
+
self.projName = self.project.getShortName()
|
71
|
+
|
65
72
|
try:
|
66
73
|
projTitle = '%s (%s on %s)' % (self.projName,
|
67
74
|
pwutils.getLocalUserName(),
|
@@ -69,8 +76,6 @@ class ProjectWindow(ProjectBaseWindow):
|
|
69
76
|
except Exception:
|
70
77
|
projTitle = self.projName
|
71
78
|
|
72
|
-
self.projPath = path
|
73
|
-
self.project = self.loadProject()
|
74
79
|
|
75
80
|
# TODO: put the menu part more nicely. From here:
|
76
81
|
menu = MenuConfig()
|
@@ -119,6 +124,8 @@ class ProjectWindow(ProjectBaseWindow):
|
|
119
124
|
# Notify about the workflow in this project
|
120
125
|
self.selectedProtocol = None
|
121
126
|
self.showGraph = False
|
127
|
+
self.commentTT = None # Tooltip to store the project description
|
128
|
+
|
122
129
|
Plotter.setBackend('TkAgg')
|
123
130
|
ProjectBaseWindow.__init__(self, projTitle, master,
|
124
131
|
minsize=(90, 50), icon=Icon.SCIPION_ICON_PROJ, _class=self.projName)
|
@@ -131,12 +138,30 @@ class ProjectWindow(ProjectBaseWindow):
|
|
131
138
|
|
132
139
|
ProjectWorkflowNotifier(self.project).notifyWorkflow()
|
133
140
|
|
141
|
+
|
134
142
|
def createHeaderFrame(self, parent):
|
135
143
|
"""Create the header and add the view selection frame at the right."""
|
136
144
|
header = ProjectBaseWindow.createHeaderFrame(self, parent)
|
137
145
|
self.addViewList(header)
|
138
146
|
return header
|
139
147
|
|
148
|
+
def customizeheader(self, headerFrame):
|
149
|
+
""" Adds the Project name in the header frame"""
|
150
|
+
# Create the Project Name label
|
151
|
+
|
152
|
+
projLabel = Label(headerFrame, text=self.projName, font=getBigFont(),
|
153
|
+
borderwidth=0, anchor='nw', bg=pw.Config.SCIPION_BG_COLOR,
|
154
|
+
fg=pw.Color.ALT_COLOR_DARK)
|
155
|
+
projLabel.bind("<Button-1>", self.setComment)
|
156
|
+
projLabel.grid(row=0, column=2, sticky='sw', padx=(20, 5), pady=10)
|
157
|
+
|
158
|
+
self.commentTT = ToolTip(projLabel, self.project.getComment(), 200)
|
159
|
+
def setComment(self, e):
|
160
|
+
|
161
|
+
newComment = askString("Change project description", "Description", self.root, entryWidth=100, defaultValue=self.project.getComment())
|
162
|
+
self.commentTT.configure(text=newComment)
|
163
|
+
self.project.setComment(newComment)
|
164
|
+
self.project._storeCreationTime() # Comment is stored as creation time comment for now
|
140
165
|
def getSettings(self):
|
141
166
|
return self.settings
|
142
167
|
|
@@ -334,30 +359,6 @@ class ProjectWindow(ProjectBaseWindow):
|
|
334
359
|
|
335
360
|
except Exception as ex:
|
336
361
|
logger.error("There was an error executing object command !!!:", exc_info=ex)
|
337
|
-
|
338
|
-
def recalculateCTF(self, inputObjId, sqliteFile):
|
339
|
-
""" Load the project and launch the protocol to
|
340
|
-
create the subset.
|
341
|
-
"""
|
342
|
-
# Retrieve project, input protocol and object from db
|
343
|
-
project = self.project
|
344
|
-
inputObj = project.mapper.selectById(int(inputObjId))
|
345
|
-
parentProtId = inputObj.getObjParentId()
|
346
|
-
parentProt = project.mapper.selectById(parentProtId)
|
347
|
-
protDep = project._getProtocolsDependencies([parentProt])
|
348
|
-
if protDep:
|
349
|
-
prot = project.copyProtocol(parentProt)
|
350
|
-
prot.continueRun.set(parentProt)
|
351
|
-
else:
|
352
|
-
prot = parentProt
|
353
|
-
prot.isFirstTime.set(True)
|
354
|
-
|
355
|
-
# Define the input params of the new protocol
|
356
|
-
prot.recalculate.set(True)
|
357
|
-
prot.sqliteFile.set(sqliteFile)
|
358
|
-
# Launch the protocol
|
359
|
-
self.getViewWidget().executeProtocol(prot)
|
360
|
-
|
361
362
|
|
362
363
|
class ProjectManagerWindow(ProjectBaseWindow):
|
363
364
|
""" Windows to manage all projects. """
|
@@ -466,6 +467,13 @@ class ProjectTCPRequestHandler(socketserver.BaseRequestHandler):
|
|
466
467
|
attr.set(obj)
|
467
468
|
elif value:
|
468
469
|
attr.set(value)
|
470
|
+
|
471
|
+
if protocol.useQueue():
|
472
|
+
# Do not use the queue in this case otherwise we need to ask for queue parameters.
|
473
|
+
# Maybe something to do in the future. But now this logic is in form.py.
|
474
|
+
logger.warning('Cancelling launching protocol "%s" to the queue.' % protocol)
|
475
|
+
protocol._useQueue.set(False)
|
476
|
+
|
469
477
|
# project.launchProtocol(protocol)
|
470
478
|
# We need to enqueue the action of execute a new protocol
|
471
479
|
# to be run in the same GUI thread and avoid concurrent
|
pyworkflow/gui/project/steps.py
CHANGED
@@ -30,7 +30,6 @@ import pyworkflow.protocol as pwprot
|
|
30
30
|
import pyworkflow.gui as pwgui
|
31
31
|
import pyworkflow.utils as pwutils
|
32
32
|
|
33
|
-
|
34
33
|
from pyworkflow import TK
|
35
34
|
from pyworkflow.utils import Icon
|
36
35
|
|
@@ -59,11 +58,17 @@ class StepsTreeProvider(pwgui.tree.TreeProvider):
|
|
59
58
|
return info
|
60
59
|
|
61
60
|
@staticmethod
|
62
|
-
def getObjectPreview(obj):
|
61
|
+
def getObjectPreview(obj: pwprot.Step):
|
63
62
|
|
64
63
|
args = json.loads(obj.argsStr.get())
|
65
64
|
msg = "*Prerequisites*: %s \n" % str(obj._prerequisites)
|
66
|
-
|
65
|
+
|
66
|
+
msg += ("*Arguments*:\n")
|
67
|
+
for arg in args:
|
68
|
+
msg += " %s\n" % arg
|
69
|
+
|
70
|
+
msg += "*Needs GPU*: %s" % obj.needsGPU()
|
71
|
+
|
67
72
|
if hasattr(obj, 'resultFiles'):
|
68
73
|
results = json.loads(obj.resultFiles.get())
|
69
74
|
if len(results):
|
@@ -55,7 +55,7 @@ def populateTree(tree, elements, parentId=''):
|
|
55
55
|
t += ' (%d)' % node.count
|
56
56
|
node.nodeId = tree.insert(parentId, 'end', node.getName(),
|
57
57
|
text=t, tags=DATA_TAG)
|
58
|
-
populateTree(tree, node.
|
58
|
+
populateTree(tree, node.getChildren(), node.nodeId)
|
59
59
|
if node.count:
|
60
60
|
tree.see(node.nodeId)
|
61
61
|
tree.item(node.nodeId, tags=('non-empty', DATA_TAG))
|
@@ -808,7 +808,7 @@ class ProtocolsView(tk.Frame):
|
|
808
808
|
item.margin = 0
|
809
809
|
|
810
810
|
# Paint the oval..if apply.
|
811
|
-
self._paintOval(item, statusColor)
|
811
|
+
#self._paintOval(item, statusColor)
|
812
812
|
|
813
813
|
# Paint the bottom line (for now only labels are painted there).
|
814
814
|
self._paintBottomLine(item)
|
@@ -821,7 +821,7 @@ class ProtocolsView(tk.Frame):
|
|
821
821
|
try:
|
822
822
|
|
823
823
|
# If the color has to go to the box
|
824
|
-
if self.settings.statusColorMode():
|
824
|
+
if self.settings.statusColorMode() or self.settings.labelsColorMode():
|
825
825
|
boxColor = statusColor
|
826
826
|
|
827
827
|
elif self.settings.ageColorMode():
|
@@ -931,7 +931,7 @@ class ProtocolsView(tk.Frame):
|
|
931
931
|
|
932
932
|
def _paintBottomLine(self, item):
|
933
933
|
|
934
|
-
if self.settings.labelsColorMode():
|
934
|
+
if self.settings.labelsColorMode() or self.settings.statusColorMode():
|
935
935
|
self._addLabels(item)
|
936
936
|
|
937
937
|
def _paintOval(self, item, statusColor):
|
@@ -967,7 +967,7 @@ class ProtocolsView(tk.Frame):
|
|
967
967
|
nodeText = nodeText[:37] + "..."
|
968
968
|
|
969
969
|
if node.run:
|
970
|
-
expandedStr = '' if node.expanded else '\n ➕ %s more' % str(node.
|
970
|
+
expandedStr = '' if node.expanded else '\n ➕ %s more' % str(node.countChildren({}))
|
971
971
|
if self.runsView == VIEW_TREE_SMALL:
|
972
972
|
nodeText = node.getName() + expandedStr
|
973
973
|
else:
|
@@ -978,7 +978,7 @@ class ProtocolsView(tk.Frame):
|
|
978
978
|
|
979
979
|
def _addLabels(self, item):
|
980
980
|
# If there is only one label it should be already used in the box color.
|
981
|
-
if self._getLabelsCount(item.nodeInfo) <
|
981
|
+
if self._getLabelsCount(item.nodeInfo) < 1:
|
982
982
|
return
|
983
983
|
# Get the positions of the box
|
984
984
|
(topLeftX, topLeftY, bottomRightX,
|
@@ -1056,7 +1056,7 @@ class ProtocolsView(tk.Frame):
|
|
1056
1056
|
nextColorMode = currentMode + 1
|
1057
1057
|
|
1058
1058
|
self.settings.setColorMode(nextColorMode)
|
1059
|
-
self._updateActionToolbar()
|
1059
|
+
# WHY? self._updateActionToolbar()
|
1060
1060
|
# self.updateRunsGraph()
|
1061
1061
|
self.drawRunsGraph()
|
1062
1062
|
self._infoAboutColorScheme()
|
@@ -1283,7 +1283,7 @@ class ProtocolsView(tk.Frame):
|
|
1283
1283
|
tm += 'Status: %s\n' % prot.getStatusMessage()
|
1284
1284
|
tm += 'Wall time: %s\n' % pwutils.prettyDelta(prot.getElapsedTime())
|
1285
1285
|
tm += 'CPU time: %s\n' % pwutils.prettyDelta(dt.timedelta(seconds=prot.cpuTime))
|
1286
|
-
tm += 'Folder size: %s\n' % pwutils.prettySize(prot.getSize())
|
1286
|
+
# tm += 'Folder size: %s\n' % pwutils.prettySize(prot.getSize())
|
1287
1287
|
|
1288
1288
|
if not hasattr(tw, 'tooltipText'):
|
1289
1289
|
frame = tk.Frame(tw)
|
@@ -1805,7 +1805,7 @@ class ProtocolsView(tk.Frame):
|
|
1805
1805
|
# Go in the direction .
|
1806
1806
|
for run in nodesToSelect:
|
1807
1807
|
# Choose the direction: down or up.
|
1808
|
-
direction = run.
|
1808
|
+
direction = run.getChildren if down else run.getParents
|
1809
1809
|
|
1810
1810
|
# Select himself plus ancestors
|
1811
1811
|
for parent in direction():
|
@@ -2054,7 +2054,7 @@ class ProtocolsView(tk.Frame):
|
|
2054
2054
|
|
2055
2055
|
def setVisibleNodes(self, node, visible=True):
|
2056
2056
|
hasParentHidden = False
|
2057
|
-
for child in node.
|
2057
|
+
for child in node.getChildren():
|
2058
2058
|
prot = child.run
|
2059
2059
|
nodeInfo = self.settings.getNodeById(prot.getObjId())
|
2060
2060
|
if visible:
|
@@ -245,7 +245,7 @@ class RunIOTreeProvider(pwgui.tree.TreeProvider):
|
|
245
245
|
if obj is None or not obj.hasValue():
|
246
246
|
return None
|
247
247
|
|
248
|
-
if isinstance(obj, pwobj.String):
|
248
|
+
if isinstance(obj, pwobj.String) and not obj.getName():
|
249
249
|
info = stringToInfo()
|
250
250
|
else:
|
251
251
|
# All attributes are considered output, unless they are pointers
|
@@ -504,7 +504,7 @@ class ProtocolTreeConfig:
|
|
504
504
|
|
505
505
|
except Exception as e:
|
506
506
|
print('Failed to read settings. The reported error was:\n %s\n'
|
507
|
-
'To solve it, fix %s and run again.' % e)
|
507
|
+
'To solve it, fix %s and run again.' % (e, pluginName))
|
508
508
|
|
509
509
|
# Clean empty sections
|
510
510
|
cls._hideEmptySections(protocols)
|
@@ -557,4 +557,4 @@ class ProtocolConfig(MenuConfig):
|
|
557
557
|
return MenuConfig.addSubMenu(self, text, value, **args)
|
558
558
|
|
559
559
|
def __str__(self):
|
560
|
-
return self.text
|
560
|
+
return self.text
|
pyworkflow/gui/widgets.py
CHANGED
@@ -161,8 +161,8 @@ class Scrollable(object):
|
|
161
161
|
widget.bind("<Button-4>", self.scrollV)
|
162
162
|
widget.bind("<Button-5>", self.scrollV)
|
163
163
|
|
164
|
-
widget.bind("<
|
165
|
-
widget.bind("<
|
164
|
+
widget.bind("<Shift-Button-4>", self.scrollH)
|
165
|
+
widget.bind("<Shift-Button-5>", self.scrollH)
|
166
166
|
|
167
167
|
|
168
168
|
def getVScroll(self):
|
pyworkflow/mapper/sqlite.py
CHANGED
@@ -27,12 +27,12 @@ logger = logging.getLogger(__name__)
|
|
27
27
|
import re
|
28
28
|
from collections import OrderedDict
|
29
29
|
|
30
|
-
from pyworkflow import Config
|
30
|
+
from pyworkflow import Config, ID_ATTRIBUTE, ID_COLUMN
|
31
31
|
from pyworkflow.utils import replaceExt, joinExt, valueToList
|
32
32
|
from .sqlite_db import SqliteDb, OperationalError
|
33
33
|
from .mapper import Mapper
|
34
34
|
|
35
|
-
ID =
|
35
|
+
ID = ID_COLUMN
|
36
36
|
CREATION = 'creation'
|
37
37
|
PARENT_ID = 'parent_id'
|
38
38
|
CLASSNAME = 'classname'
|
@@ -210,7 +210,7 @@ class SqliteMapper(Mapper):
|
|
210
210
|
rowId = objRow[ID]
|
211
211
|
rowName = self._getStrValue(objRow['name'])
|
212
212
|
|
213
|
-
if not hasattr(obj,
|
213
|
+
if not hasattr(obj, ID_ATTRIBUTE):
|
214
214
|
raise Exception("Entry '%s' (id=%s) in the database, stored as '%s'"
|
215
215
|
", is being mapped to %s object. " %
|
216
216
|
(rowName, rowId,
|
@@ -1356,8 +1356,8 @@ class SqliteFlatDb(SqliteDb):
|
|
1356
1356
|
def addCommonFieldsToMap(self):
|
1357
1357
|
|
1358
1358
|
# Add common fields to the mapping
|
1359
|
-
self._columnsMapping[
|
1360
|
-
self._columnsMapping[
|
1359
|
+
self._columnsMapping[ID_COLUMN] = ID_COLUMN
|
1360
|
+
self._columnsMapping[ID_ATTRIBUTE] = ID_COLUMN
|
1361
1361
|
|
1362
1362
|
def getClassRows(self):
|
1363
1363
|
""" Create a dictionary with names of the attributes
|
pyworkflow/object.py
CHANGED
@@ -66,7 +66,7 @@ class Object(object):
|
|
66
66
|
self._objId = None # Unique identifier of this object in some context
|
67
67
|
self._objParentId = None # identifier of the parent object
|
68
68
|
self._objName = '' # The name of the object will contains the whole path of ancestors
|
69
|
-
self._objLabel =
|
69
|
+
self._objLabel = '' # This will serve to label the objects
|
70
70
|
self._objComment = ''
|
71
71
|
# Performance: self._objTag = None # This attribute serve to make some annotation on the object.
|
72
72
|
self._objDoStore = True
|
pyworkflow/plugin.py
CHANGED
@@ -124,6 +124,7 @@ class Domain:
|
|
124
124
|
(pwutils.yellow("WARNING!!: Plugin containing module %s does not import properly. "
|
125
125
|
"All its content will be missing in this execution." % name))
|
126
126
|
logger.info("Please, contact developers at %s and send this ugly information below. They'll understand it!." % DOCSITEURLS.CONTACTUS)
|
127
|
+
logger.info("Error message: %s"% str(e))
|
127
128
|
logger.info(pwutils.yellow(traceback.format_exc()))
|
128
129
|
|
129
130
|
@classmethod
|
pyworkflow/project/config.py
CHANGED
@@ -40,7 +40,7 @@ class ProjectSettings(pwobj.Object):
|
|
40
40
|
COLOR_MODE_LABELS = 1
|
41
41
|
COLOR_MODE_AGE = 2
|
42
42
|
COLOR_MODE_SIZE = 3
|
43
|
-
COLOR_MODES = (COLOR_MODE_STATUS, COLOR_MODE_LABELS, COLOR_MODE_AGE, COLOR_MODE_SIZE)
|
43
|
+
COLOR_MODES = (COLOR_MODE_STATUS, COLOR_MODE_LABELS, COLOR_MODE_AGE) # This has poor performance many cases, COLOR_MODE_SIZE)
|
44
44
|
|
45
45
|
def __init__(self, confs={}, **kwargs):
|
46
46
|
super().__init__(**kwargs)
|
@@ -112,6 +112,9 @@ class ProjectSettings(pwobj.Object):
|
|
112
112
|
def setColorMode(self, colorMode):
|
113
113
|
""" Set the color mode to use when drawing the graph.
|
114
114
|
"""
|
115
|
+
# Skip LABELS color mode to avoid double iteration
|
116
|
+
if colorMode == self.COLOR_MODE_LABELS:
|
117
|
+
colorMode+=1
|
115
118
|
self.colorMode.set(colorMode)
|
116
119
|
|
117
120
|
def statusColorMode(self):
|
pyworkflow/project/manager.py
CHANGED
@@ -97,9 +97,9 @@ class Manager(object):
|
|
97
97
|
return projList
|
98
98
|
|
99
99
|
def createProject(self, projectName, runsView=1,
|
100
|
-
hostsConf=None, protocolsConf=None, location=None):
|
100
|
+
hostsConf=None, protocolsConf=None, location=None, comment=None):
|
101
101
|
"""Create a new project.
|
102
|
-
confs dict can
|
102
|
+
confs dict can contain customs .conf files
|
103
103
|
for: menus, protocols, or hosts
|
104
104
|
"""
|
105
105
|
# Clean project name from undesired characters
|
@@ -118,7 +118,8 @@ class Manager(object):
|
|
118
118
|
project = Project(pw.Config.getDomain(), projectPath)
|
119
119
|
project.create(runsView=runsView,
|
120
120
|
hostsConf=hostsConf,
|
121
|
-
protocolsConf=protocolsConf
|
121
|
+
protocolsConf=protocolsConf,
|
122
|
+
comment=comment)
|
122
123
|
# If location is not the default one create a symlink on self.PROJECTS directory
|
123
124
|
if projectPath != self.getProjectPath(projectName):
|
124
125
|
# JMRT: Let's create the link to the absolute path, since relative
|