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.
Files changed (140) hide show
  1. pyworkflow/__init__.py +33 -0
  2. pyworkflow/apps/__init__.py +29 -0
  3. pyworkflow/apps/pw_manager.py +37 -0
  4. pyworkflow/apps/pw_plot.py +51 -0
  5. pyworkflow/apps/pw_project.py +113 -0
  6. pyworkflow/apps/pw_protocol_list.py +143 -0
  7. pyworkflow/apps/pw_protocol_run.py +51 -0
  8. pyworkflow/apps/pw_run_tests.py +267 -0
  9. pyworkflow/apps/pw_schedule_run.py +322 -0
  10. pyworkflow/apps/pw_sleep.py +37 -0
  11. pyworkflow/apps/pw_sync_data.py +439 -0
  12. pyworkflow/apps/pw_viewer.py +78 -0
  13. pyworkflow/config.py +536 -0
  14. pyworkflow/constants.py +212 -0
  15. pyworkflow/exceptions.py +18 -0
  16. pyworkflow/gui/__init__.py +36 -0
  17. pyworkflow/gui/browser.py +726 -0
  18. pyworkflow/gui/canvas.py +1190 -0
  19. pyworkflow/gui/dialog.py +976 -0
  20. pyworkflow/gui/form.py +2627 -0
  21. pyworkflow/gui/graph.py +247 -0
  22. pyworkflow/gui/graph_layout.py +271 -0
  23. pyworkflow/gui/gui.py +566 -0
  24. pyworkflow/gui/matplotlib_image.py +233 -0
  25. pyworkflow/gui/plotter.py +247 -0
  26. pyworkflow/gui/project/__init__.py +25 -0
  27. pyworkflow/gui/project/base.py +192 -0
  28. pyworkflow/gui/project/constants.py +139 -0
  29. pyworkflow/gui/project/labels.py +205 -0
  30. pyworkflow/gui/project/project.py +484 -0
  31. pyworkflow/gui/project/searchprotocol.py +154 -0
  32. pyworkflow/gui/project/searchrun.py +181 -0
  33. pyworkflow/gui/project/steps.py +166 -0
  34. pyworkflow/gui/project/utils.py +332 -0
  35. pyworkflow/gui/project/variables.py +179 -0
  36. pyworkflow/gui/project/viewdata.py +472 -0
  37. pyworkflow/gui/project/viewprojects.py +510 -0
  38. pyworkflow/gui/project/viewprotocols.py +2093 -0
  39. pyworkflow/gui/project/viewprotocols_extra.py +560 -0
  40. pyworkflow/gui/text.py +771 -0
  41. pyworkflow/gui/tooltip.py +185 -0
  42. pyworkflow/gui/tree.py +684 -0
  43. pyworkflow/gui/widgets.py +307 -0
  44. pyworkflow/mapper/__init__.py +26 -0
  45. pyworkflow/mapper/mapper.py +222 -0
  46. pyworkflow/mapper/sqlite.py +1578 -0
  47. pyworkflow/mapper/sqlite_db.py +145 -0
  48. pyworkflow/object.py +1512 -0
  49. pyworkflow/plugin.py +712 -0
  50. pyworkflow/project/__init__.py +31 -0
  51. pyworkflow/project/config.py +451 -0
  52. pyworkflow/project/manager.py +179 -0
  53. pyworkflow/project/project.py +1990 -0
  54. pyworkflow/project/scripts/clean_projects.py +77 -0
  55. pyworkflow/project/scripts/config.py +92 -0
  56. pyworkflow/project/scripts/create.py +77 -0
  57. pyworkflow/project/scripts/edit_workflow.py +90 -0
  58. pyworkflow/project/scripts/fix_links.py +39 -0
  59. pyworkflow/project/scripts/load.py +87 -0
  60. pyworkflow/project/scripts/refresh.py +83 -0
  61. pyworkflow/project/scripts/schedule.py +111 -0
  62. pyworkflow/project/scripts/stack2volume.py +41 -0
  63. pyworkflow/project/scripts/stop.py +81 -0
  64. pyworkflow/protocol/__init__.py +38 -0
  65. pyworkflow/protocol/bibtex.py +48 -0
  66. pyworkflow/protocol/constants.py +86 -0
  67. pyworkflow/protocol/executor.py +334 -0
  68. pyworkflow/protocol/hosts.py +313 -0
  69. pyworkflow/protocol/launch.py +270 -0
  70. pyworkflow/protocol/package.py +42 -0
  71. pyworkflow/protocol/params.py +744 -0
  72. pyworkflow/protocol/protocol.py +2554 -0
  73. pyworkflow/resources/Imagej.png +0 -0
  74. pyworkflow/resources/chimera.png +0 -0
  75. pyworkflow/resources/fa-exclamation-triangle_alert.png +0 -0
  76. pyworkflow/resources/fa-info-circle_alert.png +0 -0
  77. pyworkflow/resources/fa-search.png +0 -0
  78. pyworkflow/resources/fa-times-circle_alert.png +0 -0
  79. pyworkflow/resources/file_vol.png +0 -0
  80. pyworkflow/resources/loading.gif +0 -0
  81. pyworkflow/resources/no-image128.png +0 -0
  82. pyworkflow/resources/scipion_bn.png +0 -0
  83. pyworkflow/resources/scipion_icon.png +0 -0
  84. pyworkflow/resources/scipion_icon.svg +397 -0
  85. pyworkflow/resources/scipion_icon_proj.png +0 -0
  86. pyworkflow/resources/scipion_icon_projs.png +0 -0
  87. pyworkflow/resources/scipion_icon_prot.png +0 -0
  88. pyworkflow/resources/scipion_logo.png +0 -0
  89. pyworkflow/resources/scipion_logo_normal.png +0 -0
  90. pyworkflow/resources/scipion_logo_small.png +0 -0
  91. pyworkflow/resources/sprites.png +0 -0
  92. pyworkflow/resources/sprites.xcf +0 -0
  93. pyworkflow/resources/wait.gif +0 -0
  94. pyworkflow/template.py +322 -0
  95. pyworkflow/tests/__init__.py +29 -0
  96. pyworkflow/tests/test_utils.py +25 -0
  97. pyworkflow/tests/tests.py +341 -0
  98. pyworkflow/utils/__init__.py +38 -0
  99. pyworkflow/utils/dataset.py +414 -0
  100. pyworkflow/utils/echo.py +104 -0
  101. pyworkflow/utils/graph.py +196 -0
  102. pyworkflow/utils/log.py +284 -0
  103. pyworkflow/utils/path.py +527 -0
  104. pyworkflow/utils/process.py +132 -0
  105. pyworkflow/utils/profiler.py +92 -0
  106. pyworkflow/utils/progressbar.py +154 -0
  107. pyworkflow/utils/properties.py +627 -0
  108. pyworkflow/utils/reflection.py +129 -0
  109. pyworkflow/utils/utils.py +877 -0
  110. pyworkflow/utils/which.py +229 -0
  111. pyworkflow/viewer.py +328 -0
  112. pyworkflow/webservices/__init__.py +8 -0
  113. pyworkflow/webservices/config.py +11 -0
  114. pyworkflow/webservices/notifier.py +162 -0
  115. pyworkflow/webservices/repository.py +59 -0
  116. pyworkflow/webservices/workflowhub.py +74 -0
  117. pyworkflow/wizard.py +64 -0
  118. pyworkflowtests/__init__.py +51 -0
  119. pyworkflowtests/bibtex.py +51 -0
  120. pyworkflowtests/objects.py +830 -0
  121. pyworkflowtests/protocols.py +154 -0
  122. pyworkflowtests/tests/__init__.py +0 -0
  123. pyworkflowtests/tests/test_canvas.py +72 -0
  124. pyworkflowtests/tests/test_domain.py +45 -0
  125. pyworkflowtests/tests/test_logs.py +74 -0
  126. pyworkflowtests/tests/test_mappers.py +392 -0
  127. pyworkflowtests/tests/test_object.py +507 -0
  128. pyworkflowtests/tests/test_project.py +42 -0
  129. pyworkflowtests/tests/test_protocol_execution.py +72 -0
  130. pyworkflowtests/tests/test_protocol_export.py +78 -0
  131. pyworkflowtests/tests/test_protocol_output.py +158 -0
  132. pyworkflowtests/tests/test_streaming.py +47 -0
  133. pyworkflowtests/tests/test_utils.py +210 -0
  134. scipion_pyworkflow-3.7.0.dist-info/LICENSE.txt +674 -0
  135. scipion_pyworkflow-3.7.0.dist-info/METADATA +107 -0
  136. scipion_pyworkflow-3.7.0.dist-info/RECORD +140 -0
  137. scipion_pyworkflow-3.7.0.dist-info/WHEEL +5 -0
  138. scipion_pyworkflow-3.7.0.dist-info/dependency_links.txt +1 -0
  139. scipion_pyworkflow-3.7.0.dist-info/entry_points.txt +5 -0
  140. 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
+