scipion-pyworkflow 3.10.6__py3-none-any.whl → 3.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyworkflow/config.py +131 -67
- pyworkflow/constants.py +2 -1
- pyworkflow/plugin.py +93 -44
- pyworkflow/resources/showj/arrowDown.png +0 -0
- pyworkflow/resources/showj/arrowUp.png +0 -0
- pyworkflow/resources/showj/background_section.png +0 -0
- pyworkflow/resources/showj/colRowModeOff.png +0 -0
- pyworkflow/resources/showj/colRowModeOn.png +0 -0
- pyworkflow/resources/showj/delete.png +0 -0
- pyworkflow/resources/showj/doc_icon.png +0 -0
- pyworkflow/resources/showj/download_icon.png +0 -0
- pyworkflow/resources/showj/enabled_gallery.png +0 -0
- pyworkflow/resources/showj/galleryViewOff.png +0 -0
- pyworkflow/resources/showj/galleryViewOn.png +0 -0
- pyworkflow/resources/showj/goto.png +0 -0
- pyworkflow/resources/showj/menu.png +0 -0
- pyworkflow/resources/showj/separator.png +0 -0
- pyworkflow/resources/showj/tableViewOff.png +0 -0
- pyworkflow/resources/showj/tableViewOn.png +0 -0
- pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- pyworkflow/resources/showj/volumeOff.png +0 -0
- pyworkflow/resources/showj/volumeOn.png +0 -0
- pyworkflow/viewer.py +23 -1
- pyworkflowtests/protocols.py +1 -3
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +13 -27
- scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
- pyworkflow/apps/__init__.py +0 -29
- pyworkflow/apps/pw_manager.py +0 -37
- pyworkflow/apps/pw_plot.py +0 -51
- pyworkflow/apps/pw_project.py +0 -113
- pyworkflow/apps/pw_protocol_list.py +0 -143
- pyworkflow/apps/pw_protocol_run.py +0 -51
- pyworkflow/apps/pw_run_tests.py +0 -267
- pyworkflow/apps/pw_schedule_run.py +0 -322
- pyworkflow/apps/pw_sleep.py +0 -37
- pyworkflow/apps/pw_sync_data.py +0 -439
- pyworkflow/apps/pw_viewer.py +0 -78
- pyworkflow/gui/__init__.py +0 -36
- pyworkflow/gui/browser.py +0 -726
- pyworkflow/gui/canvas.py +0 -1190
- pyworkflow/gui/dialog.py +0 -977
- pyworkflow/gui/form.py +0 -2637
- pyworkflow/gui/graph.py +0 -247
- pyworkflow/gui/graph_layout.py +0 -271
- pyworkflow/gui/gui.py +0 -566
- pyworkflow/gui/matplotlib_image.py +0 -233
- pyworkflow/gui/plotter.py +0 -247
- pyworkflow/gui/project/__init__.py +0 -25
- pyworkflow/gui/project/base.py +0 -192
- pyworkflow/gui/project/constants.py +0 -139
- pyworkflow/gui/project/labels.py +0 -205
- pyworkflow/gui/project/project.py +0 -492
- pyworkflow/gui/project/searchprotocol.py +0 -154
- pyworkflow/gui/project/searchrun.py +0 -181
- pyworkflow/gui/project/steps.py +0 -171
- pyworkflow/gui/project/utils.py +0 -332
- pyworkflow/gui/project/variables.py +0 -179
- pyworkflow/gui/project/viewdata.py +0 -472
- pyworkflow/gui/project/viewprojects.py +0 -510
- pyworkflow/gui/project/viewprotocols.py +0 -2093
- pyworkflow/gui/project/viewprotocols_extra.py +0 -559
- pyworkflow/gui/text.py +0 -771
- pyworkflow/gui/tooltip.py +0 -185
- pyworkflow/gui/tree.py +0 -684
- pyworkflow/gui/widgets.py +0 -307
- pyworkflow/mapper/__init__.py +0 -26
- pyworkflow/mapper/mapper.py +0 -222
- pyworkflow/mapper/sqlite.py +0 -1581
- pyworkflow/mapper/sqlite_db.py +0 -145
- pyworkflow/project/__init__.py +0 -31
- pyworkflow/project/config.py +0 -454
- pyworkflow/project/manager.py +0 -180
- pyworkflow/project/project.py +0 -2007
- pyworkflow/protocol/__init__.py +0 -38
- pyworkflow/protocol/bibtex.py +0 -48
- pyworkflow/protocol/constants.py +0 -87
- pyworkflow/protocol/executor.py +0 -471
- pyworkflow/protocol/hosts.py +0 -314
- pyworkflow/protocol/launch.py +0 -270
- pyworkflow/protocol/package.py +0 -42
- pyworkflow/protocol/params.py +0 -741
- pyworkflow/protocol/protocol.py +0 -2641
- pyworkflow/tests/__init__.py +0 -29
- pyworkflow/tests/test_utils.py +0 -25
- pyworkflow/tests/tests.py +0 -341
- pyworkflow/utils/__init__.py +0 -38
- pyworkflow/utils/dataset.py +0 -414
- pyworkflow/utils/echo.py +0 -104
- pyworkflow/utils/graph.py +0 -169
- pyworkflow/utils/log.py +0 -284
- pyworkflow/utils/path.py +0 -528
- pyworkflow/utils/process.py +0 -153
- pyworkflow/utils/profiler.py +0 -92
- pyworkflow/utils/progressbar.py +0 -154
- pyworkflow/utils/properties.py +0 -631
- pyworkflow/utils/reflection.py +0 -129
- pyworkflow/utils/utils.py +0 -879
- pyworkflow/utils/which.py +0 -229
- pyworkflow/webservices/__init__.py +0 -8
- pyworkflow/webservices/config.py +0 -11
- pyworkflow/webservices/notifier.py +0 -162
- pyworkflow/webservices/repository.py +0 -59
- pyworkflow/webservices/workflowhub.py +0 -74
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +0 -72
- pyworkflowtests/tests/test_domain.py +0 -45
- pyworkflowtests/tests/test_logs.py +0 -74
- pyworkflowtests/tests/test_mappers.py +0 -392
- pyworkflowtests/tests/test_object.py +0 -507
- pyworkflowtests/tests/test_project.py +0 -42
- pyworkflowtests/tests/test_protocol_execution.py +0 -142
- pyworkflowtests/tests/test_protocol_export.py +0 -78
- pyworkflowtests/tests/test_protocol_output.py +0 -158
- pyworkflowtests/tests/test_streaming.py +0 -47
- pyworkflowtests/tests/test_utils.py +0 -210
- scipion_pyworkflow-3.10.6.dist-info/RECORD +0 -140
- scipion_pyworkflow-3.10.6.dist-info/dependency_links.txt +0 -1
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/licenses/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/top_level.txt +0 -0
@@ -1,492 +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
|
-
Main Project window implementation.
|
27
|
-
It is composed by three panels:
|
28
|
-
1. Left: protocol tree.
|
29
|
-
2. Right upper: VIEWS (Data/Protocols)
|
30
|
-
3. Summary/Details
|
31
|
-
"""
|
32
|
-
|
33
|
-
import logging
|
34
|
-
from tkinter import Label
|
35
|
-
|
36
|
-
from .. import askString
|
37
|
-
|
38
|
-
logger = logging.getLogger(__name__)
|
39
|
-
|
40
|
-
import os
|
41
|
-
import threading
|
42
|
-
import shlex
|
43
|
-
import subprocess
|
44
|
-
import socketserver
|
45
|
-
|
46
|
-
import pyworkflow as pw
|
47
|
-
import pyworkflow.utils as pwutils
|
48
|
-
from pyworkflow.gui.project.utils import OS
|
49
|
-
from pyworkflow.project import MenuConfig
|
50
|
-
from pyworkflow.gui import Message, Icon, getBigFont, ToolTip
|
51
|
-
from pyworkflow.gui.browser import FileBrowserWindow
|
52
|
-
|
53
|
-
from pyworkflow.gui.plotter import Plotter
|
54
|
-
from pyworkflow.gui.text import _open_cmd, openTextFileEditor
|
55
|
-
from pyworkflow.webservices import ProjectWorkflowNotifier, WorkflowRepository
|
56
|
-
|
57
|
-
from .labels import LabelsDialog
|
58
|
-
# Import possible Object commands to be handled
|
59
|
-
from .base import ProjectBaseWindow, VIEW_PROTOCOLS, VIEW_PROJECTS
|
60
|
-
|
61
|
-
|
62
|
-
class ProjectWindow(ProjectBaseWindow):
|
63
|
-
""" Main window for working in a Project. """
|
64
|
-
_OBJECT_COMMANDS = {}
|
65
|
-
|
66
|
-
def __init__(self, path, master=None):
|
67
|
-
# Load global configuration
|
68
|
-
self.projPath = path
|
69
|
-
self.project = self.loadProject()
|
70
|
-
self.projName = self.project.getShortName()
|
71
|
-
|
72
|
-
try:
|
73
|
-
projTitle = '%s (%s on %s)' % (self.projName,
|
74
|
-
pwutils.getLocalUserName(),
|
75
|
-
pwutils.getLocalHostName())
|
76
|
-
except Exception:
|
77
|
-
projTitle = self.projName
|
78
|
-
|
79
|
-
|
80
|
-
# TODO: put the menu part more nicely. From here:
|
81
|
-
menu = MenuConfig()
|
82
|
-
|
83
|
-
projMenu = menu.addSubMenu('Project')
|
84
|
-
projMenu.addSubMenu('Browse files', 'browse',
|
85
|
-
icon=Icon.FOLDER_OPEN)
|
86
|
-
projMenu.addSubMenu('Remove temporary files', 'delete',
|
87
|
-
icon=Icon.ACTION_DELETE)
|
88
|
-
projMenu.addSubMenu('Toggle color mode', 'color_mode',
|
89
|
-
shortCut="Ctrl+t", icon=Icon.ACTION_VISUALIZE)
|
90
|
-
projMenu.addSubMenu('Select all protocols', 'select all',
|
91
|
-
shortCut="Ctrl+a", icon=Icon.SELECT_ALL)
|
92
|
-
projMenu.addSubMenu('Locate a protocol', 'locate protocol',
|
93
|
-
shortCut="Ctrl+l")
|
94
|
-
projMenu.addSubMenu('', '') # add separator
|
95
|
-
projMenu.addSubMenu('Import workflow', 'load_workflow',
|
96
|
-
icon=Icon.DOWNLOAD)
|
97
|
-
projMenu.addSubMenu('Search workflow', 'search_workflow',
|
98
|
-
icon=Icon.ACTION_SEARCH)
|
99
|
-
|
100
|
-
projMenu.addSubMenu('Configuration', 'configuration',
|
101
|
-
icon=Icon.SETTINGS)
|
102
|
-
|
103
|
-
projMenu.addSubMenu('', '') # add separator
|
104
|
-
projMenu.addSubMenu('Debug Mode', 'debug mode',
|
105
|
-
shortCut="Ctrl+D", icon=Icon.DEBUG)
|
106
|
-
projMenu.addSubMenu('', '') # add separator
|
107
|
-
projMenu.addSubMenu('Notes', 'notes', icon=Icon.ACTION_EDIT)
|
108
|
-
projMenu.addSubMenu('', '') # add separator
|
109
|
-
projMenu.addSubMenu('Exit', 'exit', icon=Icon.ACTION_OUT)
|
110
|
-
|
111
|
-
helpMenu = menu.addSubMenu('Help')
|
112
|
-
helpMenu.addSubMenu('Online help', 'online_help',
|
113
|
-
icon=Icon.ACTION_EXPORT)
|
114
|
-
helpMenu.addSubMenu('About', 'about',
|
115
|
-
icon=Icon.ACTION_HELP)
|
116
|
-
helpMenu.addSubMenu('Contact support', 'contact_us',
|
117
|
-
icon=Icon.ACTION_HELP)
|
118
|
-
|
119
|
-
self.menuCfg = menu
|
120
|
-
|
121
|
-
if self.project.openedAsReadOnly():
|
122
|
-
self.projName += "<READ ONLY>"
|
123
|
-
|
124
|
-
# Notify about the workflow in this project
|
125
|
-
self.selectedProtocol = None
|
126
|
-
self.showGraph = False
|
127
|
-
self.commentTT = None # Tooltip to store the project description
|
128
|
-
|
129
|
-
Plotter.setBackend('TkAgg')
|
130
|
-
ProjectBaseWindow.__init__(self, projTitle, master,
|
131
|
-
minsize=(90, 50), icon=Icon.SCIPION_ICON_PROJ, _class=self.projName)
|
132
|
-
|
133
|
-
OS.handler().maximizeWindow(self.root)
|
134
|
-
|
135
|
-
self.switchView(VIEW_PROTOCOLS)
|
136
|
-
|
137
|
-
self.initProjectTCPServer() # Socket thread to communicate with clients
|
138
|
-
|
139
|
-
ProjectWorkflowNotifier(self.project).notifyWorkflow()
|
140
|
-
|
141
|
-
|
142
|
-
def createHeaderFrame(self, parent):
|
143
|
-
"""Create the header and add the view selection frame at the right."""
|
144
|
-
header = ProjectBaseWindow.createHeaderFrame(self, parent)
|
145
|
-
self.addViewList(header)
|
146
|
-
return header
|
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
|
165
|
-
def getSettings(self):
|
166
|
-
return self.settings
|
167
|
-
|
168
|
-
def saveSettings(self):
|
169
|
-
|
170
|
-
try:
|
171
|
-
self.settings.write()
|
172
|
-
except Exception as ex:
|
173
|
-
logger.error(Message.NO_SAVE_SETTINGS, exc_info=ex)
|
174
|
-
|
175
|
-
def _onClosing(self):
|
176
|
-
if not self.project.openedAsReadOnly():
|
177
|
-
self.saveSettings()
|
178
|
-
|
179
|
-
ProjectBaseWindow._onClosing(self)
|
180
|
-
|
181
|
-
def loadProject(self):
|
182
|
-
proj = pw.project.Project(pw.Config.getDomain(), self.projPath)
|
183
|
-
proj.configureLogging()
|
184
|
-
proj.load()
|
185
|
-
|
186
|
-
# Check if we have settings.sqlite, generate if not
|
187
|
-
settingsPath = os.path.join(proj.path, proj.settingsPath)
|
188
|
-
if os.path.exists(settingsPath):
|
189
|
-
self.settings = proj.getSettings()
|
190
|
-
else:
|
191
|
-
logger.info('Warning: settings.sqlite not found! '
|
192
|
-
'Creating default settings..')
|
193
|
-
self.settings = proj.createSettings()
|
194
|
-
|
195
|
-
return proj
|
196
|
-
|
197
|
-
# The next functions are callbacks from the menu options.
|
198
|
-
# See how it is done in pyworkflow/gui/gui.py:Window._addMenuChilds()
|
199
|
-
#
|
200
|
-
def onBrowseFiles(self):
|
201
|
-
# Project -> Browse files
|
202
|
-
FileBrowserWindow("Browse Project files",
|
203
|
-
self, self.project.getPath(''),
|
204
|
-
selectButton=None # we will select nothing
|
205
|
-
).show()
|
206
|
-
|
207
|
-
def onDebugMode(self):
|
208
|
-
pw.Config.toggleDebug()
|
209
|
-
|
210
|
-
def onNotes(self):
|
211
|
-
notes_program = pw.Config.SCIPION_NOTES_PROGRAM
|
212
|
-
notes_args = pw.Config.SCIPION_NOTES_ARGS
|
213
|
-
args = []
|
214
|
-
notes_file = self.project.getPath('Logs', pw.Config.SCIPION_NOTES_FILE)
|
215
|
-
|
216
|
-
# If notesFile does not exist, it is created and an explanation/documentation comment is added at the top.
|
217
|
-
if not os.path.exists(notes_file):
|
218
|
-
f = open(notes_file, 'a')
|
219
|
-
f.write(pw.genNotesHeading())
|
220
|
-
f.close()
|
221
|
-
|
222
|
-
# Then, it will be opened as specified in the conf
|
223
|
-
if notes_program:
|
224
|
-
args.append(notes_program)
|
225
|
-
# Custom arguments
|
226
|
-
if notes_args:
|
227
|
-
args.append(notes_args)
|
228
|
-
args.append(notes_file)
|
229
|
-
subprocess.Popen(args) # nonblocking
|
230
|
-
else:
|
231
|
-
# if no program has been selected
|
232
|
-
# xdg-open will try to guess but
|
233
|
-
# if the file does not exist it
|
234
|
-
# will return an error so If the file does
|
235
|
-
# not exist I will create an empty one
|
236
|
-
# 'a' will avoid accidental truncation
|
237
|
-
openTextFileEditor(notes_file)
|
238
|
-
|
239
|
-
def onRemoveTemporaryFiles(self):
|
240
|
-
# Project -> Remove temporary files
|
241
|
-
tmpPath = os.path.join(self.project.path, self.project.tmpPath)
|
242
|
-
n = 0
|
243
|
-
try:
|
244
|
-
for fname in os.listdir(tmpPath):
|
245
|
-
fpath = "%s/%s" % (tmpPath, fname)
|
246
|
-
if os.path.isfile(fpath):
|
247
|
-
os.remove(fpath)
|
248
|
-
n += 1
|
249
|
-
# TODO: think what to do with directories. Delete? Report?
|
250
|
-
self.showInfo("Deleted content of %s -- %d file(s)." % (tmpPath, n))
|
251
|
-
except Exception as e:
|
252
|
-
self.showError(str(e))
|
253
|
-
|
254
|
-
def _loadWorkflow(self, obj):
|
255
|
-
try:
|
256
|
-
self.getViewWidget().info('Importing workflow %s' % obj.getPath())
|
257
|
-
self.project.loadProtocols(obj.getPath())
|
258
|
-
self.getViewWidget().updateRunsGraph(True)
|
259
|
-
self.getViewWidget().cleanInfo()
|
260
|
-
except Exception as ex:
|
261
|
-
self.showError(str(ex), exception=ex)
|
262
|
-
|
263
|
-
def onImportWorkflow(self):
|
264
|
-
FileBrowserWindow("Select workflow .json file",
|
265
|
-
self, self.project.getPath(''),
|
266
|
-
onSelect=self._loadWorkflow,
|
267
|
-
selectButton='Import'
|
268
|
-
).show()
|
269
|
-
|
270
|
-
def onSearchWorkflow(self):
|
271
|
-
WorkflowRepository().search()
|
272
|
-
|
273
|
-
def onToggleColorMode(self):
|
274
|
-
self.getViewWidget()._toggleColorScheme(None)
|
275
|
-
|
276
|
-
def onSelectAllProtocols(self):
|
277
|
-
self.getViewWidget()._selectAllProtocols(None)
|
278
|
-
|
279
|
-
def onLocateAProtocol(self):
|
280
|
-
self.getViewWidget()._locateProtocol(None)
|
281
|
-
|
282
|
-
def manageLabels(self):
|
283
|
-
|
284
|
-
labels = self.project.settings.getLabels()
|
285
|
-
dialog = LabelsDialog(self.root,
|
286
|
-
labels,
|
287
|
-
allowSelect=True)
|
288
|
-
|
289
|
-
# Scan for renamed labels to update node info...
|
290
|
-
labelsRenamed = dict()
|
291
|
-
for label in labels:
|
292
|
-
if label.hasOldName():
|
293
|
-
oldName = label.getOldName()
|
294
|
-
newName = label.getName()
|
295
|
-
logger.info("Label %s renamed to %s" % (oldName, newName))
|
296
|
-
labelsRenamed[oldName] = newName
|
297
|
-
label.clearOldName()
|
298
|
-
|
299
|
-
# If there are labels renamed
|
300
|
-
if labelsRenamed:
|
301
|
-
logger.info("Updating labels of protocols after renaming.")
|
302
|
-
labels.updateDict()
|
303
|
-
|
304
|
-
for node in self.project.settings.getNodes():
|
305
|
-
nodeLabels = node.getLabels()
|
306
|
-
for index, nodeLabel in enumerate(nodeLabels):
|
307
|
-
|
308
|
-
newLabel = labelsRenamed.get(nodeLabel, None)
|
309
|
-
if newLabel is not None:
|
310
|
-
logger.info("Label %s found in %s. Updating it to %s" % (nodeLabel,node, newLabel))
|
311
|
-
nodeLabels[index] = newLabel
|
312
|
-
|
313
|
-
return dialog
|
314
|
-
|
315
|
-
def initProjectTCPServer(self):
|
316
|
-
server = ProjectTCPServer((self.project.address, self.project.port),
|
317
|
-
ProjectTCPRequestHandler)
|
318
|
-
server.project = self.project
|
319
|
-
server.window = self
|
320
|
-
server_thread = threading.Thread(name="projectTCPserver", target=server.serve_forever)
|
321
|
-
# Exit the server thread when the main thread terminates
|
322
|
-
server_thread.daemon = True
|
323
|
-
server_thread.start()
|
324
|
-
|
325
|
-
# Seems it is not used and should be in scipion-em
|
326
|
-
# Not within scipion but used from ShowJ
|
327
|
-
def schedulePlot(self, path, *args):
|
328
|
-
# FIXME: This import should not be here
|
329
|
-
from pwem.viewers import EmPlotter
|
330
|
-
self.enqueue(lambda: EmPlotter.createFromFile(path, *args).show())
|
331
|
-
|
332
|
-
@classmethod
|
333
|
-
def registerObjectCommand(cls, cmd, func):
|
334
|
-
""" Register an object command to be handled when receiving the
|
335
|
-
action from showj. """
|
336
|
-
cls._OBJECT_COMMANDS[cmd] = func
|
337
|
-
|
338
|
-
def runObjectCommand(self, cmd, inputStrId, objStrId):
|
339
|
-
try:
|
340
|
-
objId = int(objStrId)
|
341
|
-
project = self.project
|
342
|
-
|
343
|
-
if os.path.isfile(inputStrId) and os.path.exists(inputStrId):
|
344
|
-
from pwem.utils import loadSetFromDb
|
345
|
-
inputObj = loadSetFromDb(inputStrId)
|
346
|
-
else:
|
347
|
-
inputId = int(inputStrId)
|
348
|
-
inputObj = project.mapper.selectById(inputId)
|
349
|
-
|
350
|
-
func = self._OBJECT_COMMANDS.get(cmd, None)
|
351
|
-
|
352
|
-
if func is None:
|
353
|
-
logger.info("Error, command '%s' not found. " % cmd)
|
354
|
-
else:
|
355
|
-
def myfunc():
|
356
|
-
func(inputObj, objId)
|
357
|
-
inputObj.close()
|
358
|
-
self.enqueue(myfunc)
|
359
|
-
|
360
|
-
except Exception as ex:
|
361
|
-
logger.error("There was an error executing object command !!!:", exc_info=ex)
|
362
|
-
|
363
|
-
class ProjectManagerWindow(ProjectBaseWindow):
|
364
|
-
""" Windows to manage all projects. """
|
365
|
-
# To allow plugins to add their own menus
|
366
|
-
_pluginMenus = dict()
|
367
|
-
|
368
|
-
def __init__(self, **kwargs):
|
369
|
-
|
370
|
-
# TODO: put the menu part more nicely. From here:
|
371
|
-
menu = MenuConfig()
|
372
|
-
|
373
|
-
fileMenu = menu.addSubMenu('File')
|
374
|
-
fileMenu.addSubMenu('Browse files', 'browse', icon=Icon.FOLDER_OPEN)
|
375
|
-
fileMenu.addSubMenu('Exit', 'exit', icon=Icon.ACTION_OUT)
|
376
|
-
|
377
|
-
confMenu = menu.addSubMenu('Configuration')
|
378
|
-
if os.path.exists(pw.Config.SCIPION_CONFIG):
|
379
|
-
confMenu.addSubMenu('General', 'general')
|
380
|
-
confMenu.addSubMenu('Hosts', 'hosts')
|
381
|
-
if os.path.exists(pw.Config.SCIPION_PROTOCOLS):
|
382
|
-
confMenu.addSubMenu('Protocols', 'protocols')
|
383
|
-
if os.path.exists(pw.Config.SCIPION_LOCAL_CONFIG):
|
384
|
-
confMenu.addSubMenu('User', 'user')
|
385
|
-
|
386
|
-
helpMenu = menu.addSubMenu('Help')
|
387
|
-
helpMenu.addSubMenu('Online help', 'online_help', icon=Icon.ACTION_EXPORT)
|
388
|
-
helpMenu.addSubMenu('About', 'about', icon=Icon.ACTION_HELP)
|
389
|
-
|
390
|
-
self.menuCfg = menu
|
391
|
-
|
392
|
-
try:
|
393
|
-
title = '%s (%s on %s)' % (Message.LABEL_PROJECTS,
|
394
|
-
pwutils.getLocalUserName(),
|
395
|
-
pwutils.getLocalHostName())
|
396
|
-
except Exception:
|
397
|
-
title = Message.LABEL_PROJECTS
|
398
|
-
|
399
|
-
ProjectBaseWindow.__init__(self, title, minsize=(750, 500),
|
400
|
-
icon=Icon.SCIPION_ICON_PROJS, **kwargs)
|
401
|
-
self.manager = pw.project.Manager()
|
402
|
-
self.switchView(VIEW_PROJECTS)
|
403
|
-
|
404
|
-
#
|
405
|
-
# The next functions are callbacks from the menu options.
|
406
|
-
# See how it is done in pyworkflow/gui/gui.py:Window._addMenuChilds()
|
407
|
-
#
|
408
|
-
def onBrowseFiles(self):
|
409
|
-
# File -> Browse files
|
410
|
-
FileBrowserWindow("Browse files", self,
|
411
|
-
pw.Config.SCIPION_USER_DATA,
|
412
|
-
selectButton=None).show()
|
413
|
-
|
414
|
-
def onGeneral(self):
|
415
|
-
# Config -> General
|
416
|
-
self._openConfigFile(pw.Config.SCIPION_CONFIG)
|
417
|
-
|
418
|
-
@staticmethod
|
419
|
-
def _openConfigFile(configFile):
|
420
|
-
""" Open an Scipion configuration file, if the user have one defined,
|
421
|
-
also open that one with the defined text editor.
|
422
|
-
"""
|
423
|
-
_open_cmd(configFile)
|
424
|
-
|
425
|
-
@staticmethod
|
426
|
-
def onHosts():
|
427
|
-
# Config -> Hosts
|
428
|
-
ProjectManagerWindow._openConfigFile(pw.Config.SCIPION_HOSTS)
|
429
|
-
|
430
|
-
@staticmethod
|
431
|
-
def onProtocols():
|
432
|
-
ProjectManagerWindow._openConfigFile(pw.Config.SCIPION_PROTOCOLS)
|
433
|
-
|
434
|
-
@staticmethod
|
435
|
-
def onUser():
|
436
|
-
ProjectManagerWindow._openConfigFile(pw.Config.SCIPION_LOCAL_CONFIG)
|
437
|
-
|
438
|
-
|
439
|
-
class ProjectTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
440
|
-
pass
|
441
|
-
|
442
|
-
|
443
|
-
class ProjectTCPRequestHandler(socketserver.BaseRequestHandler):
|
444
|
-
|
445
|
-
def handle(self):
|
446
|
-
try:
|
447
|
-
project = self.server.project
|
448
|
-
window = self.server.window
|
449
|
-
msg = self.request.recv(1024)
|
450
|
-
msg = msg.decode()
|
451
|
-
tokens = shlex.split(msg)
|
452
|
-
if msg.startswith('run protocol'):
|
453
|
-
|
454
|
-
logger.debug("run protocol messaged arrived: %s" % msg)
|
455
|
-
protocolName = tokens[2]
|
456
|
-
protocolClass = pw.Config.getDomain().getProtocols()[protocolName]
|
457
|
-
# Create the new protocol instance and set the input values
|
458
|
-
protocol = project.newProtocol(protocolClass)
|
459
|
-
|
460
|
-
for token in tokens[3:]:
|
461
|
-
param, value = token.split('=')
|
462
|
-
attr = getattr(protocol, param, None)
|
463
|
-
if param == 'label':
|
464
|
-
protocol.setObjLabel(value)
|
465
|
-
elif attr.isPointer():
|
466
|
-
obj = project.getObject(int(value))
|
467
|
-
attr.set(obj)
|
468
|
-
elif value:
|
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
|
-
|
477
|
-
# project.launchProtocol(protocol)
|
478
|
-
# We need to enqueue the action of execute a new protocol
|
479
|
-
# to be run in the same GUI thread and avoid concurrent
|
480
|
-
# access to the project sqlite database
|
481
|
-
window.getViewWidget().executeProtocol(protocol)
|
482
|
-
elif msg.startswith('run function'):
|
483
|
-
functionName = tokens[2]
|
484
|
-
functionPointer = getattr(window, functionName)
|
485
|
-
functionPointer(*tokens[3:])
|
486
|
-
else:
|
487
|
-
answer = b'no answer available\n'
|
488
|
-
self.request.sendall(answer)
|
489
|
-
except Exception as e:
|
490
|
-
print(e)
|
491
|
-
import traceback
|
492
|
-
traceback.print_stack()
|
@@ -1,154 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# **************************************************************************
|
3
|
-
# *
|
4
|
-
# * Authors: Pablo Conesa [1]
|
5
|
-
# *
|
6
|
-
# * [1] Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
|
7
|
-
# *
|
8
|
-
# * This program is free software: you can redistribute it and/or modify
|
9
|
-
# * it under the terms of the GNU General Public License as published by
|
10
|
-
# * the Free Software Foundation, either version 3 of the License, or
|
11
|
-
# * (at your option) any later version.
|
12
|
-
# *
|
13
|
-
# * This program is distributed in the hope that it will be useful,
|
14
|
-
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
-
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
-
# * GNU General Public License for more details.
|
17
|
-
# *
|
18
|
-
# * You should have received a copy of the GNU General Public License
|
19
|
-
# * along with this program. If not, 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
|
-
""" This modules hosts code provider and window to search for a protocol"""
|
26
|
-
import tkinter as tk
|
27
|
-
|
28
|
-
from pyworkflow import Config
|
29
|
-
import pyworkflow.gui as pwgui
|
30
|
-
import pyworkflow.object as pwobj
|
31
|
-
from pyworkflow.gui.dialog import SearchBaseWindow
|
32
|
-
|
33
|
-
from pyworkflow.gui.project.utils import isAFinalProtocol
|
34
|
-
from pyworkflow.gui.project.viewprotocols_extra import ProtocolTreeConfig
|
35
|
-
|
36
|
-
UPDATED = "updated"
|
37
|
-
|
38
|
-
NEW = "new"
|
39
|
-
|
40
|
-
BETA = "beta"
|
41
|
-
|
42
|
-
|
43
|
-
class ProtocolTreeProvider(pwgui.tree.ObjectTreeProvider):
|
44
|
-
"""Create the tree elements for a Protocol run"""
|
45
|
-
|
46
|
-
def __init__(self, protocol):
|
47
|
-
self.protocol = protocol
|
48
|
-
# This list is create to group the protocol parameters
|
49
|
-
# in the tree display
|
50
|
-
self.status = pwobj.List(objName='_status')
|
51
|
-
self.params = pwobj.List(objName='_params')
|
52
|
-
self.statusList = ['status', 'initTime', 'endTime', 'error',
|
53
|
-
'interactive', 'mode']
|
54
|
-
|
55
|
-
objList = [] if protocol is None else [protocol]
|
56
|
-
pwgui.tree.ObjectTreeProvider.__init__(self, objList)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
class SearchProtocolWindow(SearchBaseWindow):
|
61
|
-
|
62
|
-
columnConfig = {
|
63
|
-
'#0': ('Status', {'width': 50, 'minwidth': 50, 'stretch': tk.NO}, 5),
|
64
|
-
# Heading, tree column kwargs, casting for sorting
|
65
|
-
'protocol': ('Protocol', {'width': 300, 'stretch': tk.FALSE}, 10),
|
66
|
-
'streaming': ('Streamified', {'width': 100, 'stretch': tk.FALSE}, 5),
|
67
|
-
'installed': ('Installation', {'width': 110, 'stretch': tk.FALSE}, 5),
|
68
|
-
'help': ('Help', {'minwidth': 300, 'stretch': tk.YES}, 5),
|
69
|
-
'score': ('Score', {'width': 50, 'stretch': tk.FALSE}, 5, int)
|
70
|
-
}
|
71
|
-
|
72
|
-
def __init__(self, parentWindow, position=None):
|
73
|
-
|
74
|
-
posStr = "" if position is None else " at (%s,%s)" % position
|
75
|
-
self.position = position
|
76
|
-
super().__init__(parentWindow,
|
77
|
-
title="Add a protocol" + posStr)
|
78
|
-
|
79
|
-
def _createResultsTree(self, frame, show, columns):
|
80
|
-
return self.master.getViewWidget()._createProtocolsTree(frame, show=show, columns=columns, position=self.position)
|
81
|
-
|
82
|
-
def _onSearchClick(self, e=None):
|
83
|
-
|
84
|
-
self._resultsTree.clear()
|
85
|
-
|
86
|
-
protList = self.scoreProtocols()
|
87
|
-
|
88
|
-
# Sort by weight
|
89
|
-
protList.sort(reverse=True, key=lambda x: x[8])
|
90
|
-
|
91
|
-
self._addProtocolToTree(protList)
|
92
|
-
|
93
|
-
def scoreProtocols(self):
|
94
|
-
|
95
|
-
keyword = self._searchVar.get().lower().strip()
|
96
|
-
emProtocolsDict = Config.getDomain().getProtocols()
|
97
|
-
protList = []
|
98
|
-
|
99
|
-
for key, prot in emProtocolsDict.items():
|
100
|
-
if isAFinalProtocol(prot, key):
|
101
|
-
label = prot.getClassLabel().lower()
|
102
|
-
line = (key, label,
|
103
|
-
"installed" if prot.isInstalled() else "missing installation",
|
104
|
-
prot.getHelpText().strip().replace('\r', '').replace('\n', '').lower(),
|
105
|
-
"streamified" if prot.worksInStreaming() else "static",
|
106
|
-
BETA if prot.isBeta() else "",
|
107
|
-
NEW if prot.isNewDev() else "",
|
108
|
-
UPDATED if prot.isUpdated() else "")
|
109
|
-
|
110
|
-
line = self._addSearchWeight(line, keyword)
|
111
|
-
# something was found: weight > 0
|
112
|
-
if line[8] != 0:
|
113
|
-
protList.append(line)
|
114
|
-
|
115
|
-
return protList
|
116
|
-
|
117
|
-
@staticmethod
|
118
|
-
def _addSearchWeight(line2Search, searchtext):
|
119
|
-
# Adds a weight value for the search
|
120
|
-
weight = 0
|
121
|
-
|
122
|
-
# prioritize findings in label
|
123
|
-
if searchtext in line2Search[1]:
|
124
|
-
weight += 10
|
125
|
-
|
126
|
-
for value in line2Search[2:]:
|
127
|
-
weight += 5 if searchtext in value else 0
|
128
|
-
|
129
|
-
if " " in searchtext:
|
130
|
-
for word in searchtext.split():
|
131
|
-
if word in line2Search[1]:
|
132
|
-
weight += 3
|
133
|
-
|
134
|
-
for value in line2Search[2:]:
|
135
|
-
weight += 1 if word in value else 0
|
136
|
-
|
137
|
-
return line2Search + (weight,)
|
138
|
-
|
139
|
-
def _addProtocolToTree(self, protList):
|
140
|
-
""" Adds the items in protList to the tree
|
141
|
-
|
142
|
-
:param protList: List of tuples with all the values/columns used in the search search to show in the tree"""
|
143
|
-
|
144
|
-
for key, label, installed, help, streamified, beta, new, updated, weight in protList:
|
145
|
-
tag = ProtocolTreeConfig.getProtocolTag(installed == 'installed',
|
146
|
-
beta == BETA,
|
147
|
-
new == NEW,
|
148
|
-
updated == UPDATED)
|
149
|
-
|
150
|
-
self._resultsTree.insert(
|
151
|
-
'', 'end', key, text="", tags=tag,
|
152
|
-
values=(label, streamified, installed, help, weight))
|
153
|
-
|
154
|
-
|