scipion-pyworkflow 3.10.6__py3-none-any.whl → 3.11.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.
Files changed (57) hide show
  1. pyworkflow/config.py +131 -67
  2. pyworkflow/constants.py +2 -1
  3. pyworkflow/gui/browser.py +39 -5
  4. pyworkflow/gui/dialog.py +2 -0
  5. pyworkflow/gui/form.py +141 -52
  6. pyworkflow/gui/gui.py +8 -8
  7. pyworkflow/gui/project/project.py +6 -7
  8. pyworkflow/gui/project/searchprotocol.py +91 -7
  9. pyworkflow/gui/project/viewdata.py +1 -1
  10. pyworkflow/gui/project/viewprotocols.py +45 -22
  11. pyworkflow/gui/project/viewprotocols_extra.py +9 -6
  12. pyworkflow/gui/widgets.py +2 -2
  13. pyworkflow/mapper/sqlite.py +4 -4
  14. pyworkflow/plugin.py +93 -44
  15. pyworkflow/project/project.py +158 -70
  16. pyworkflow/project/usage.py +165 -0
  17. pyworkflow/protocol/executor.py +30 -18
  18. pyworkflow/protocol/hosts.py +9 -6
  19. pyworkflow/protocol/launch.py +15 -8
  20. pyworkflow/protocol/params.py +59 -19
  21. pyworkflow/protocol/protocol.py +124 -58
  22. pyworkflow/resources/showj/arrowDown.png +0 -0
  23. pyworkflow/resources/showj/arrowUp.png +0 -0
  24. pyworkflow/resources/showj/background_section.png +0 -0
  25. pyworkflow/resources/showj/colRowModeOff.png +0 -0
  26. pyworkflow/resources/showj/colRowModeOn.png +0 -0
  27. pyworkflow/resources/showj/delete.png +0 -0
  28. pyworkflow/resources/showj/doc_icon.png +0 -0
  29. pyworkflow/resources/showj/download_icon.png +0 -0
  30. pyworkflow/resources/showj/enabled_gallery.png +0 -0
  31. pyworkflow/resources/showj/galleryViewOff.png +0 -0
  32. pyworkflow/resources/showj/galleryViewOn.png +0 -0
  33. pyworkflow/resources/showj/goto.png +0 -0
  34. pyworkflow/resources/showj/menu.png +0 -0
  35. pyworkflow/resources/showj/separator.png +0 -0
  36. pyworkflow/resources/showj/tableViewOff.png +0 -0
  37. pyworkflow/resources/showj/tableViewOn.png +0 -0
  38. pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  39. pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  40. pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  41. pyworkflow/resources/showj/volumeOff.png +0 -0
  42. pyworkflow/resources/showj/volumeOn.png +0 -0
  43. pyworkflow/utils/log.py +15 -6
  44. pyworkflow/utils/properties.py +78 -92
  45. pyworkflow/utils/utils.py +3 -2
  46. pyworkflow/viewer.py +23 -1
  47. pyworkflow/webservices/config.py +0 -3
  48. pyworkflow/webservices/notifier.py +24 -34
  49. pyworkflowtests/protocols.py +1 -3
  50. pyworkflowtests/tests/test_protocol_execution.py +4 -0
  51. {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.1.dist-info}/METADATA +13 -27
  52. {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.1.dist-info}/RECORD +56 -35
  53. {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.1.dist-info}/WHEEL +1 -1
  54. scipion_pyworkflow-3.10.6.dist-info/dependency_links.txt +0 -1
  55. {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.1.dist-info}/entry_points.txt +0 -0
  56. {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.1.dist-info}/licenses/LICENSE.txt +0 -0
  57. {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.1.dist-info}/top_level.txt +0 -0
pyworkflow/config.py CHANGED
@@ -97,12 +97,15 @@ class Variable:
97
97
  self.value = new_value
98
98
  self.isDefault= self._isValueDefault()
99
99
  def _isValueDefault(self):
100
- return self.value==self.default
100
+ return self.value == self.default
101
+
102
+
101
103
  class VariablesRegistry:
102
- _variables={}
104
+ _variables = {}
103
105
 
104
106
  def __init__(self):
105
107
  raise RuntimeError("Variables class doesn't need to be instantiated.")
108
+
106
109
  @classmethod
107
110
  def register(cls, variable: Variable):
108
111
  cls._variables[variable.name] = variable
@@ -146,7 +149,7 @@ class Config:
146
149
 
147
150
  if key in os.environ:
148
151
  value = os.environ.get(key)
149
- isDefault = (value==default)
152
+ isDefault = (value == default)
150
153
  else:
151
154
  isDefault = True
152
155
  value = default
@@ -154,10 +157,10 @@ class Config:
154
157
  # If the caster is passed do the casting, if fails go back to default
155
158
  if caster:
156
159
  try:
157
- value=caster(value)
160
+ value = caster(value)
158
161
  except:
159
162
  logger.warning("Variable %s has this value %s that can't be casted to the right type (%s). Using %s (default value)" %
160
- (key,value, caster, default))
163
+ (key, value, caster, default))
161
164
  value = default
162
165
  # If empty use default value
163
166
  if value == "" != default:
@@ -168,10 +171,23 @@ class Config:
168
171
  if isinstance(value, str):
169
172
  value = os.path.expandvars(os.path.expanduser(value))
170
173
 
171
- # Register the variable
174
+ # Register the variable. Boolean variables are converted to booleans after this call. May not be accurate.
175
+ # 1 turns into False in the Config
172
176
  VariablesRegistry.register(Variable(key,description, source, value, default, var_type=var_type, isDefault=isDefault))
173
177
  return value
174
178
 
179
+ @staticmethod
180
+ def __notFalse(value):
181
+ return value != FALSE_STR
182
+
183
+ @staticmethod
184
+ def __notTrue(value):
185
+ return value != TRUE_STR
186
+
187
+ @staticmethod
188
+ def __bool(value):
189
+ return value.lower() in TRUE_YES_ON_
190
+
175
191
  class Root:
176
192
  """ Simple helper to return path from a root. """
177
193
 
@@ -185,13 +201,17 @@ class Config:
185
201
  # join will not join if expanded is absolute
186
202
  return os.path.join(self._root, expanded)
187
203
 
188
- # Home for scipion
189
204
  _get = __get.__func__
190
- SCIPION_HOME = os.path.abspath(_get(SCIPION_HOME_VAR, '',
191
- "Path where Scipion is installed. Other paths are based on this like SCIPION_SOFTWARE, SCIPION_TESTS,... unless specified"))
205
+ _notFalse = __notFalse.__func__
206
+ __bool = __bool.__func__
207
+
208
+ # Home for scipion
209
+ _home_var = _get(SCIPION_HOME_VAR, '',
210
+ "Path where Scipion is installed. Other paths are based on this like SCIPION_SOFTWARE, SCIPION_TESTS,... unless specified")
211
+ SCIPION_HOME = os.path.abspath(_home_var)
192
212
 
193
213
  # False if SCIPION_HOME is not found in the environment. To distinguish API documentation generation execution.
194
- SCIPION_HOME_DEFINED = SCIPION_HOME != ''
214
+ SCIPION_HOME_DEFINED = _home_var != ''
195
215
 
196
216
  _root = Root(str(SCIPION_HOME))
197
217
  _join = _root.join
@@ -201,68 +221,75 @@ class Config:
201
221
  __defaultSpritesFile = _join(getResourcesPath(),'sprites.png')
202
222
 
203
223
  CONDA_ACTIVATION_CMD = _get(CONDA_ACTIVATION_CMD_VAR,'',
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)\"")
224
+ "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)\"")
205
225
 
206
226
  # SCIPION PATHS
207
227
  SCIPION_SOFTWARE = _get('SCIPION_SOFTWARE', _join('software'),
208
- "Path where Scipion will install the software. Defaults to SCIPION_HOME/software.", var_type=VarTypes.FOLDER)
228
+ "Path where Scipion will install the software. Defaults to SCIPION_HOME/software.", var_type=VarTypes.FOLDER)
209
229
 
210
230
  SCIPION_TESTS = _get('SCIPION_TESTS', _join('data', 'tests'),
211
- "Path where to find/download test data. Defaults to SCIPION_HOME/data/tests.", var_type=VarTypes.FOLDER)
231
+ "Path where to find/download test data. Defaults to SCIPION_HOME/data/tests.", var_type=VarTypes.FOLDER)
212
232
 
213
233
  # User dependent paths
214
234
  SCIPION_USER_DATA = _get('SCIPION_USER_DATA', '~/ScipionUserData',
215
- "Path where Scipion projects are or will be created. Defaults to ~/ScipionUserData", var_type=VarTypes.FOLDER)
235
+ "Path where Scipion projects are or will be created. Defaults to ~/ScipionUserData", var_type=VarTypes.FOLDER)
216
236
 
217
237
  # LOGGING variables
218
238
  SCIPION_LOGS = _get('SCIPION_LOGS', _join(SCIPION_USER_DATA, 'logs'),
219
- "Folder for Scipion logs used by the GUI. Defaults to SCIPION_USER_DATA/logs.", var_type=VarTypes.FOLDER)
239
+ "Folder for Scipion logs used by the GUI. Defaults to SCIPION_USER_DATA/logs.", var_type=VarTypes.FOLDER)
220
240
 
221
241
  SCIPION_LOG_CONFIG = _get('SCIPION_LOG_CONFIG', None,
222
- "Optional. Path to a python logging configuration file to fine tune the logging.", var_type=VarTypes.PATH)
242
+ "Optional. Path to a python logging configuration file to fine tune the logging.", var_type=VarTypes.PATH)
223
243
 
224
244
  SCIPION_LOG = _get('SCIPION_LOG', _join(SCIPION_LOGS, 'scipion.log'),
225
- "Path to the file where scipion will write GUI logging messages. Defaults to SCIPION_LOGS/scipion.log", var_type=VarTypes.PATH)
245
+ "Path to the file where scipion will write GUI logging messages. Defaults to SCIPION_LOGS/scipion.log", var_type=VarTypes.PATH)
226
246
 
227
247
  SCIPION_LOG_FORMAT = _get('SCIPION_LOG_FORMAT', "%(message)s",
228
- "str: Format for all the log lines, defaults to %(message)s. To compose the format see https://docs.python.org/3/library/logging.html#logrecord-attributes")
248
+ "str: Format for all the log lines, defaults to %(message)s. To compose the format see https://docs.python.org/3/library/logging.html#logrecord-attributes")
229
249
 
230
250
  SCIPION_LOG_LEVEL = _get(SCIPION_LOG_LEVEL, 'INFO',
231
- "Default logging level. String among CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET. Default value is INFO.")
251
+ "Default logging level. String among CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET. Default value is INFO.")
232
252
 
233
253
  NO_COLOR = _get('NO_COLOR', '',
234
- "str: Comply with https://no-color.org/ initiative. Set it to something different than '' to deactivate colors in the output.")
254
+ "str: Comply with https://no-color.org/ initiative. Set it to something different than '' to deactivate colors in the output.")
235
255
 
236
256
  SCIPION_SCRATCH = _get(SCIPION_SCRATCH, None,
237
- "Optional. Path to a location mounted in a scratch drive (SSD,...)")
257
+ "Optional. Path to a location mounted in a scratch drive (SSD,...)")
238
258
 
239
259
  SCIPION_TESTS_OUTPUT = _get('SCIPION_TESTS_OUTPUT', _join(SCIPION_USER_DATA, 'Tests'),
240
- "Path to a folder where the output of the tests will be written. Defaults to SCIPION_USER_DATA/Tests.", var_type=VarTypes.FOLDER)
260
+ "Path to a folder where the output of the tests will be written. Defaults to SCIPION_USER_DATA/Tests.", var_type=VarTypes.FOLDER)
241
261
 
242
262
  SCIPION_TEST_NOSYNC = _get('SCIPION_TEST_NOSYNC', FALSE_STR,
243
- "Set it to 1, True, Yes or y to cancel test dataset synchronization. Needed when updating files in a dataset.") != FALSE_STR
263
+ "Set it to any value except False to cancel test dataset synchronization."
264
+ " Needed when updating files in a dataset.", caster=_notFalse)
244
265
 
245
266
  SCIPION_SUPPORT_EMAIL = 'scipion@cnb.csic.es'
246
267
 
247
268
  # Config variables
248
269
  SCIPION_CONFIG = _get('SCIPION_CONFIG', _join('config','scipion.conf'),
249
- "Path to the scipion configuration file where all this variables could be defined.", var_type=VarTypes.PATH)
270
+ "Path to the scipion configuration file where all this variables could be defined.", var_type=VarTypes.PATH)
250
271
 
251
272
  SCIPION_LOCAL_CONFIG = _get('SCIPION_LOCAL_CONFIG', SCIPION_CONFIG,
252
- "Path to an optional/extra/user configuration file meant to overwrite default variables.", var_type=VarTypes.PATH)
273
+ "Path to an optional/extra/user configuration file meant to overwrite default variables.", var_type=VarTypes.PATH)
253
274
 
254
275
  SCIPION_HOSTS = _get('SCIPION_HOSTS', _join('config','hosts.conf'),
255
- "Path to the host.cof file to allow scipion to use queue engines and run in HPC environments.")
276
+ "Path to the host.cof file to allow scipion to use queue engines and run in HPC environments.")
256
277
 
257
278
  SCIPION_PROTOCOLS = _get('SCIPION_PROTOCOLS', _join('config','protocols.conf'),
258
- "Custom conf file to extend the protocols tree view panel (panel on the left)")
279
+ "Custom conf file to extend the protocols tree view panel (panel on the left)")
259
280
 
260
281
  SCIPION_PLUGIN_JSON = _get('SCIPION_PLUGIN_JSON', None,
261
- "Optional. Path to get the json file with all the plugins available for Scipion.")
282
+ "Optional. Path to get the json file with all the plugins available for Scipion.")
283
+
284
+ SCIPION_SITE = _get('SCIPION_SITE', 'https://scipion.i2pc.es',
285
+ "Scipion site URL.")
286
+ SCIPION_SITE_API = SCIPION_SITE + '/report_protocols/api/v2'
287
+ SCIPION_STATS_WORKFLOW_APP = SCIPION_SITE_API + '/workflow/'
288
+ SCIPION_STATS_SUGGESTION = SCIPION_SITE_API + '/nextprotocol/suggestion/%s'
262
289
 
263
290
  SCIPION_PLUGIN_REPO_URL = _get('SCIPION_PLUGIN_REPO_URL',
264
- 'https://scipion.i2pc.es/getplugins/',
265
- "Url from where to get the list of plugins.")
291
+ SCIPION_SITE + '/getplugins/',
292
+ "Url from where to get the list of plugins.")
266
293
 
267
294
  # REMOTE Section
268
295
  SCIPION_URL = 'https://scipion.cnb.csic.es/downloads/scipion'
@@ -271,65 +298,69 @@ class Config:
271
298
 
272
299
  # Scipion Notes
273
300
  SCIPION_NOTES_FILE = _get(SCIPION_NOTES_FILE, 'notes.txt',
274
- "Name of the file where to write per project notes.")
301
+ "Name of the file where to write per project notes.")
275
302
 
276
303
  SCIPION_NOTES_PROGRAM = _get(SCIPION_NOTES_PROGRAM, None,
277
- "Command or program to use to open the notes file. Otherwise system will extension association will take place.")
304
+ "Command or program to use to open the notes file. Otherwise system will extension association will take place.")
278
305
 
279
306
  SCIPION_NOTES_ARGS = _get(SCIPION_NOTES_ARGS, None)
280
307
 
281
308
  # External text editor:
282
309
  SCIPION_TEXT_EDITOR = _get(SCIPION_TEXT_EDITOR, '',
283
- "Preferred text editor executable.", caster=str)
310
+ "Preferred text editor executable.", caster=str)
284
311
 
285
312
  # Aspect
286
313
  SCIPION_FONT_NAME = _get('SCIPION_FONT_NAME', "Helvetica",
287
- "Name of the font to use in Scipion GUI. Defaults to Helvetica.")
314
+ "Name of the font to use in Scipion GUI. Defaults to Helvetica.")
288
315
 
289
- SCIPION_FONT_SIZE = _get('SCIPION_FONT_SIZE', SCIPION_DEFAULT_FONT_SIZE,
290
- "Size of the 'normal' font to be used in Scipion GUI. Defaults to 10.", caster=int)
316
+ SCIPION_FONT_SIZE = _get('SCIPION_FONT_SIZE', str(SCIPION_DEFAULT_FONT_SIZE),
317
+ "Size of the 'normal' font to be used in Scipion GUI. "
318
+ "Defaults to 10.", caster=int)
291
319
 
292
320
  SCIPION_MAIN_COLOR = _get('SCIPION_MAIN_COLOR', Color.MAIN_COLOR,
293
- "str: Main color of the GUI. Background will be white, so for better contrast choose a dark color. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
321
+ "str: Main color of the GUI. Background will be white, so for better contrast choose a dark color. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
294
322
  caster=validColor)
295
323
 
296
324
  SCIPION_BG_COLOR = _get('SCIPION_BG_COLOR', Color.BG_COLOR,
297
- "str: Main background color of the GUI. Default is white, chose a light one. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
325
+ "str: Main background color of the GUI. Default is white, chose a light one. Probably any name here will work: https://matplotlib.org/stable/gallery/color/named_colors.html",
298
326
  validColor)
299
327
 
300
- SCIPION_CONTRAST_COLOR = _get('SCIPION_CONTRAST_COLOR', 'cyan',
301
- "Color used to highlight features over grayscaled images.", caster=validColor)
328
+ SCIPION_CONTRAST_COLOR = _get('SCIPION_CONTRAST_COLOR', 'cyan', "Color used to highlight features over "
329
+ "gray-scaled images.", caster=validColor)
302
330
 
303
331
  SCIPION_SPRITES_FILE = _get('SCIPION_SPRITES_FILE', __defaultSpritesFile,
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.")
332
+ "File (png) with the icons in a collage. Default is found at "
333
+ "pyworkflow/resources/sprites.png. And a GIMP file could be found at the same "
334
+ "folder in the github repo.")
305
335
 
306
- SCIPION_SHOW_TEXT_IN_TOOLBAR = _get('SCIPION_SHOW_TEXT_IN_TOOLBAR', TRUE_STR,
307
- "Define it to anything else except False to show the label of the icons. It will take more space.") == TRUE_STR
336
+ SCIPION_SHOW_TEXT_IN_TOOLBAR = _get('SCIPION_SHOW_TEXT_IN_TOOLBAR', TRUE_STR, "Define it to anything else except "
337
+ "%s to hide labels. It will take less space." % TRUE_YES_ON_, caster=__bool)
308
338
 
309
- SCIPION_ICON_ZOOM = _get('SCIPION_ICON_ZOOM', 50,
310
- "Define it to anything else except False to show the label of the icons. It will take more space.", var_type=VarTypes.INTEGER, caster=int)
339
+ SCIPION_ICON_ZOOM = _get('SCIPION_ICON_ZOOM', "50",
340
+ "Define it to any integer value(percentage) to increase/decrease the size of the icons.",
341
+ var_type=VarTypes.INTEGER, caster=int)
311
342
 
312
343
  # Notification
313
- SCIPION_NOTIFY = _get('SCIPION_NOTIFY', TRUE_STR,
314
- "If set to False, Scipion developers will know almost nothing about Scipion usage and will have less information to improve it.") == TRUE_STR
344
+ SCIPION_NOTIFY = _get('SCIPION_NOTIFY', TRUE_STR, "If set to anything except %s Scipion developers will know "
345
+ "nothing abut Scipion usage and will have less information to improve it." % TRUE_YES_ON_,
346
+ caster=__bool)
315
347
 
316
348
  # *** Execution variables ***
317
- SCIPION_CWD = _get('SCIPION_CWD', os.path.abspath(os.getcwd()),
318
- "Directory when scipion was launched")
349
+ SCIPION_CWD = _get('SCIPION_CWD', os.path.abspath(os.getcwd()), "Directory when scipion was launched")
319
350
 
320
351
  SCIPION_GUI_REFRESH_IN_THREAD = _get('SCIPION_GUI_REFRESH_IN_THREAD', FALSE_STR,
321
- "True to refresh the runs graph with a thread. Unstable.") != FALSE_STR
352
+ "True to refresh the runs graph with a thread. Unstable.") != FALSE_STR
322
353
 
323
354
  SCIPION_GUI_REFRESH_INITIAL_WAIT = _get("SCIPION_GUI_REFRESH_INITIAL_WAIT", 5,
324
- "Seconds to wait after a manual refresh", caster=int)
355
+ "Seconds to wait after a manual refresh", caster=int)
325
356
 
326
357
  SCIPION_GUI_CANCEL_AUTO_REFRESH = _get("SCIPION_GUI_CANCEL_AUTO_REFRESH",FALSE_STR,
327
- "Set it to True to cancel automatic refresh of the runs.") != FALSE_STR
358
+ "Set it to True to cancel automatic refresh of the runs.") != FALSE_STR
328
359
 
329
360
  # Cancel shutil fast copy. In GPFS, shutil.copy does fail when trying a fastcopy and does not
330
361
  # fall back on the slow copy. For legacy reasons None is also False.
331
362
  SCIPION_CANCEL_FASTCOPY = _get('SCIPION_CANCEL_FASTCOPY', FALSE_STR,
332
- "Cancel fast copy done by shutil (copying files) when it fails. Has happened in GPFS environments. Defaults to False. None is also False otherwise fastcopy is cancelled."
363
+ "Cancel fast copy done by shutil (copying files) when it fails. Has happened in GPFS environments. Defaults to False. None is also False otherwise fastcopy is cancelled."
333
364
  ) not in [NONE_STR, FALSE_STR]
334
365
 
335
366
  # Priority package list: This variable is used in the view protocols in
@@ -339,26 +370,40 @@ class Config:
339
370
  SCIPION_PRIORITY_PACKAGE_LIST = _get('SCIPION_PRIORITY_PACKAGE_LIST', EMPTY_STR)
340
371
 
341
372
  SCIPION_STEPS_CHECK_SEC = _get('SCIPION_STEPS_CHECK_SEC', 5,
342
- "Number of seconds to wait before checking if new input is available in streamified protocols.", caster=int)
373
+ "Number of seconds to wait before checking if new input is available in "
374
+ "streamified protocols.", caster=int)
343
375
 
344
376
  SCIPION_UPDATE_SET_ATTEMPTS = _get('SCIPION_UPDATE_SET_ATTEMPTS', 3,
345
- "Number of attempts to modify the protocol output before failing. The default value is 3", caster=int)
377
+ "Number of attempts to modify the protocol output before failing. "
378
+ "The default value is 3", caster=int)
346
379
 
347
380
  SCIPION_UPDATE_SET_ATTEMPT_WAIT = _get('SCIPION_UPDATE_SET_ATTEMPT_WAIT', 2,
348
- "Time in seconds to wait until the next attempt when checking new outputs. The default value is 2 seconds", caster=int)
381
+ "Time in seconds to wait until the next attempt when checking new outputs. "
382
+ "The default value is 2 seconds", caster=int)
383
+
384
+ SCIPION_UNLOAD_FORM_ON_SAVE = _get('SCIPION_UNLOAD_FORM_ON_SAVE', TRUE_STR,
385
+ "When a protocol is saved it is unloaded if all goes fine. Set it to false "
386
+ "otherwise to rescue old behaviour.", caster=__bool)
349
387
 
350
388
  SCIPION_USE_QUEUE = _get("SCIPION_USE_QUEUE", FALSE_STR,
351
- "Default value for using the queue. By default is False. ANY value will be True except and empty value. \"False\" or \"0\" will be True too.")!= FALSE_STR
389
+ "Default value for using the queue. By default is False. "
390
+ "ANY value will be True except and empty value. \"False\" or \"0\" "
391
+ "will be True too.") != FALSE_STR
352
392
 
353
393
  SCIPION_DEFAULT_EXECUTION_ACTION = _get('SCIPION_DEFAULT_EXECUTION_ACTION', DEFAULT_EXECUTION_ACTION_ASK,
354
- """Ask if you want to launch a single protocol or a sub-workflow. The default value is 1
355
- 1: Scipion always ask
356
- 2: Run a single protocol
357
- 3: Run a sub-workflow """, caster=int)
394
+ """Ask if you want to launch a single protocol or a sub-workflow. The default value is 1
395
+ 1: Scipion always ask
396
+ 2: Run a single protocol
397
+ 3: Run a sub-workflow """, caster=int)
358
398
 
359
399
  SCIPION_MAPPER_USE_TEMPLATE = _get('SCIPION_MAPPER_USE_TEMPLATE', TRUE_STR,
360
- "Set it to False to force instantiation for each item during sets iterations. Experimental. This penalize the iteration but avoids"
361
- "the use of .clone() ot the items.") == TRUE_STR
400
+ "Set it to False to force instantiation for each item during sets iterations. Experimental. This penalize the iteration but avoids"
401
+ "the use of .clone() ot the items.") == TRUE_STR
402
+
403
+ CUDA_DEVICE_ORDER = _get('CUDA_DEVICE_ORDER', 'PCI_BUS_ID',
404
+ "To make GPU ID match what you see in nvidia-smi, that is the PCI ID."
405
+ " Use FASTEST_FIRST for default behaviour but this may not match what is returned by nvidia-smi."
406
+ " See https://docs.nvidia.com/cuda/cuda-c-programming-guide/#:~:text=in%20device%20memory.-,CUDA_DEVICE_ORDER,-FASTEST_FIRST%2C%20PCI_BUS_ID%2C%20(default ")
362
407
 
363
408
  try:
364
409
  VIEWERS = ast.literal_eval(_get('VIEWERS', "{}", "Json string to define which viewer are the default ones per output type."))
@@ -377,7 +422,8 @@ class Config:
377
422
  :return: Folder where libraries must be placed in case a binding needs them
378
423
  """
379
424
  lib = cls._join(cls.SCIPION_SOFTWARE, 'lib')
380
- os.makedirs(lib, exist_ok=True)
425
+ if cls.SCIPION_HOME_DEFINED:
426
+ os.makedirs(lib, exist_ok=True)
381
427
  return lib
382
428
 
383
429
  @classmethod
@@ -388,7 +434,8 @@ class Config:
388
434
  :return: The bindings folder
389
435
  """
390
436
  bindings = cls._join(cls.SCIPION_SOFTWARE, 'bindings')
391
- os.makedirs(bindings, exist_ok=True)
437
+ if cls.SCIPION_HOME_DEFINED:
438
+ os.makedirs(bindings, exist_ok=True)
392
439
  return bindings
393
440
 
394
441
  @classmethod
@@ -397,7 +444,8 @@ class Config:
397
444
  Folder where scipion logs must be placed. The folder is created
398
445
  """
399
446
  logsFolder = cls.SCIPION_LOGS
400
- os.makedirs(logsFolder, exist_ok=True)
447
+ if cls.SCIPION_HOME_DEFINED:
448
+ os.makedirs(logsFolder, exist_ok=True)
401
449
  return logsFolder
402
450
 
403
451
  @classmethod
@@ -455,8 +503,13 @@ class Config:
455
503
  debugOn = not Config.debugOn()
456
504
  os.environ[SCIPION_DEBUG] = str(debugOn)
457
505
  os.environ[SCIPION_DEBUG_NOCLEAN] = str(debugOn)
458
- os.environ[SCIPION_LOG_LEVEL] = "INFO" if not debugOn else "DEBUG"
459
506
 
507
+ newLevel = "DEBUG" if debugOn else "INFO"
508
+ os.environ[SCIPION_LOG_LEVEL] = newLevel
509
+
510
+ from pyworkflow.utils import changeLogLevel
511
+ changeLogLevel(newLevel)
512
+ logger.info("Log level set to %s" % newLevel)
460
513
  @staticmethod
461
514
  def debugSQLOn():
462
515
  from .utils import envVarOn
@@ -545,6 +598,17 @@ class Config:
545
598
  condaExe = os.path.join(envFolder, "bin", "python")
546
599
  return condaExe == getPython()
547
600
 
601
+ @classmethod
602
+ def getEnvName(cls):
603
+ """ Returns Scipion's environment name(conda or venv)"""
604
+ if cls.isCondaInstallation():
605
+ envPath = os.environ['CONDA_PREFIX']
606
+ else: # Virtualenv
607
+ envPath = os.environ['VIRTUAL_ENV']
608
+
609
+ return os.path.basename(envPath)
610
+
611
+
548
612
  @classmethod
549
613
  def getSpritesFile(cls):
550
614
  if not os.path.exists(Config.SCIPION_SPRITES_FILE):
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.10.6'
46
+ VERSION_3_0 = '3.11.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.
@@ -141,6 +141,7 @@ PROJECT_SETTINGS = 'settings.sqlite'
141
141
  FALSE_STR = str(False)
142
142
  TRUE_STR = str(True)
143
143
  NONE_STR = str(None)
144
+ TRUE_YES_ON_ = ['true', 'yes', 'on', '1']
144
145
  EMPTY_STR = ""
145
146
 
146
147
  # GUI colors
pyworkflow/gui/browser.py CHANGED
@@ -217,6 +217,33 @@ class FileHandler(object):
217
217
  return []
218
218
 
219
219
 
220
+ class FSFileHandler(FileHandler):
221
+
222
+ def __init__(self):
223
+ self._refresh_callback = None
224
+
225
+ def setRefresh(self, refresh_callback):
226
+ self._refresh_callback = refresh_callback
227
+
228
+ def copyToClipboard(self, file):
229
+ import pyperclip
230
+ pyperclip.copy(file)
231
+ logger.info(f'{file} copy to clipboard')
232
+
233
+ def deleteFile(self, file):
234
+ pwutils.cleanPath(file)
235
+
236
+ if self._refresh_callback:
237
+ self._refresh_callback(None)
238
+ def getFileActions(self, objFile):
239
+ """ Return basic os actions like delete or copy to clipboard
240
+ """
241
+ fn = objFile.getPath()
242
+ return [('Copy path', lambda: self.copyToClipboard(fn), pwutils.Icon.ACTION_COPY),
243
+ ('Delete', lambda: self.deleteFile(fn), pwutils.Icon.DELETE_OPERATION)
244
+ ]
245
+
246
+
220
247
  class TextFileHandler(FileHandler):
221
248
  def __init__(self, textIcon):
222
249
  FileHandler.__init__(self)
@@ -235,7 +262,7 @@ class FileTreeProvider(TreeProvider):
235
262
  """ Populate a tree with files and folders of a given path """
236
263
 
237
264
  _FILE_HANDLERS = {}
238
- _DEFAULT_HANDLER = FileHandler()
265
+ _FS_HANDLER = FSFileHandler()
239
266
  FILE_COLUMN = 'File'
240
267
  SIZE_COLUMN = 'Size'
241
268
 
@@ -252,18 +279,23 @@ class FileTreeProvider(TreeProvider):
252
279
  handlersList.append(fileHandler)
253
280
  cls._FILE_HANDLERS[fileExt] = handlersList
254
281
 
255
- def __init__(self, currentDir=None, showHidden=False, onlyFolders=False):
282
+ def __init__(self, currentDir, showHidden, onlyFolders, browser):
256
283
  TreeProvider.__init__(self, sortingColumnName=self.FILE_COLUMN)
257
284
  self._currentDir = os.path.abspath(currentDir)
258
285
  self._showHidden = showHidden
259
286
  self._onlyFolders = onlyFolders
287
+ self._browser = browser
260
288
  self.getColumns = lambda: [(self.FILE_COLUMN, 300),
261
289
  (self.SIZE_COLUMN, 70), ('Time', 150)]
262
290
 
263
291
  def getFileHandlers(self, obj):
264
292
  filename = obj.getFileName()
265
293
  fileExt = pwutils.getExt(filename)
266
- return self._FILE_HANDLERS.get(fileExt, [self._DEFAULT_HANDLER])
294
+ fhs = self._FILE_HANDLERS.get(fileExt,[])
295
+ # add basic options: delete, copy. They do not depend on the extension.
296
+ if self._FS_HANDLER not in fhs:
297
+ fhs.append(self._FS_HANDLER)
298
+ return fhs
267
299
 
268
300
  def getObjectInfo(self, obj):
269
301
  filename = obj.getFileName()
@@ -299,11 +331,13 @@ class FileTreeProvider(TreeProvider):
299
331
  fileHandlers = self.getFileHandlers(obj)
300
332
  actions = []
301
333
  for fileHandler in fileHandlers:
334
+ if fileHandler == self._FS_HANDLER:
335
+ fileHandler.setRefresh(self._browser._actionRefresh)
302
336
  actions += fileHandler.getFileActions(obj)
303
337
  # Always allow the option to open as text
304
338
  # specially useful for unknown formats
305
339
  fn = obj.getPath()
306
- actions.append(("Open external Editor",
340
+ actions.append(("Open external program",
307
341
  lambda: openTextFileEditor(fn), pwutils.Icon.ACTION_REFERENCES))
308
342
 
309
343
  return actions
@@ -404,7 +438,7 @@ class FileBrowser(ObjectBrowser):
404
438
  self.previousSearch = None
405
439
  self.previousSearchTS = None
406
440
  self.shortCuts = shortCuts
407
- self._provider = FileTreeProvider(initialDir, showHidden, onlyFolders)
441
+ self._provider = FileTreeProvider(initialDir, showHidden, onlyFolders, self)
408
442
  self.selectButton = selectButton
409
443
  self.entryLabel = entryLabel
410
444
  self.entryVar = tk.StringVar()
pyworkflow/gui/dialog.py CHANGED
@@ -917,6 +917,8 @@ class SearchBaseWindow(Window):
917
917
 
918
918
  frame.grid(row=0, column=0, sticky='new', padx=5, pady=(10, 5))
919
919
 
920
+ return frame
921
+
920
922
  def _createResultsBox(self, content):
921
923
  frame = tk.Frame(content, bg=Color.ALT_COLOR, padx=5, pady=5)
922
924
  configureWeigths(frame)