scipion-pyworkflow 3.7.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/__init__.py +33 -0
- pyworkflow/apps/__init__.py +29 -0
- pyworkflow/apps/pw_manager.py +37 -0
- pyworkflow/apps/pw_plot.py +51 -0
- pyworkflow/apps/pw_project.py +113 -0
- pyworkflow/apps/pw_protocol_list.py +143 -0
- pyworkflow/apps/pw_protocol_run.py +51 -0
- pyworkflow/apps/pw_run_tests.py +267 -0
- pyworkflow/apps/pw_schedule_run.py +322 -0
- pyworkflow/apps/pw_sleep.py +37 -0
- pyworkflow/apps/pw_sync_data.py +439 -0
- pyworkflow/apps/pw_viewer.py +78 -0
- pyworkflow/config.py +536 -0
- pyworkflow/constants.py +212 -0
- pyworkflow/exceptions.py +18 -0
- pyworkflow/gui/__init__.py +36 -0
- pyworkflow/gui/browser.py +726 -0
- pyworkflow/gui/canvas.py +1190 -0
- pyworkflow/gui/dialog.py +976 -0
- pyworkflow/gui/form.py +2627 -0
- pyworkflow/gui/graph.py +247 -0
- pyworkflow/gui/graph_layout.py +271 -0
- pyworkflow/gui/gui.py +566 -0
- pyworkflow/gui/matplotlib_image.py +233 -0
- pyworkflow/gui/plotter.py +247 -0
- pyworkflow/gui/project/__init__.py +25 -0
- pyworkflow/gui/project/base.py +192 -0
- pyworkflow/gui/project/constants.py +139 -0
- pyworkflow/gui/project/labels.py +205 -0
- pyworkflow/gui/project/project.py +484 -0
- pyworkflow/gui/project/searchprotocol.py +154 -0
- pyworkflow/gui/project/searchrun.py +181 -0
- pyworkflow/gui/project/steps.py +166 -0
- pyworkflow/gui/project/utils.py +332 -0
- pyworkflow/gui/project/variables.py +179 -0
- pyworkflow/gui/project/viewdata.py +472 -0
- pyworkflow/gui/project/viewprojects.py +510 -0
- pyworkflow/gui/project/viewprotocols.py +2093 -0
- pyworkflow/gui/project/viewprotocols_extra.py +560 -0
- pyworkflow/gui/text.py +771 -0
- pyworkflow/gui/tooltip.py +185 -0
- pyworkflow/gui/tree.py +684 -0
- pyworkflow/gui/widgets.py +307 -0
- pyworkflow/mapper/__init__.py +26 -0
- pyworkflow/mapper/mapper.py +222 -0
- pyworkflow/mapper/sqlite.py +1578 -0
- pyworkflow/mapper/sqlite_db.py +145 -0
- pyworkflow/object.py +1512 -0
- pyworkflow/plugin.py +712 -0
- pyworkflow/project/__init__.py +31 -0
- pyworkflow/project/config.py +451 -0
- pyworkflow/project/manager.py +179 -0
- pyworkflow/project/project.py +1990 -0
- pyworkflow/project/scripts/clean_projects.py +77 -0
- pyworkflow/project/scripts/config.py +92 -0
- pyworkflow/project/scripts/create.py +77 -0
- pyworkflow/project/scripts/edit_workflow.py +90 -0
- pyworkflow/project/scripts/fix_links.py +39 -0
- pyworkflow/project/scripts/load.py +87 -0
- pyworkflow/project/scripts/refresh.py +83 -0
- pyworkflow/project/scripts/schedule.py +111 -0
- pyworkflow/project/scripts/stack2volume.py +41 -0
- pyworkflow/project/scripts/stop.py +81 -0
- pyworkflow/protocol/__init__.py +38 -0
- pyworkflow/protocol/bibtex.py +48 -0
- pyworkflow/protocol/constants.py +86 -0
- pyworkflow/protocol/executor.py +334 -0
- pyworkflow/protocol/hosts.py +313 -0
- pyworkflow/protocol/launch.py +270 -0
- pyworkflow/protocol/package.py +42 -0
- pyworkflow/protocol/params.py +744 -0
- pyworkflow/protocol/protocol.py +2554 -0
- pyworkflow/resources/Imagej.png +0 -0
- pyworkflow/resources/chimera.png +0 -0
- pyworkflow/resources/fa-exclamation-triangle_alert.png +0 -0
- pyworkflow/resources/fa-info-circle_alert.png +0 -0
- pyworkflow/resources/fa-search.png +0 -0
- pyworkflow/resources/fa-times-circle_alert.png +0 -0
- pyworkflow/resources/file_vol.png +0 -0
- pyworkflow/resources/loading.gif +0 -0
- pyworkflow/resources/no-image128.png +0 -0
- pyworkflow/resources/scipion_bn.png +0 -0
- pyworkflow/resources/scipion_icon.png +0 -0
- pyworkflow/resources/scipion_icon.svg +397 -0
- pyworkflow/resources/scipion_icon_proj.png +0 -0
- pyworkflow/resources/scipion_icon_projs.png +0 -0
- pyworkflow/resources/scipion_icon_prot.png +0 -0
- pyworkflow/resources/scipion_logo.png +0 -0
- pyworkflow/resources/scipion_logo_normal.png +0 -0
- pyworkflow/resources/scipion_logo_small.png +0 -0
- pyworkflow/resources/sprites.png +0 -0
- pyworkflow/resources/sprites.xcf +0 -0
- pyworkflow/resources/wait.gif +0 -0
- pyworkflow/template.py +322 -0
- pyworkflow/tests/__init__.py +29 -0
- pyworkflow/tests/test_utils.py +25 -0
- pyworkflow/tests/tests.py +341 -0
- pyworkflow/utils/__init__.py +38 -0
- pyworkflow/utils/dataset.py +414 -0
- pyworkflow/utils/echo.py +104 -0
- pyworkflow/utils/graph.py +196 -0
- pyworkflow/utils/log.py +284 -0
- pyworkflow/utils/path.py +527 -0
- pyworkflow/utils/process.py +132 -0
- pyworkflow/utils/profiler.py +92 -0
- pyworkflow/utils/progressbar.py +154 -0
- pyworkflow/utils/properties.py +627 -0
- pyworkflow/utils/reflection.py +129 -0
- pyworkflow/utils/utils.py +877 -0
- pyworkflow/utils/which.py +229 -0
- pyworkflow/viewer.py +328 -0
- pyworkflow/webservices/__init__.py +8 -0
- pyworkflow/webservices/config.py +11 -0
- pyworkflow/webservices/notifier.py +162 -0
- pyworkflow/webservices/repository.py +59 -0
- pyworkflow/webservices/workflowhub.py +74 -0
- pyworkflow/wizard.py +64 -0
- pyworkflowtests/__init__.py +51 -0
- pyworkflowtests/bibtex.py +51 -0
- pyworkflowtests/objects.py +830 -0
- pyworkflowtests/protocols.py +154 -0
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +72 -0
- pyworkflowtests/tests/test_domain.py +45 -0
- pyworkflowtests/tests/test_logs.py +74 -0
- pyworkflowtests/tests/test_mappers.py +392 -0
- pyworkflowtests/tests/test_object.py +507 -0
- pyworkflowtests/tests/test_project.py +42 -0
- pyworkflowtests/tests/test_protocol_execution.py +72 -0
- pyworkflowtests/tests/test_protocol_export.py +78 -0
- pyworkflowtests/tests/test_protocol_output.py +158 -0
- pyworkflowtests/tests/test_streaming.py +47 -0
- pyworkflowtests/tests/test_utils.py +210 -0
- scipion_pyworkflow-3.7.0.dist-info/LICENSE.txt +674 -0
- scipion_pyworkflow-3.7.0.dist-info/METADATA +107 -0
- scipion_pyworkflow-3.7.0.dist-info/RECORD +140 -0
- scipion_pyworkflow-3.7.0.dist-info/WHEEL +5 -0
- scipion_pyworkflow-3.7.0.dist-info/dependency_links.txt +1 -0
- scipion_pyworkflow-3.7.0.dist-info/entry_points.txt +5 -0
- scipion_pyworkflow-3.7.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,830 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
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
|
+
Definition of Mock objects to be used within the tests in the Mock Domain
|
27
|
+
"""
|
28
|
+
|
29
|
+
import os
|
30
|
+
import logging
|
31
|
+
logger = logging.getLogger(__name__)
|
32
|
+
|
33
|
+
import pyworkflow.object as pwobj
|
34
|
+
from pyworkflow.utils import cleanPath
|
35
|
+
|
36
|
+
NO_INDEX = 0
|
37
|
+
|
38
|
+
|
39
|
+
class MockObject(pwobj.Object):
|
40
|
+
"""Base object for all Mock classes"""
|
41
|
+
def __init__(self, **kwargs):
|
42
|
+
super().__init__(**kwargs)
|
43
|
+
|
44
|
+
def __str__(self):
|
45
|
+
return self.getClassName()
|
46
|
+
|
47
|
+
def getFiles(self):
|
48
|
+
""" Get all filePaths """
|
49
|
+
return None
|
50
|
+
|
51
|
+
# Maybe something to be moved to Object?
|
52
|
+
def __eq__(self, other):
|
53
|
+
if self._objValue is None:
|
54
|
+
return self.__class__ == other.__class__ and self._objId == other._objId
|
55
|
+
return self._objValue == other._objValue
|
56
|
+
|
57
|
+
|
58
|
+
class Complex(MockObject):
|
59
|
+
""" Simple class used for tests here. """
|
60
|
+
|
61
|
+
cGold = complex(1.0, 1.0)
|
62
|
+
|
63
|
+
def __init__(self, imag=0., real=0., **args):
|
64
|
+
MockObject.__init__(self, **args)
|
65
|
+
self.imag = pwobj.Float(imag)
|
66
|
+
self.real = pwobj.Float(real)
|
67
|
+
# Create reference complex values
|
68
|
+
|
69
|
+
def __str__(self):
|
70
|
+
return '(%s, %s)' % (self.imag, self.real)
|
71
|
+
|
72
|
+
def __eq__(self, other):
|
73
|
+
return (self.imag == other.imag and
|
74
|
+
self.real == other.real)
|
75
|
+
|
76
|
+
def hasValue(self):
|
77
|
+
return True
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def createComplex(cls):
|
81
|
+
"""Create a Complex object and set
|
82
|
+
values with cls.cGold standard"""
|
83
|
+
c = Complex() # Create Complex object and set values
|
84
|
+
c.imag.set(cls.cGold.imag)
|
85
|
+
c.real.set(cls.cGold.real)
|
86
|
+
return c
|
87
|
+
|
88
|
+
|
89
|
+
class MockAcquisition(MockObject):
|
90
|
+
"""Acquisition information"""
|
91
|
+
def __init__(self, **kwargs):
|
92
|
+
MockObject.__init__(self, **kwargs)
|
93
|
+
self._magnification = pwobj.Float(kwargs.get('magnification', None))
|
94
|
+
# Microscope voltage in kV
|
95
|
+
self._voltage = pwobj.Float(kwargs.get('voltage', None))
|
96
|
+
# Spherical aberration in mm
|
97
|
+
self._sphericalAberration = pwobj.Float(kwargs.get('sphericalAberration',
|
98
|
+
None))
|
99
|
+
self._amplitudeContrast = pwobj.Float(kwargs.get('amplitudeContrast', None))
|
100
|
+
self._doseInitial = pwobj.Float(kwargs.get('doseInitial', 0))
|
101
|
+
self._dosePerFrame = pwobj.Float(kwargs.get('dosePerFrame', None))
|
102
|
+
|
103
|
+
def copyInfo(self, other):
|
104
|
+
self.copyAttributes(other, '_magnification', '_voltage',
|
105
|
+
'_sphericalAberration', '_amplitudeContrast',
|
106
|
+
'_doseInitial', '_dosePerFrame')
|
107
|
+
|
108
|
+
def getMagnification(self):
|
109
|
+
return self._magnification.get()
|
110
|
+
|
111
|
+
def setMagnification(self, value):
|
112
|
+
self._magnification.set(value)
|
113
|
+
|
114
|
+
def getVoltage(self):
|
115
|
+
return self._voltage.get()
|
116
|
+
|
117
|
+
def setVoltage(self, value):
|
118
|
+
self._voltage.set(value)
|
119
|
+
|
120
|
+
def getSphericalAberration(self):
|
121
|
+
return self._sphericalAberration.get()
|
122
|
+
|
123
|
+
def setSphericalAberration(self, value):
|
124
|
+
self._sphericalAberration.set(value)
|
125
|
+
|
126
|
+
def getAmplitudeContrast(self):
|
127
|
+
return self._amplitudeContrast.get()
|
128
|
+
|
129
|
+
def setAmplitudeContrast(self, value):
|
130
|
+
self._amplitudeContrast.set(value)
|
131
|
+
|
132
|
+
def getDoseInitial(self):
|
133
|
+
return self._doseInitial.get()
|
134
|
+
|
135
|
+
def setDoseInitial(self, value):
|
136
|
+
self._doseInitial.set(value)
|
137
|
+
|
138
|
+
def getDosePerFrame(self):
|
139
|
+
return self._dosePerFrame.get()
|
140
|
+
|
141
|
+
def setDosePerFrame(self, value):
|
142
|
+
self._dosePerFrame.set(value)
|
143
|
+
|
144
|
+
def __str__(self):
|
145
|
+
return "\n mag=%s\n volt= %s\n Cs=%s\n Q0=%s\n\n" % \
|
146
|
+
(self._magnification.get(),
|
147
|
+
self._voltage.get(),
|
148
|
+
self._sphericalAberration.get(),
|
149
|
+
self._amplitudeContrast.get())
|
150
|
+
|
151
|
+
|
152
|
+
class MockImageDim(pwobj.CsvList):
|
153
|
+
""" Just a wrapper to a pwobj.CsvList to store image dimensions
|
154
|
+
as X, Y and Z.
|
155
|
+
"""
|
156
|
+
def __init__(self, x=None, y=None, z=None):
|
157
|
+
pwobj.CsvList.__init__(self, pType=int)
|
158
|
+
if x is not None and y is not None:
|
159
|
+
self.append(x)
|
160
|
+
self.append(y)
|
161
|
+
if z is not None:
|
162
|
+
self.append(z)
|
163
|
+
|
164
|
+
def getX(self):
|
165
|
+
return self[0]
|
166
|
+
|
167
|
+
def getY(self):
|
168
|
+
return self[1]
|
169
|
+
|
170
|
+
def getZ(self):
|
171
|
+
return self[2]
|
172
|
+
|
173
|
+
def __str__(self):
|
174
|
+
if self.isEmpty():
|
175
|
+
s = 'No-Dim'
|
176
|
+
else:
|
177
|
+
s = '%d x %d' % (self.getX(), self.getY())
|
178
|
+
if self.getZ() > 1:
|
179
|
+
s += ' x %d' % self.getZ()
|
180
|
+
return s
|
181
|
+
|
182
|
+
|
183
|
+
class MockImage(MockObject):
|
184
|
+
"""Represents an EM Image object"""
|
185
|
+
def __init__(self, location=None, **kwargs):
|
186
|
+
"""
|
187
|
+
Params:
|
188
|
+
:param location: Could be a valid location: (index, filename)
|
189
|
+
or filename
|
190
|
+
"""
|
191
|
+
MockObject.__init__(self, **kwargs)
|
192
|
+
# Image location is composed by an index and a filename
|
193
|
+
self._index = pwobj.Integer(0)
|
194
|
+
self._filename = pwobj.String()
|
195
|
+
self._samplingRate = pwobj.Float()
|
196
|
+
self._ctfModel = None
|
197
|
+
self._acquisition = None
|
198
|
+
if location:
|
199
|
+
self.setLocation(location)
|
200
|
+
|
201
|
+
def getSamplingRate(self):
|
202
|
+
""" Return image sampling rate. (A/pix) """
|
203
|
+
return self._samplingRate.get()
|
204
|
+
|
205
|
+
def setSamplingRate(self, sampling):
|
206
|
+
self._samplingRate.set(sampling)
|
207
|
+
|
208
|
+
def getFormat(self):
|
209
|
+
pass
|
210
|
+
|
211
|
+
def getDataType(self):
|
212
|
+
pass
|
213
|
+
|
214
|
+
def getDimensions(self):
|
215
|
+
"""getDimensions is redundant here but not in setOfVolumes
|
216
|
+
create it makes easier to create protocols for both images
|
217
|
+
and sets of images
|
218
|
+
"""
|
219
|
+
return self.getDim()
|
220
|
+
|
221
|
+
def getDim(self):
|
222
|
+
return 0, 0, 0 # We don't need dim now
|
223
|
+
|
224
|
+
def getXDim(self):
|
225
|
+
return self.getDim()[0] if self.getDim() is not None else 0
|
226
|
+
|
227
|
+
def getYDim(self):
|
228
|
+
return self.getDim()[1] if self.getDim() is not None else 0
|
229
|
+
|
230
|
+
def getIndex(self):
|
231
|
+
return self._index.get()
|
232
|
+
|
233
|
+
def setIndex(self, index):
|
234
|
+
self._index.set(index)
|
235
|
+
|
236
|
+
def getFileName(self):
|
237
|
+
""" Use the _objValue attribute to store filename. """
|
238
|
+
return self._filename.get()
|
239
|
+
|
240
|
+
def setFileName(self, filename):
|
241
|
+
""" Use the _objValue attribute to store filename. """
|
242
|
+
self._filename.set(filename)
|
243
|
+
|
244
|
+
def getLocation(self):
|
245
|
+
""" This function return the image index and filename.
|
246
|
+
It will only differs from getFileName, when the image
|
247
|
+
is contained in a stack and the index make sense.
|
248
|
+
"""
|
249
|
+
return self.getIndex(), self.getFileName()
|
250
|
+
|
251
|
+
def setLocation(self, *args):
|
252
|
+
""" Set the image location, see getLocation.
|
253
|
+
Params:
|
254
|
+
First argument can be:
|
255
|
+
1. a tuple with (index, filename)
|
256
|
+
2. a index, this implies a second argument with filename
|
257
|
+
3. a filename, this implies index=NO_INDEX
|
258
|
+
"""
|
259
|
+
first = args[0]
|
260
|
+
t = type(first)
|
261
|
+
if t == tuple:
|
262
|
+
index, filename = first
|
263
|
+
elif t == int:
|
264
|
+
index, filename = first, args[1]
|
265
|
+
elif t == str:
|
266
|
+
index, filename = NO_INDEX, first
|
267
|
+
else:
|
268
|
+
raise Exception('setLocation: unsupported type %s as input.' % t)
|
269
|
+
|
270
|
+
self.setIndex(index)
|
271
|
+
self.setFileName(filename)
|
272
|
+
|
273
|
+
def getBaseName(self):
|
274
|
+
return os.path.basename(self.getFileName())
|
275
|
+
|
276
|
+
def copyInfo(self, other):
|
277
|
+
""" Copy basic information """
|
278
|
+
self.copyAttributes(other, '_samplingRate')
|
279
|
+
|
280
|
+
def copyLocation(self, other):
|
281
|
+
""" Copy location index and filename from other image. """
|
282
|
+
self.setIndex(other.getIndex())
|
283
|
+
self.setFileName(other.getFileName())
|
284
|
+
|
285
|
+
def hasCTF(self):
|
286
|
+
return self._ctfModel is not None
|
287
|
+
|
288
|
+
def getCTF(self):
|
289
|
+
""" Return the CTF model """
|
290
|
+
return self._ctfModel
|
291
|
+
|
292
|
+
def setCTF(self, newCTF):
|
293
|
+
self._ctfModel = newCTF
|
294
|
+
|
295
|
+
def hasAcquisition(self):
|
296
|
+
return (self._acquisition is not None and
|
297
|
+
self._acquisition.getVoltage() is not None and
|
298
|
+
self._acquisition.getMagnification() is not None
|
299
|
+
)
|
300
|
+
|
301
|
+
def getAcquisition(self):
|
302
|
+
return self._acquisition
|
303
|
+
|
304
|
+
def setAcquisition(self, acquisition):
|
305
|
+
self._acquisition = acquisition
|
306
|
+
|
307
|
+
def hasTransform(self):
|
308
|
+
return self._transform is not None
|
309
|
+
|
310
|
+
def getTransform(self):
|
311
|
+
return self._transform
|
312
|
+
|
313
|
+
def setTransform(self, newTransform):
|
314
|
+
self._transform = newTransform
|
315
|
+
|
316
|
+
def hasOrigin(self):
|
317
|
+
return self._origin is not None
|
318
|
+
|
319
|
+
def getOrigin(self, force=False):
|
320
|
+
"""shifts in A"""
|
321
|
+
if self.hasOrigin():
|
322
|
+
return self._origin
|
323
|
+
else:
|
324
|
+
if force:
|
325
|
+
return self._getDefaultOrigin()
|
326
|
+
else:
|
327
|
+
return None
|
328
|
+
|
329
|
+
def _getDefaultOrigin(self):
|
330
|
+
# original code from em required Transform, which will require
|
331
|
+
# Matrix and then import numpy. Since this method is not used in
|
332
|
+
# the tests I'm emptying it.
|
333
|
+
pass
|
334
|
+
|
335
|
+
def getShiftsiftsFromOrigin(self):
|
336
|
+
origin = self.getOrigin(force=True).getShifts()
|
337
|
+
x = origin[0]
|
338
|
+
y = origin[1]
|
339
|
+
z = origin[2]
|
340
|
+
return x, y, z
|
341
|
+
# x, y, z are floats in Angstroms
|
342
|
+
|
343
|
+
def setShiftsInOrigin(self, x, y, z):
|
344
|
+
origin = self.getOrigin(force=True)
|
345
|
+
origin.setShifts(x, y, z)
|
346
|
+
|
347
|
+
def setOrigin(self, newOrigin):
|
348
|
+
"""shifts in A"""
|
349
|
+
self._origin = newOrigin
|
350
|
+
|
351
|
+
def originResampled(self, originNotResampled, oldSampling):
|
352
|
+
factor = self.getSamplingRate() / oldSampling
|
353
|
+
shifts = originNotResampled.getShifts()
|
354
|
+
origin = self.getOrigin(force=True)
|
355
|
+
origin.setShifts(shifts[0] * factor,
|
356
|
+
shifts[1] * factor,
|
357
|
+
shifts[2] * factor)
|
358
|
+
return origin
|
359
|
+
|
360
|
+
def __str__(self):
|
361
|
+
""" String representation of an Image. """
|
362
|
+
dim = self.getDim()
|
363
|
+
dimStr = str(MockImageDim(*dim)) if dim else 'No-Dim'
|
364
|
+
return ("%s (%s, %0.2f Å/px)"
|
365
|
+
% (self.getClassName(), dimStr,
|
366
|
+
self.getSamplingRate() or 99999.))
|
367
|
+
|
368
|
+
def getFiles(self):
|
369
|
+
filePaths = set()
|
370
|
+
filePaths.add(self.getFileName())
|
371
|
+
return filePaths
|
372
|
+
|
373
|
+
|
374
|
+
class MockMicrograph(MockImage):
|
375
|
+
""" Represents an EM Micrograph object """
|
376
|
+
def __init__(self, location=None, **kwargs):
|
377
|
+
MockImage.__init__(self, location, **kwargs)
|
378
|
+
self._micName = pwobj.String()
|
379
|
+
|
380
|
+
def setMicName(self, micName):
|
381
|
+
self._micName.set(micName)
|
382
|
+
|
383
|
+
def getMicName(self):
|
384
|
+
if self._micName.get():
|
385
|
+
return self._micName.get()
|
386
|
+
else:
|
387
|
+
self.getFileName()
|
388
|
+
|
389
|
+
def copyInfo(self, other):
|
390
|
+
""" Copy basic information """
|
391
|
+
MockImage.copyInfo(self, other)
|
392
|
+
self.setMicName(other.getMicName())
|
393
|
+
|
394
|
+
|
395
|
+
class MockParticle(MockImage):
|
396
|
+
""" Represents an EM Particle object """
|
397
|
+
def __init__(self, location=None, **kwargs):
|
398
|
+
MockImage.__init__(self, location, **kwargs)
|
399
|
+
# This may be redundant, but make the Particle
|
400
|
+
# object more independent for tracking coordinates
|
401
|
+
self._coordinate = None
|
402
|
+
self._micId = pwobj.Integer()
|
403
|
+
self._classId = pwobj.Integer()
|
404
|
+
|
405
|
+
def hasCoordinate(self):
|
406
|
+
return self._coordinate is not None
|
407
|
+
|
408
|
+
def setCoordinate(self, coordinate):
|
409
|
+
self._coordinate = coordinate
|
410
|
+
|
411
|
+
def getCoordinate(self):
|
412
|
+
return self._coordinate
|
413
|
+
|
414
|
+
def scaleCoordinate(self, factor):
|
415
|
+
self.getCoordinate().scale(factor)
|
416
|
+
|
417
|
+
def getMicId(self):
|
418
|
+
""" Return the micrograph id if the coordinate is not None.
|
419
|
+
or have set the _micId property.
|
420
|
+
"""
|
421
|
+
if self._micId.hasValue():
|
422
|
+
return self._micId.get()
|
423
|
+
if self.hasCoordinate():
|
424
|
+
return self.getCoordinate().getMicId()
|
425
|
+
|
426
|
+
return None
|
427
|
+
|
428
|
+
def setMicId(self, micId):
|
429
|
+
self._micId.set(micId)
|
430
|
+
|
431
|
+
def hasMicId(self):
|
432
|
+
return self.getMicId() is not None
|
433
|
+
|
434
|
+
def getClassId(self):
|
435
|
+
return self._classId.get()
|
436
|
+
|
437
|
+
def setClassId(self, classId):
|
438
|
+
self._classId.set(classId)
|
439
|
+
|
440
|
+
def hasClassId(self):
|
441
|
+
return self._classId.hasValue()
|
442
|
+
|
443
|
+
|
444
|
+
class MockSet(pwobj.Set, MockObject):
|
445
|
+
|
446
|
+
def _loadClassesDict(self):
|
447
|
+
from pyworkflow.plugin import Domain
|
448
|
+
classDict = Domain.getObjects()
|
449
|
+
classDict.update(globals())
|
450
|
+
|
451
|
+
return classDict
|
452
|
+
|
453
|
+
def copyInfo(self, other):
|
454
|
+
""" Define a dummy copyInfo function to be used
|
455
|
+
for some generic operations on sets.
|
456
|
+
"""
|
457
|
+
pass
|
458
|
+
|
459
|
+
def clone(self):
|
460
|
+
""" Override the clone defined in Object
|
461
|
+
to avoid copying _mapperPath property
|
462
|
+
"""
|
463
|
+
pass
|
464
|
+
|
465
|
+
def copyItems(self, otherSet,
|
466
|
+
updateItemCallback=None,
|
467
|
+
itemDataIterator=None,
|
468
|
+
copyDisabled=False,
|
469
|
+
itemSelectedCallback=None):
|
470
|
+
""" Copy items from another set.
|
471
|
+
If the updateItemCallback is passed, it will be
|
472
|
+
called with each item (and optionally with a data row).
|
473
|
+
This is a place where items can be updated while copying.
|
474
|
+
This is useful to set new attributes or update values
|
475
|
+
for each item.
|
476
|
+
"""
|
477
|
+
|
478
|
+
if itemSelectedCallback is None:
|
479
|
+
itemSelectedCallback = self.isItemEnabled
|
480
|
+
|
481
|
+
for item in otherSet:
|
482
|
+
# copy items if enabled or copyDisabled=True
|
483
|
+
if copyDisabled or itemSelectedCallback(item):
|
484
|
+
newItem = item.clone()
|
485
|
+
if updateItemCallback:
|
486
|
+
row = None if itemDataIterator is None \
|
487
|
+
else next(itemDataIterator)
|
488
|
+
updateItemCallback(newItem, row)
|
489
|
+
# If updateCallBack function returns attribute
|
490
|
+
# _appendItem to False do not append the item
|
491
|
+
if getattr(newItem, "_appendItem", True):
|
492
|
+
self.append(newItem)
|
493
|
+
else:
|
494
|
+
if itemDataIterator is not None:
|
495
|
+
next(itemDataIterator) # just skip disabled data row
|
496
|
+
|
497
|
+
def getFiles(self):
|
498
|
+
return pwobj.Set.getFiles(self)
|
499
|
+
|
500
|
+
@classmethod
|
501
|
+
def create(cls, outputPath,
|
502
|
+
prefix=None, suffix=None, ext=None,
|
503
|
+
**kwargs):
|
504
|
+
""" Create an empty set from the current Set class.
|
505
|
+
Params:
|
506
|
+
outputPath: where the output file will be written.
|
507
|
+
prefix: prefix of the created file, if None, it will be deduced
|
508
|
+
from the ClassName.
|
509
|
+
suffix: additional suffix that will be added to the prefix with a
|
510
|
+
"_" in between.
|
511
|
+
ext: extension of the output file, be default will use .sqlite
|
512
|
+
"""
|
513
|
+
fn = prefix or cls.__name__.lower().replace('setof', '')
|
514
|
+
|
515
|
+
if suffix:
|
516
|
+
fn += '_%s' % suffix
|
517
|
+
|
518
|
+
fn += '.%s' % (ext or 'sqlite')
|
519
|
+
|
520
|
+
setPath = os.path.join(outputPath, fn)
|
521
|
+
cleanPath(setPath)
|
522
|
+
|
523
|
+
return cls(filename=setPath, **kwargs)
|
524
|
+
|
525
|
+
|
526
|
+
class MockSetOfImages(MockSet):
|
527
|
+
""" Represents a set of Images """
|
528
|
+
ITEM_TYPE = MockImage
|
529
|
+
|
530
|
+
def __init__(self, **kwargs):
|
531
|
+
MockSet.__init__(self, **kwargs)
|
532
|
+
self._samplingRate = pwobj.Float()
|
533
|
+
self._hasCtf = pwobj.Boolean(kwargs.get('ctf', False))
|
534
|
+
self._isPhaseFlipped = pwobj.Boolean(False)
|
535
|
+
self._isAmplitudeCorrected = pwobj.Boolean(False)
|
536
|
+
self._acquisition = MockAcquisition()
|
537
|
+
self._firstDim = MockImageDim() # Dimensions of the first image
|
538
|
+
|
539
|
+
def getAcquisition(self):
|
540
|
+
return self._acquisition
|
541
|
+
|
542
|
+
def setAcquisition(self, acquisition):
|
543
|
+
self._acquisition = acquisition
|
544
|
+
|
545
|
+
def hasAcquisition(self):
|
546
|
+
return self._acquisition.getMagnification() is not None
|
547
|
+
|
548
|
+
def append(self, image):
|
549
|
+
""" Add a image to the set. """
|
550
|
+
# If the sampling rate was set before, the same value
|
551
|
+
# will be set for each image added to the set
|
552
|
+
if self.getSamplingRate() or not image.getSamplingRate():
|
553
|
+
image.setSamplingRate(self.getSamplingRate())
|
554
|
+
# Copy the acquisition from the set to images
|
555
|
+
# only override image acquisition if setofImages acquisition
|
556
|
+
# is not none
|
557
|
+
if self.hasAcquisition():
|
558
|
+
# TODO: image acquisition should not be overwritten
|
559
|
+
if not image.hasAcquisition():
|
560
|
+
image.setAcquisition(self.getAcquisition())
|
561
|
+
# Store the dimensions of the first image, just to
|
562
|
+
# avoid reading image files for further queries to dimensions
|
563
|
+
# only check this for first time append is called
|
564
|
+
if self.isEmpty():
|
565
|
+
self._setFirstDim(image)
|
566
|
+
|
567
|
+
MockSet.append(self, image)
|
568
|
+
|
569
|
+
def _setFirstDim(self, image):
|
570
|
+
""" Store dimensions when the first image is found.
|
571
|
+
This function should be called only once, to avoid reading
|
572
|
+
dimension from image file. """
|
573
|
+
if self._firstDim.isEmpty():
|
574
|
+
self._firstDim.set(image.getDim())
|
575
|
+
|
576
|
+
def copyInfo(self, other):
|
577
|
+
""" Copy basic information (sampling rate and ctf)
|
578
|
+
from other set of images to current one"""
|
579
|
+
self.copyAttributes(other, '_samplingRate', '_isPhaseFlipped',
|
580
|
+
'_isAmplitudeCorrected', '_alignment')
|
581
|
+
self._acquisition.copyInfo(other._acquisition)
|
582
|
+
|
583
|
+
def getFiles(self):
|
584
|
+
filePaths = set()
|
585
|
+
uniqueFiles = self.aggregate(['count'], '_filename', ['_filename'])
|
586
|
+
|
587
|
+
for row in uniqueFiles:
|
588
|
+
filePaths.add(row['_filename'])
|
589
|
+
return filePaths
|
590
|
+
|
591
|
+
def setDownsample(self, downFactor):
|
592
|
+
""" Update the values of samplingRate and scannedPixelSize
|
593
|
+
after applying a downsampling factor of downFactor.
|
594
|
+
"""
|
595
|
+
self.setSamplingRate(self.getSamplingRate() * downFactor)
|
596
|
+
|
597
|
+
def setSamplingRate(self, samplingRate):
|
598
|
+
""" Set the sampling rate and adjust the scannedPixelSize. """
|
599
|
+
self._samplingRate.set(samplingRate)
|
600
|
+
|
601
|
+
def getSamplingRate(self):
|
602
|
+
return self._samplingRate.get()
|
603
|
+
|
604
|
+
def getDim(self):
|
605
|
+
""" Return the dimensions of the first image in the set. """
|
606
|
+
if self._firstDim.isEmpty():
|
607
|
+
return None
|
608
|
+
x, y, z = self._firstDim
|
609
|
+
return x, y, z
|
610
|
+
|
611
|
+
def setDim(self, newDim):
|
612
|
+
self._firstDim.set(newDim)
|
613
|
+
|
614
|
+
def getXDim(self):
|
615
|
+
return self.getDim()[0] if self.getDim() is not None else 0
|
616
|
+
|
617
|
+
def isOddX(self):
|
618
|
+
""" Return True if the first item x dimension is odd. """
|
619
|
+
return self.getXDim() % 2 == 1
|
620
|
+
|
621
|
+
def getDimensions(self):
|
622
|
+
"""Return first image dimensions as a tuple: (xdim, ydim, zdim)"""
|
623
|
+
return self.getFirstItem().getDim()
|
624
|
+
|
625
|
+
def __str__(self):
|
626
|
+
""" String representation of a set of images. """
|
627
|
+
sampling = self.getSamplingRate()
|
628
|
+
|
629
|
+
if not sampling:
|
630
|
+
logger.info("FATAL ERROR: Object %s has no sampling rate!!!"
|
631
|
+
% self.getName())
|
632
|
+
sampling = -999.0
|
633
|
+
|
634
|
+
s = "%s (%d items, %s, %0.2f Å/px%s)" % \
|
635
|
+
(self.getClassName(), self.getSize(),
|
636
|
+
self._dimStr(), sampling, self._appendStreamState())
|
637
|
+
return s
|
638
|
+
|
639
|
+
def _dimStr(self):
|
640
|
+
""" Return the string representing the dimensions. """
|
641
|
+
return str(self._firstDim)
|
642
|
+
|
643
|
+
def iterItems(self, orderBy='id', direction='ASC', where=None, limit=None):
|
644
|
+
""" Redefine iteration to set the acquisition to images. """
|
645
|
+
for img in pwobj.Set.iterItems(self, orderBy=orderBy, direction=direction,
|
646
|
+
where=where, limit=limit):
|
647
|
+
|
648
|
+
# Sometimes the images items in the set could
|
649
|
+
# have the acquisition info per data row and we
|
650
|
+
# don't want to override with the set acquisition for this case
|
651
|
+
if not img.hasAcquisition():
|
652
|
+
img.setAcquisition(self.getAcquisition())
|
653
|
+
yield img
|
654
|
+
|
655
|
+
def appendFromImages(self, imagesSet):
|
656
|
+
""" Iterate over the images and append
|
657
|
+
every image that is enabled.
|
658
|
+
"""
|
659
|
+
for img in imagesSet:
|
660
|
+
if img.isEnabled():
|
661
|
+
self.append(img)
|
662
|
+
|
663
|
+
def appendFromClasses(self, classesSet):
|
664
|
+
""" Iterate over the classes and the element inside each
|
665
|
+
class and append to the set all that are enabled.
|
666
|
+
"""
|
667
|
+
for cls in classesSet:
|
668
|
+
if cls.isEnabled() and cls.getSize() > 0:
|
669
|
+
for img in cls:
|
670
|
+
if img.isEnabled():
|
671
|
+
self.append(img)
|
672
|
+
|
673
|
+
|
674
|
+
class MockSetOfMicrographs(MockSetOfImages):
|
675
|
+
""" Create a base class for both Micrographs and Movies,
|
676
|
+
but avoid to select Movies when Micrographs are required.
|
677
|
+
"""
|
678
|
+
ITEM_TYPE = MockMicrograph
|
679
|
+
|
680
|
+
def __init__(self, **kwargs):
|
681
|
+
MockSetOfImages.__init__(self, **kwargs)
|
682
|
+
self._scannedPixelSize = pwobj.Float()
|
683
|
+
|
684
|
+
def copyInfo(self, other):
|
685
|
+
""" Copy basic information (voltage, spherical aberration and
|
686
|
+
sampling rate) from other set of micrographs to current one.
|
687
|
+
"""
|
688
|
+
MockSetOfImages.copyInfo(self, other)
|
689
|
+
self._scannedPixelSize.set(other.getScannedPixelSize())
|
690
|
+
|
691
|
+
def setSamplingRate(self, samplingRate):
|
692
|
+
""" Set the sampling rate and adjust the scannedPixelSize. """
|
693
|
+
self._samplingRate.set(samplingRate)
|
694
|
+
mag = self._acquisition.getMagnification()
|
695
|
+
if mag is None:
|
696
|
+
self._scannedPixelSize.set(None)
|
697
|
+
else:
|
698
|
+
self._scannedPixelSize.set(1e-4 * samplingRate * mag)
|
699
|
+
|
700
|
+
def getScannedPixelSize(self):
|
701
|
+
return self._scannedPixelSize.get()
|
702
|
+
|
703
|
+
def setScannedPixelSize(self, scannedPixelSize):
|
704
|
+
""" Set scannedPixelSize and update samplingRate. """
|
705
|
+
mag = self._acquisition.getMagnification()
|
706
|
+
if mag is None:
|
707
|
+
raise Exception("SetOfMicrographs: cannot set scanned pixel size "
|
708
|
+
"if Magnification is not set.")
|
709
|
+
self._scannedPixelSize.set(scannedPixelSize)
|
710
|
+
self._samplingRate.set((1e+4 * scannedPixelSize) / mag)
|
711
|
+
|
712
|
+
|
713
|
+
class MockSetOfParticles(MockSetOfImages):
|
714
|
+
""" Represents a set of Particles.
|
715
|
+
The purpose of this class is to separate the
|
716
|
+
concepts of Micrographs and Particles, even if
|
717
|
+
both are considered Images
|
718
|
+
"""
|
719
|
+
ITEM_TYPE = MockParticle
|
720
|
+
REP_TYPE = MockParticle
|
721
|
+
|
722
|
+
def __init__(self, **kwargs):
|
723
|
+
MockSetOfImages.__init__(self, **kwargs)
|
724
|
+
self._coordsPointer = pwobj.Pointer()
|
725
|
+
|
726
|
+
def hasCoordinates(self):
|
727
|
+
return self._coordsPointer.hasValue()
|
728
|
+
|
729
|
+
def getCoordinates(self):
|
730
|
+
""" Returns the SetOfCoordinates associated with
|
731
|
+
this SetOfParticles"""
|
732
|
+
return self._coordsPointer.get()
|
733
|
+
|
734
|
+
def setCoordinates(self, coordinates):
|
735
|
+
""" Set the SetOfCoordinates associates with
|
736
|
+
this set of particles.
|
737
|
+
"""
|
738
|
+
self._coordsPointer.set(coordinates)
|
739
|
+
|
740
|
+
def copyInfo(self, other):
|
741
|
+
""" Copy basic information (voltage, spherical aberration and
|
742
|
+
sampling rate) from other set of micrographs to current one.
|
743
|
+
"""
|
744
|
+
MockSetOfImages.copyInfo(self, other)
|
745
|
+
self.setHasCTF(other.hasCTF())
|
746
|
+
|
747
|
+
|
748
|
+
class MockCoordinate(MockObject):
|
749
|
+
"""This class holds the (x,y) position and other information
|
750
|
+
associated with a coordinate"""
|
751
|
+
def __init__(self, **kwargs):
|
752
|
+
MockObject.__init__(self, **kwargs)
|
753
|
+
self._micrographPointer = pwobj.Pointer(objDoStore=False)
|
754
|
+
self._x = pwobj.Integer(kwargs.get('x', None))
|
755
|
+
self._y = pwobj.Integer(kwargs.get('y', None))
|
756
|
+
self._micId = pwobj.Integer()
|
757
|
+
self._micName = pwobj.String()
|
758
|
+
|
759
|
+
def getX(self):
|
760
|
+
return self._x.get()
|
761
|
+
|
762
|
+
def setX(self, x):
|
763
|
+
self._x.set(x)
|
764
|
+
|
765
|
+
def shiftX(self, shiftX):
|
766
|
+
self._x.sum(shiftX)
|
767
|
+
|
768
|
+
def getY(self):
|
769
|
+
return self._y.get()
|
770
|
+
|
771
|
+
def setY(self, y):
|
772
|
+
self._y.set(y)
|
773
|
+
|
774
|
+
def shiftY(self, shiftY):
|
775
|
+
self._y.sum(shiftY)
|
776
|
+
|
777
|
+
def scale(self, factor):
|
778
|
+
""" Scale both x and y coordinates by a given factor.
|
779
|
+
"""
|
780
|
+
self._x.multiply(factor)
|
781
|
+
self._y.multiply(factor)
|
782
|
+
|
783
|
+
def getPosition(self):
|
784
|
+
""" Return the position of the coordinate as a (x, y) tuple.
|
785
|
+
mode: select if the position is the center of the box
|
786
|
+
or in the top left corner.
|
787
|
+
"""
|
788
|
+
return self.getX(), self.getY()
|
789
|
+
|
790
|
+
def setPosition(self, x, y):
|
791
|
+
self.setX(x)
|
792
|
+
self.setY(y)
|
793
|
+
|
794
|
+
def getMicrograph(self):
|
795
|
+
""" Return the micrograph object to which
|
796
|
+
this coordinate is associated.
|
797
|
+
"""
|
798
|
+
return self._micrographPointer.get()
|
799
|
+
|
800
|
+
def setMicrograph(self, micrograph):
|
801
|
+
""" Set the micrograph to which this coordinate belongs. """
|
802
|
+
self._micrographPointer.set(micrograph)
|
803
|
+
self._micId.set(micrograph.getObjId())
|
804
|
+
self._micName.set(micrograph.getMicName())
|
805
|
+
|
806
|
+
def copyInfo(self, coord):
|
807
|
+
""" Copy information from other coordinate. """
|
808
|
+
self.setPosition(*coord.getPosition())
|
809
|
+
self.setObjId(coord.getObjId())
|
810
|
+
self.setBoxSize(coord.getBoxSize())
|
811
|
+
|
812
|
+
def getMicId(self):
|
813
|
+
return self._micId.get()
|
814
|
+
|
815
|
+
def setMicId(self, micId):
|
816
|
+
self._micId.set(micId)
|
817
|
+
|
818
|
+
def invertY(self):
|
819
|
+
if not self.getMicrograph() is None:
|
820
|
+
dims = self.getMicrograph().getDim()
|
821
|
+
height = dims[1]
|
822
|
+
self.setY(height - self.getY())
|
823
|
+
# else: error TODO
|
824
|
+
|
825
|
+
def setMicName(self, micName):
|
826
|
+
self._micName.set(micName)
|
827
|
+
|
828
|
+
def getMicName(self):
|
829
|
+
return self._micName.get()
|
830
|
+
|