scipion-pyworkflow 3.8.0__py3-none-any.whl → 3.9.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.
Files changed (38) hide show
  1. pyworkflow/config.py +8 -2
  2. pyworkflow/constants.py +6 -1
  3. pyworkflow/gui/canvas.py +6 -6
  4. pyworkflow/gui/dialog.py +3 -2
  5. pyworkflow/gui/form.py +12 -2
  6. pyworkflow/gui/graph.py +6 -6
  7. pyworkflow/gui/graph_layout.py +2 -2
  8. pyworkflow/gui/project/base.py +7 -7
  9. pyworkflow/gui/project/project.py +36 -28
  10. pyworkflow/gui/project/steps.py +8 -3
  11. pyworkflow/gui/project/viewdata.py +1 -1
  12. pyworkflow/gui/project/viewprotocols.py +9 -9
  13. pyworkflow/gui/project/viewprotocols_extra.py +3 -3
  14. pyworkflow/gui/widgets.py +2 -2
  15. pyworkflow/mapper/sqlite.py +5 -5
  16. pyworkflow/object.py +1 -1
  17. pyworkflow/plugin.py +1 -0
  18. pyworkflow/project/config.py +4 -1
  19. pyworkflow/project/manager.py +4 -3
  20. pyworkflow/project/project.py +36 -20
  21. pyworkflow/project/scripts/create.py +14 -4
  22. pyworkflow/project/scripts/schedule.py +1 -1
  23. pyworkflow/protocol/constants.py +1 -0
  24. pyworkflow/protocol/executor.py +133 -19
  25. pyworkflow/protocol/params.py +5 -8
  26. pyworkflow/protocol/protocol.py +36 -36
  27. pyworkflow/template.py +1 -1
  28. pyworkflow/utils/graph.py +24 -51
  29. pyworkflow/utils/properties.py +16 -12
  30. pyworkflowtests/protocols.py +3 -3
  31. pyworkflowtests/tests/test_protocol_execution.py +63 -0
  32. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/METADATA +1 -1
  33. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/RECORD +38 -38
  34. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/WHEEL +1 -1
  35. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/LICENSE.txt +0 -0
  36. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/dependency_links.txt +0 -0
  37. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.dist-info}/entry_points.txt +0 -0
  38. {scipion_pyworkflow-3.8.0.dist-info → scipion_pyworkflow-3.9.0.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', _join(getResourcesPath(),'sprites.png'),
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.8.0'
46
+ VERSION_3_0 = '3.9.0'
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("<Shift-Button-4>", self.zoomerP)
86
- self.bind("<Shift-Button-5>", self.zoomerM)
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.getChilds():
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.getChilds():
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.getChilds():
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.getChilds():
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.LABEL_THREADS,
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
- pwutils.Message.HELP_MPI_THREADS))
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.getChilds():
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.getChilds() if c.parent is 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.getChilds() if c.parent is 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.getChilds():
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.getChilds():
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.getChilds():
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)
@@ -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.getChilds():
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.getChilds() if c._layout['parent'] is 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
 
@@ -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
- # Create the Project Name label
102
- projName = getattr(self, 'projName', '')
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.projName = os.path.basename(path)
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
@@ -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
- msg += "*Arguments*: " + '\n '.join([str(a) for a in args])
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.getChilds(), node.nodeId)
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.countChilds({}))
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) < 2:
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.getChilds if down else run.getParents
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.getChilds():
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("<Control-Button-4>", self.scrollH)
165
- widget.bind("<Control-Button-5>", self.scrollH)
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):
@@ -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 = '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, '_objId'):
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["id"] = "id"
1360
- self._columnsMapping["_objId"] = "id"
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 = '' # This will serve to label the objects
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
@@ -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):
@@ -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 contains customs .conf files
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