microeye 2.3.2__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 (190) hide show
  1. microEye/__init__.py +47 -0
  2. microEye/_version.py +2 -0
  3. microEye/analysis/__init__.py +1 -0
  4. microEye/analysis/checklist_dialog.py +143 -0
  5. microEye/analysis/cmosMaps.py +228 -0
  6. microEye/analysis/filters/__init__.py +9 -0
  7. microEye/analysis/filters/base.py +21 -0
  8. microEye/analysis/filters/spatial.py +338 -0
  9. microEye/analysis/filters/temporal.py +76 -0
  10. microEye/analysis/fitting/__init__.py +0 -0
  11. microEye/analysis/fitting/fit.py +680 -0
  12. microEye/analysis/fitting/nena.py +375 -0
  13. microEye/analysis/fitting/phasor_fit.py +90 -0
  14. microEye/analysis/fitting/processing.py +317 -0
  15. microEye/analysis/fitting/psf/__init__.py +6 -0
  16. microEye/analysis/fitting/psf/extract.py +1129 -0
  17. microEye/analysis/fitting/psf/rubost_mean.py +150 -0
  18. microEye/analysis/fitting/psf/spline.py +167 -0
  19. microEye/analysis/fitting/psf/stats/__init__.py +12 -0
  20. microEye/analysis/fitting/psf/stats/core.py +295 -0
  21. microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
  22. microEye/analysis/fitting/psf/stats/io.py +104 -0
  23. microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
  24. microEye/analysis/fitting/psf/temp.py +147 -0
  25. microEye/analysis/fitting/psf/test.py +47 -0
  26. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
  27. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
  28. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
  29. microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
  30. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
  31. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
  32. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
  33. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
  34. microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
  35. microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
  36. microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
  37. microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
  38. microEye/analysis/fitting/results.py +917 -0
  39. microEye/analysis/fitting/results_stats.py +251 -0
  40. microEye/analysis/fitting/tardis.py +209 -0
  41. microEye/analysis/multi_viewer.py +568 -0
  42. microEye/analysis/processing/__init__.py +1 -0
  43. microEye/analysis/processing/frc.py +120 -0
  44. microEye/analysis/rendering/__init__.py +3 -0
  45. microEye/analysis/rendering/base.py +452 -0
  46. microEye/analysis/rendering/cloud.py +215 -0
  47. microEye/analysis/rendering/core.py +47 -0
  48. microEye/analysis/rendering/volumetric.py +198 -0
  49. microEye/analysis/tools/__init__.py +0 -0
  50. microEye/analysis/tools/kymograms.py +1079 -0
  51. microEye/analysis/tools/roi_selectors.py +556 -0
  52. microEye/analysis/utils/__init__.py +3 -0
  53. microEye/analysis/utils/coordinates.py +18 -0
  54. microEye/analysis/utils/images.py +68 -0
  55. microEye/analysis/utils/windows.py +19 -0
  56. microEye/analysis/viewer/__init__.py +3 -0
  57. microEye/analysis/viewer/image_options_widget.py +615 -0
  58. microEye/analysis/viewer/images.py +1225 -0
  59. microEye/analysis/viewer/layers_widget.py +415 -0
  60. microEye/analysis/viewer/localizations.py +1304 -0
  61. microEye/analysis/viewer/psf.py +1003 -0
  62. microEye/analysis/viewer/volume.py +456 -0
  63. microEye/hardware/__init__.py +3 -0
  64. microEye/hardware/cams/__init__.py +29 -0
  65. microEye/hardware/cams/camera_calibration.py +99 -0
  66. microEye/hardware/cams/camera_list.py +528 -0
  67. microEye/hardware/cams/camera_options.py +694 -0
  68. microEye/hardware/cams/camera_panel.py +941 -0
  69. microEye/hardware/cams/dummy/__init__.py +1 -0
  70. microEye/hardware/cams/dummy/dummy_panel.py +759 -0
  71. microEye/hardware/cams/jobs.py +497 -0
  72. microEye/hardware/cams/line_profiler.py +99 -0
  73. microEye/hardware/cams/linescan/IR_Cam.py +474 -0
  74. microEye/hardware/cams/linescan/__init__.py +1 -0
  75. microEye/hardware/cams/micam.py +524 -0
  76. microEye/hardware/cams/pco/__init__.py +58 -0
  77. microEye/hardware/cams/pco/enums.py +382 -0
  78. microEye/hardware/cams/pco/pco_cam.py +761 -0
  79. microEye/hardware/cams/pco/pco_panel.py +477 -0
  80. microEye/hardware/cams/shortcuts.py +312 -0
  81. microEye/hardware/cams/thorlabs/__init__.py +1 -0
  82. microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
  83. microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
  84. microEye/hardware/cams/ueye/__init__.py +1 -0
  85. microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
  86. microEye/hardware/cams/ueye/ueye_panel.py +861 -0
  87. microEye/hardware/cams/vimba/__init__.py +1 -0
  88. microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
  89. microEye/hardware/cams/vimba/vimba_panel.py +813 -0
  90. microEye/hardware/device.py +60 -0
  91. microEye/hardware/lasers/__init__.py +13 -0
  92. microEye/hardware/lasers/io_matchbox.py +791 -0
  93. microEye/hardware/lasers/io_params.py +85 -0
  94. microEye/hardware/lasers/io_single_laser.py +742 -0
  95. microEye/hardware/lasers/laser_relay.py +594 -0
  96. microEye/hardware/mieye/__init__.py +1 -0
  97. microEye/hardware/mieye/acquisition_manager.py +467 -0
  98. microEye/hardware/mieye/devices_manager.py +533 -0
  99. microEye/hardware/mieye/miEye.py +659 -0
  100. microEye/hardware/misc/__init__.py +0 -0
  101. microEye/hardware/misc/acquisition_view.py +71 -0
  102. microEye/hardware/misc/reglo.py +761 -0
  103. microEye/hardware/misc/temp.py +188 -0
  104. microEye/hardware/port_config.py +59 -0
  105. microEye/hardware/protocols/__init__.py +2 -0
  106. microEye/hardware/protocols/actions.py +402 -0
  107. microEye/hardware/protocols/actions_items.py +703 -0
  108. microEye/hardware/protocols/designer.py +244 -0
  109. microEye/hardware/protocols/scene_manager.py +191 -0
  110. microEye/hardware/protocols/serialization.py +97 -0
  111. microEye/hardware/pycromanager/__init__.py +16 -0
  112. microEye/hardware/pycromanager/core.py +1433 -0
  113. microEye/hardware/pycromanager/devices.py +461 -0
  114. microEye/hardware/pycromanager/enums.py +107 -0
  115. microEye/hardware/pycromanager/headless.py +153 -0
  116. microEye/hardware/pycromanager/utils.py +34 -0
  117. microEye/hardware/pycromanager/widgets/__init__.py +5 -0
  118. microEye/hardware/pycromanager/widgets/bridges.py +407 -0
  119. microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
  120. microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
  121. microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
  122. microEye/hardware/stages/__init__.py +18 -0
  123. microEye/hardware/stages/elliptec/__init__.py +5 -0
  124. microEye/hardware/stages/elliptec/baseDevice.py +314 -0
  125. microEye/hardware/stages/elliptec/device.py +384 -0
  126. microEye/hardware/stages/elliptec/deviceID.py +212 -0
  127. microEye/hardware/stages/elliptec/devicePort.py +379 -0
  128. microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
  129. microEye/hardware/stages/elliptec/devicesView.py +706 -0
  130. microEye/hardware/stages/elliptec/ellDevices.py +134 -0
  131. microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
  132. microEye/hardware/stages/elliptec/motorInfo.py +153 -0
  133. microEye/hardware/stages/elliptec/stage.py +62 -0
  134. microEye/hardware/stages/elliptec/test.py +139 -0
  135. microEye/hardware/stages/kinesis/__init__.py +1 -0
  136. microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
  137. microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
  138. microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
  139. microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
  140. microEye/hardware/stages/kinesis/kinesis.py +776 -0
  141. microEye/hardware/stages/piezo_concept.py +607 -0
  142. microEye/hardware/stages/stabilizer.py +785 -0
  143. microEye/hardware/stages/stage.py +89 -0
  144. microEye/hardware/widgets/__init__.py +10 -0
  145. microEye/hardware/widgets/controller.py +246 -0
  146. microEye/hardware/widgets/devices.py +133 -0
  147. microEye/hardware/widgets/focusWidget.py +264 -0
  148. microEye/hardware/widgets/qlist_slider.py +113 -0
  149. microEye/hardware/widgets/scan_acquisition.py +424 -0
  150. microEye/icons/1024.png +0 -0
  151. microEye/icons/128.png +0 -0
  152. microEye/icons/16.png +0 -0
  153. microEye/icons/24.png +0 -0
  154. microEye/icons/256.png +0 -0
  155. microEye/icons/32.png +0 -0
  156. microEye/icons/48.png +0 -0
  157. microEye/icons/512.png +0 -0
  158. microEye/icons/64.png +0 -0
  159. microEye/icons/__init__.py +0 -0
  160. microEye/icons/close.svg +88 -0
  161. microEye/icons/mieye.png +0 -0
  162. microEye/icons/min.svg +83 -0
  163. microEye/icons/viewer.png +0 -0
  164. microEye/launcher.py +42 -0
  165. microEye/qt.py +181 -0
  166. microEye/utils/__init__.py +2 -0
  167. microEye/utils/enum_encoder.py +10 -0
  168. microEye/utils/expandable_groupbox.py +93 -0
  169. microEye/utils/gui_helper.py +457 -0
  170. microEye/utils/hid/__init__.py +8 -0
  171. microEye/utils/hid/controller.py +153 -0
  172. microEye/utils/hid/device.py +63 -0
  173. microEye/utils/hid/enums.py +140 -0
  174. microEye/utils/hid/utils.py +58 -0
  175. microEye/utils/labelled_slider.py +134 -0
  176. microEye/utils/metadata.py +599 -0
  177. microEye/utils/metadata_tree.py +718 -0
  178. microEye/utils/micro_launcher.py +245 -0
  179. microEye/utils/parameter_tree.py +325 -0
  180. microEye/utils/pyscripting.py +444 -0
  181. microEye/utils/retry_exec.py +35 -0
  182. microEye/utils/start_gui.py +112 -0
  183. microEye/utils/thread_worker.py +115 -0
  184. microEye/utils/uImage.py +1512 -0
  185. microeye-2.3.2.dist-info/METADATA +401 -0
  186. microeye-2.3.2.dist-info/RECORD +190 -0
  187. microeye-2.3.2.dist-info/WHEEL +5 -0
  188. microeye-2.3.2.dist-info/entry_points.txt +2 -0
  189. microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
  190. microeye-2.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,533 @@
1
+ import traceback
2
+ import weakref
3
+ from enum import Enum, auto
4
+ from typing import Any, Union
5
+
6
+ from microEye.hardware.cams import *
7
+ from microEye.hardware.lasers import *
8
+ from microEye.hardware.protocols import WeakObjects
9
+ from microEye.hardware.pycromanager.devices import PycroCore
10
+ from microEye.hardware.pycromanager.headless import HeadlessInstance, HeadlessManager
11
+ from microEye.hardware.stages import *
12
+ from microEye.hardware.widgets import focusWidget
13
+ from microEye.qt import QtCore, QtWidgets, Signal
14
+ from microEye.utils.hid import Buttons, dz_hybrid, hidController
15
+ from microEye.utils.retry_exec import retry_exec
16
+
17
+
18
+ class DEVICES(Enum):
19
+ CAM_LIST = auto()
20
+ CAMERA = auto()
21
+ ELLIPTEC = auto()
22
+ FocusStabilizer = auto()
23
+ HID_CONTROLLER = auto()
24
+ IR_CAM = auto()
25
+ LASER = auto()
26
+ LASER_RELAY = auto()
27
+ XY_STAGE = auto()
28
+ Z_STAGE = auto()
29
+
30
+
31
+ class DeviceManager(QtCore.QObject):
32
+ widgetAdded = Signal(DEVICES, QtWidgets.QWidget)
33
+ widgetRemoved = Signal(DEVICES, QtWidgets.QWidget)
34
+
35
+ WIDGETS = weakref.WeakValueDictionary()
36
+ CONTROLLERS = weakref.WeakValueDictionary()
37
+
38
+ def __new__(cls, *args, **kwargs):
39
+ # If the single instance doesn't exist, create a new one
40
+ if not hasattr(cls, '_singleton') or cls._singleton is None:
41
+ cls._singleton = super().__new__(cls, *args)
42
+ # Return the single instance
43
+ return cls._singleton
44
+
45
+ @classmethod
46
+ def instance(cls):
47
+ if not hasattr(cls, '_singleton') or cls._singleton is None:
48
+ return DeviceManager()
49
+
50
+ return cls._singleton
51
+
52
+ def __init__(self):
53
+ if not hasattr(self, '_initialized'):
54
+ super().__init__()
55
+ self.cameras = []
56
+ self.stages = []
57
+ self.lasers: list[Union[SingleMatchBox, CombinerLaserWidget]] = []
58
+
59
+ self._initialized = True
60
+
61
+ def init_devices(self):
62
+ self._init_cameras_list()
63
+ self._init_ir_cam()
64
+ self._init_laser_relay()
65
+ self._init_z_stage()
66
+ self._init_xy_stage()
67
+ self._init_elliptec_devices()
68
+ self._init_hid_controller()
69
+ self._init_focus_stabilizer()
70
+
71
+ def _init_cameras_list(self):
72
+ self.camList = CameraList()
73
+ self.camList.cameraAdded.connect(self._add_camera)
74
+ self.camList.cameraRemoved.connect(self._remove_camera)
75
+
76
+ self.widgetAdded.emit(DEVICES.CAM_LIST, self.camList)
77
+
78
+ def _init_ir_cam(self):
79
+ self.ir_array_detector = None
80
+
81
+ def _init_laser_relay(self):
82
+ self.laser_relay = LaserRelayController()
83
+ self.laser_relay.sendCommandActivated.connect(
84
+ lambda: self.laser_relay.sendCommand(self.laser_relay_settings())
85
+ )
86
+ DeviceManager.WIDGETS[DEVICES.LASER_RELAY] = self.laser_relay.view
87
+ WeakObjects.addObject(self.laser_relay)
88
+ self.widgetAdded.emit(DEVICES.LASER_RELAY, self.laser_relay.view)
89
+
90
+ def _init_elliptec_devices(self):
91
+ self._add_elliptec_devices()
92
+
93
+ def _add_elliptec_devices(self):
94
+ self.elliptec = ELLDevices()
95
+ self.elliptecView = ElliptecView(elleptic=self.elliptec)
96
+ DeviceManager.WIDGETS[DEVICES.ELLIPTEC] = self.elliptecView
97
+ WeakObjects.addObject(self.elliptecView)
98
+ self.widgetAdded.emit(DEVICES.ELLIPTEC, self.elliptecView)
99
+
100
+ def _init_z_stage(self):
101
+ self.stage: PzFocController = None
102
+
103
+ self._set_z_stage('FOC100')
104
+
105
+ def _init_xy_stage(self):
106
+ self.stage_xy = KinesisXY()
107
+ view = self.stage_xy.getViewWidget()
108
+ DeviceManager.WIDGETS[DEVICES.XY_STAGE] = view
109
+ WeakObjects.addObject(view)
110
+ self.widgetAdded.emit(DEVICES.XY_STAGE, view)
111
+
112
+ def _init_hid_controller(self):
113
+ self.hid_controller = hidController()
114
+ self.hid_controller.reportEvent.connect(self.hid_report)
115
+ # self.hid_controller.reportRStickPosition.connect(self.hid_RStick_report)
116
+ # self.hid_controller.reportLStickPosition.connect(self.hid_LStick_report)
117
+ self.hid_controller_toggle = False
118
+
119
+ self.widgetAdded.emit(DEVICES.HID_CONTROLLER, self.hid_controller)
120
+
121
+ def _init_focus_stabilizer(self):
122
+ FocusStabilizer.instance().moveStage.connect(self.moveStage)
123
+ FocusStabilizer.instance().startWorker()
124
+
125
+ self.focus = focusWidget()
126
+ DeviceManager.WIDGETS[DEVICES.FocusStabilizer] = self.focus
127
+ WeakObjects.addObject(self.focus)
128
+
129
+ self.widgetAdded.emit(DEVICES.FocusStabilizer, self.focus)
130
+
131
+ def moveStage(self, dir: bool, steps: int):
132
+ if isinstance(self.stage, PzFocController):
133
+ self.stage.moveStage(dir, steps)
134
+
135
+ def moveStageXY(self, x: float, y: float):
136
+ if isinstance(self.stage_xy, KinesisXY):
137
+ self.stage_xy.move_relative(x, y)
138
+
139
+ def stopRequest(self, axis):
140
+ if axis in ['x', 'y', 'xy']:
141
+ self.stage_xy.stop()
142
+
143
+ def homeRequest(self, axis):
144
+ if axis == 'z':
145
+ self.stage.stage.HOME()
146
+
147
+ def toggleLock(self, axis):
148
+ if axis == 'z':
149
+ widget: focusWidget = DeviceManager.WIDGETS.get(DEVICES.FocusStabilizer)
150
+ if widget:
151
+ widget.focusStabilizerView.toggleFocusStabilization()
152
+
153
+ def moveRequest(self, axis, direction, step, relative=True):
154
+ """
155
+ Move the stage in the specified direction and step size.
156
+
157
+ Parameters
158
+ ----------
159
+ axis : str
160
+ The axis to move ('x', 'y', or 'z').
161
+ direction : bool
162
+ The direction to move (True for positive, False for negative).
163
+ step : int
164
+ The step size for the movement.
165
+ relative : bool, optional
166
+ Whether to move relative to the current position (default is True).
167
+ """
168
+ if axis in ['x', 'y']:
169
+ dist = direction * step
170
+ args = [dist, 0] if axis == 'x' else [0, dist]
171
+ if relative:
172
+ self.stage_xy.move_relative(*args)
173
+ else:
174
+ self.stage_xy.move_absolute(*args)
175
+ else:
176
+ if relative:
177
+ self.stage.moveStage(
178
+ direction > 0, step, interface=True
179
+ ) if self.stage else None
180
+ else:
181
+ self.stage.moveAbsolute(step)
182
+
183
+ def hid_report(self, reportedEvent: Buttons):
184
+ self._handle_z_stage_events(reportedEvent)
185
+ self._handle_xy_stage_events(reportedEvent)
186
+
187
+ def _handle_z_stage_events(self, reportedEvent: Buttons):
188
+ if isinstance(self.stage, PzFocController):
189
+ if reportedEvent == Buttons.X:
190
+ self.stage.moveStage(True, True, True)
191
+ elif reportedEvent == Buttons.B:
192
+ self.stage.moveStage(False, True, True)
193
+ elif reportedEvent == Buttons.Y:
194
+ self.stage.moveStage(True, False, True)
195
+ elif reportedEvent == Buttons.A:
196
+ self.stage.moveStage(False, False, True)
197
+ elif reportedEvent == Buttons.Options:
198
+ self.stage.stage.HOME()
199
+ elif reportedEvent == Buttons.R3:
200
+ self.hid_controller_toggle = not self.hid_controller_toggle
201
+
202
+ def _handle_xy_stage_events(self, reportedEvent: Buttons):
203
+ kinesisView: KinesisView = DeviceManager.WIDGETS[DEVICES.XY_STAGE]
204
+
205
+ if reportedEvent == Buttons.LEFT:
206
+ kinesisView.move(True, self.hid_controller_toggle, False)
207
+ elif reportedEvent == Buttons.RIGHT:
208
+ kinesisView.move(True, self.hid_controller_toggle, True)
209
+ elif reportedEvent == Buttons.UP:
210
+ kinesisView.move(False, self.hid_controller_toggle, True)
211
+ elif reportedEvent == Buttons.DOWN:
212
+ kinesisView.move(False, self.hid_controller_toggle, False)
213
+ elif reportedEvent == Buttons.R1:
214
+ pass
215
+ # kinesisView.center()
216
+ elif reportedEvent == Buttons.L1:
217
+ kinesisView.stop()
218
+ elif reportedEvent == Buttons.L3:
219
+ self._toggle_xy_step_or_jump(kinesisView)
220
+
221
+ def _toggle_xy_step_or_jump(self, kinesisView: KinesisView):
222
+ self.hid_controller_toggle = not self.hid_controller_toggle
223
+ kinesisView.updateControls(self.hid_controller_toggle)
224
+
225
+ def hid_LStick_report(self, x, y):
226
+ diff_x = x - 128
227
+ diff_y = y - 127
228
+ deadzone = 16
229
+ res = dz_hybrid([diff_x, diff_y], deadzone)
230
+ diff = res[1]
231
+
232
+ kinesisView: KinesisView = DeviceManager.WIDGETS[DEVICES.XY_STAGE]
233
+
234
+ self._update_step_or_jump_spin(kinesisView, diff)
235
+
236
+ if abs(diff) > 0:
237
+ if self.hid_controller_toggle:
238
+ val = 0.0001 * diff
239
+ val += kinesisView.getJump()
240
+ kinesisView.setJump(val)
241
+ else:
242
+ val = 0.0001 * diff
243
+ val += kinesisView.getStep()
244
+ kinesisView.setStep(val)
245
+ else:
246
+ if self.hid_controller_toggle:
247
+ val = kinesisView.getJump()
248
+ val -= val % 0.0005
249
+ kinesisView.setJump(val)
250
+ else:
251
+ val = kinesisView.getJump()
252
+ val -= val % 0.0005
253
+ kinesisView.setJump(val)
254
+
255
+ def _update_step_or_jump_spin(self, kinesisView: KinesisView, diff):
256
+ current_value = (
257
+ kinesisView.getJump()
258
+ if self.hid_controller_toggle
259
+ else kinesisView.getStep()
260
+ )
261
+ if abs(diff) > 0:
262
+ new_value = current_value + 0.0001 * diff
263
+ else:
264
+ new_value = current_value - (current_value % 0.0005)
265
+ if self.hid_controller_toggle:
266
+ kinesisView.setJump(new_value)
267
+ else:
268
+ kinesisView.setStep(new_value)
269
+
270
+ def hid_RStick_report(self, x, y):
271
+ diff_x = x - 128
272
+ diff_y = y - 127
273
+ deadzone = 16
274
+ res = dz_hybrid([diff_x, diff_y], deadzone)
275
+ diff = res[1]
276
+
277
+ if self.hid_controller_toggle:
278
+ self._handle_focus_stabilization(diff)
279
+ else:
280
+ self._handle_z_stage_step(diff)
281
+
282
+ def _handle_focus_stabilization(self, diff):
283
+ if FocusStabilizer.instance().isFocusStabilized():
284
+ value = 1e-3 * diff
285
+ FocusStabilizer.instance().setPeakPosition(value, True)
286
+
287
+ def _handle_z_stage_step(self, diff):
288
+ if abs(diff) > 0 and abs(diff) < 100:
289
+ value = 0.25 * diff
290
+ self.stage.setStep(value, True)
291
+ else:
292
+ value = self.stage.getStep()
293
+ value -= value % 5
294
+ self.stage.setStep(value)
295
+
296
+ # gui + hardware
297
+
298
+ def _add_laser(self, laser: str):
299
+ if (
300
+ 'IO MatchBox' in laser and 'Combiner' in laser
301
+ ) or CombinerLaserWidget.__name__ in laser:
302
+ widget = CombinerLaserWidget()
303
+ elif (
304
+ 'IO MatchBox' in laser and 'Single' in laser
305
+ ) or SingleMatchBox.__name__ in laser:
306
+ widget = SingleMatchBox()
307
+ else:
308
+ raise ValueError(f'Unknown laser type: {laser}')
309
+
310
+ self.lasers.append(widget)
311
+ WeakObjects.addObject(widget)
312
+
313
+ widget.removed.connect(lambda: self._laser_removed(widget))
314
+
315
+ self.widgetAdded.emit(DEVICES.LASER, widget)
316
+
317
+ return widget
318
+
319
+ def _laser_removed(self, widget: QtWidgets.QWidget):
320
+ WeakObjects.removeObject(widget)
321
+ if widget in self.lasers:
322
+ self.lasers.remove(widget)
323
+ self.widgetRemoved.emit(DEVICES.LASER, widget)
324
+
325
+ def _add_camera(self, widget: Camera_Panel, ir: bool):
326
+ if ir:
327
+ widget._frames = FocusStabilizer.instance().buffer
328
+
329
+ self.widgetAdded.emit(DEVICES.IR_CAM, widget)
330
+
331
+ else:
332
+ widget.setWindowTitle(widget.title())
333
+ widget.show()
334
+
335
+ WeakObjects.addObject(widget)
336
+
337
+ def _remove_camera(self, widget: QtWidgets.QWidget, ir: bool):
338
+ WeakObjects.removeObject(widget)
339
+ if ir:
340
+ self.widgetRemoved.emit(DEVICES.IR_CAM, widget)
341
+
342
+ def _set_ir_array_detector(self, value: str):
343
+ if self.camList.autofocusCam:
344
+ title = self.camList.autofocusCam.title()
345
+ QtWidgets.QMessageBox.warning(
346
+ self,
347
+ 'Warning',
348
+ f'Please remove {title}.',
349
+ QtWidgets.QMessageBox.StandardButton.Ok,
350
+ )
351
+ return
352
+
353
+ if self.ir_array_detector and self.ir_array_detector.isOpen:
354
+ QtWidgets.QMessageBox.warning(
355
+ self,
356
+ 'Warning',
357
+ f'Please disconnect {self.ir_array_detector.name}.',
358
+ QtWidgets.QMessageBox.StandardButton.Ok,
359
+ )
360
+ return
361
+
362
+ if 'TSL1401' in value:
363
+ self.ir_array_detector = ParallaxLineScanner(
364
+ FocusStabilizer.instance().buffer
365
+ )
366
+ widget = self.ir_array_detector.getQWidget()
367
+ self.widgetAdded.emit(DEVICES.IR_CAM, widget)
368
+ DeviceManager.WIDGETS[DEVICES.IR_CAM] = widget
369
+ elif 'Demo Line Scanner' in value:
370
+ self.ir_array_detector = DemoLineScanner(FocusStabilizer.instance().buffer)
371
+ widget = self.ir_array_detector.getQWidget()
372
+ self.widgetAdded.emit(DEVICES.IR_CAM, widget)
373
+ DeviceManager.WIDGETS[DEVICES.IR_CAM] = widget
374
+
375
+ def _remove_ir_array_detector(self):
376
+ if self.ir_array_detector.isOpen:
377
+ QtWidgets.QMessageBox.warning(
378
+ self,
379
+ 'Warning',
380
+ f'Please disconnect {self.ir_array_detector.name}.',
381
+ QtWidgets.QMessageBox.StandardButton.Ok,
382
+ )
383
+ return
384
+
385
+ self.widgetRemoved.emit(DEVICES.IR_CAM, DeviceManager.WIDGETS[DEVICES.IR_CAM])
386
+ self.ir_array_detector = None
387
+
388
+ def _set_z_stage(self, value: str):
389
+ if self.stage and self.stage.isOpen():
390
+ QtWidgets.QMessageBox.warning(
391
+ None,
392
+ 'Warning',
393
+ f'Please disconnect {self.stage}.',
394
+ QtWidgets.QMessageBox.StandardButton.Ok,
395
+ )
396
+ return
397
+
398
+ if self.stage:
399
+ self.stage.view.remove_widget()
400
+
401
+ if 'FOC100' in value:
402
+ self.stage = PzFocController()
403
+ self.stage.view.removed.connect(self._remove_z_stage)
404
+ DeviceManager.WIDGETS[DEVICES.Z_STAGE] = self.stage.view
405
+ WeakObjects.addObject(self.stage.view)
406
+ self.widgetAdded.emit(DEVICES.Z_STAGE, self.stage.view)
407
+
408
+ def _remove_z_stage(self, panel: QtWidgets.QWidget):
409
+ DeviceManager.WIDGETS[DEVICES.Z_STAGE] = None
410
+ WeakObjects.removeObject(panel)
411
+ self.widgetRemoved.emit(DEVICES.Z_STAGE, panel)
412
+
413
+ def laser_relay_settings(self):
414
+ '''Returns the RelayBox setting command.
415
+
416
+ Returns
417
+ -------
418
+ str
419
+ the RelayBox setting command.
420
+ '''
421
+ config = ''
422
+ for panel in self.lasers:
423
+ config += panel.GetRelayState()
424
+ return self.laser_relay.getCommand(config)
425
+
426
+ def _get_laser_configs(self) -> dict:
427
+ '''
428
+ Returns the laser configurations.
429
+
430
+ Returns
431
+ -------
432
+ list
433
+ list of laser configurations.
434
+ '''
435
+ configs = []
436
+ for panel in self.lasers:
437
+ configs.append(panel.get_config())
438
+
439
+ return configs
440
+
441
+ def get_config(self) -> dict:
442
+ config = {
443
+ 'LaserRelay': self.laser_relay.get_config(),
444
+ 'Elliptec': self.elliptec.get_config(),
445
+ 'Stage Z': self.stage.get_config(),
446
+ 'Stage XY': self.stage_xy.get_config(),
447
+ 'FocusStabilizer': self.focus.get_config(),
448
+ 'Pycromanager': {
449
+ 'core_instances': list(PycroCore._instances.keys()),
450
+ 'headless_instances': HeadlessManager()._instances,
451
+ },
452
+ }
453
+
454
+ config['Lasers'] = self._get_laser_configs()
455
+
456
+ return config
457
+
458
+ def load_config(self, config: dict):
459
+ self.laser_relay.load_config(config.get('LaserRelay', {}))
460
+ self.elliptec.load_config(config.get('Elliptec', {}))
461
+ self.stage.load_config(
462
+ config.get('PiezoStage', {})
463
+ if 'PiezoStage' in config
464
+ else config.get('Stage Z', {})
465
+ )
466
+ self.stage_xy.load_config(config.get('Stage XY', {}))
467
+ self.focus.load_config(config.get('FocusStabilizer', {}))
468
+
469
+ while len(self.lasers) > 0:
470
+ panel = self.lasers.pop()
471
+ panel.Laser.CloseCOM()
472
+ panel.remove_widget()
473
+
474
+ for laser in config.get('Lasers', []):
475
+ laser_widget = self._add_laser(laser.get('class'))
476
+ laser_widget.load_config(laser)
477
+
478
+ try:
479
+ pycro_config: dict = config.get('Pycromanager', {})
480
+
481
+ core_instances: list[int] = pycro_config.get('core_instances', [])
482
+ for port in core_instances:
483
+ PycroCore.instance(int(port))
484
+
485
+ headless_instances: dict[int, dict] = pycro_config.get(
486
+ 'headless_instances', {}
487
+ )
488
+ for _, instance in headless_instances.items():
489
+ HeadlessManager().start_instance(HeadlessInstance(**instance))
490
+ except Exception:
491
+ traceback.print_exc()
492
+
493
+ def auto_connect(self):
494
+ funcs = [
495
+ self.laser_relay.connect,
496
+ self.elliptecView.open,
497
+ self.stage.connect,
498
+ self.stage_xy.open,
499
+ ]
500
+
501
+ for panel in self.lasers:
502
+ funcs.append(panel.laser_connect)
503
+
504
+ for func in funcs:
505
+ retry_exec(func)
506
+
507
+ def shutdown(self, exit=True):
508
+ '''Disconnects all devices and exits the application.'''
509
+
510
+ if self.camList.autofocusCam:
511
+ self.camList.autofocusCam.stop()
512
+
513
+ funcs = [
514
+ self.camList.removeAllCameras,
515
+ self.laser_relay.disconnect,
516
+ self.elliptecView.close,
517
+ self.stage.disconnect,
518
+ self.stage_xy.close,
519
+ ]
520
+
521
+ for panel in self.lasers:
522
+ funcs.append(panel.Laser.CloseCOM)
523
+
524
+ for func in funcs:
525
+ retry_exec(func)
526
+
527
+ print('All devices disconnected!')
528
+ if not exit:
529
+ return
530
+
531
+ import sys
532
+
533
+ sys.exit(0)