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,706 @@
1
+ from enum import Enum
2
+ from typing import Any, Callable, Optional
3
+
4
+ from pyqtgraph.parametertree import Parameter
5
+
6
+ from microEye import __version__
7
+ from microEye.hardware.stages.elliptec.baseDevice import DeviceDirection
8
+ from microEye.hardware.stages.elliptec.deviceStatus import DeviceStatusValues
9
+ from microEye.hardware.stages.elliptec.ellDevices import *
10
+ from microEye.hardware.stages.elliptec.motorInfo import MotorInfo
11
+ from microEye.qt import QtCore, QtSerialPort, QtWidgets, Signal
12
+ from microEye.utils import Tree
13
+
14
+
15
+ class ElliptecStageParams(Enum):
16
+ MODEL = 'Controller Settings.Model'
17
+
18
+ # Serial Port Settings
19
+ SERIAL_PORT = 'Controller Settings.Serial Port'
20
+ PORT = 'Controller Settings.Serial Port.Port'
21
+ REFRESH = 'Controller Settings.Serial Port.Refresh Ports'
22
+ BAUDRATE = 'Controller Settings.Serial Port.Baudrate'
23
+ SET_PORT = 'Controller Settings.Serial Port.Set Config'
24
+ OPEN = 'Controller Settings.Serial Port.Connect'
25
+ CLOSE = 'Controller Settings.Serial Port.Disconnect'
26
+ PORT_STATE = 'Controller Settings.Serial Port.State'
27
+
28
+ # Stage Management
29
+ MIN_ADDRESS = 'Stage Management.Range (min)'
30
+ MAX_ADDRESS = 'Stage Management.Range (max)'
31
+ SCAN = 'Stage Management.Scan Devices'
32
+
33
+ # Devices
34
+ DEVICES = 'Connected Devices'
35
+
36
+ # Device Specific Params
37
+ HOME = 'Home'
38
+ HOME_DIRECTION = 'Homing Direction'
39
+ FORWARD = 'Forward'
40
+ BACKWARD = 'Backward'
41
+ STOP_JOG = 'Stop Jog'
42
+ SLOT = 'Slot'
43
+ SET_SLOT = 'Set Slot'
44
+ POSITION = 'Position'
45
+ SET_POSITION = 'Set Position'
46
+ STATUS = 'Status'
47
+ JOG_STEP_SIZE = 'Jog Step Size'
48
+ SET_JOG = 'Set Jog Step Size'
49
+ GET_JOG = 'Get Jog Step Size'
50
+ VELOCITY = 'Velocity'
51
+ REMOVE = 'Remove Device'
52
+
53
+ def __str__(self):
54
+ return self.value.split('.')[-1]
55
+
56
+ def get_path(self):
57
+ return self.value.split('.')
58
+
59
+
60
+ class ElliptecStageView(Tree):
61
+ PARAMS = ElliptecStageParams
62
+ removed = Signal(object)
63
+
64
+ # STAGE_TYPES = ['ELL6', 'ELL9', 'ELL14']
65
+
66
+ def __init__(
67
+ self, controller: ELLDevices, parent: Optional['QtWidgets.QWidget'] = None
68
+ ):
69
+ super().__init__(parent=parent)
70
+
71
+ if not isinstance(controller, ELLDevices):
72
+ raise ValueError('Invalid controller type!')
73
+
74
+ self.controller = controller
75
+
76
+ def create_parameters(self):
77
+ params = [
78
+ {
79
+ 'name': 'Controller Settings',
80
+ 'type': 'group',
81
+ 'children': [
82
+ {
83
+ 'name': str(ElliptecStageParams.MODEL),
84
+ 'type': 'str',
85
+ 'value': 'Elliptec Devices Controller',
86
+ 'readonly': True,
87
+ },
88
+ {
89
+ 'name': str(ElliptecStageParams.SERIAL_PORT),
90
+ 'type': 'group',
91
+ 'expanded': False,
92
+ 'children': [
93
+ {
94
+ 'name': str(ElliptecStageParams.PORT),
95
+ 'type': 'list',
96
+ 'limits': self._get_available_ports(),
97
+ },
98
+ {
99
+ 'name': str(ElliptecStageParams.BAUDRATE),
100
+ 'type': 'list',
101
+ 'default': 9600,
102
+ 'limits': [9600, 19200, 38400, 57600, 115200],
103
+ },
104
+ {
105
+ 'name': str(ElliptecStageParams.SET_PORT),
106
+ 'type': 'action',
107
+ },
108
+ {'name': str(ElliptecStageParams.OPEN), 'type': 'action'},
109
+ {'name': str(ElliptecStageParams.CLOSE), 'type': 'action'},
110
+ {
111
+ 'name': str(ElliptecStageParams.REFRESH),
112
+ 'type': 'action',
113
+ },
114
+ {
115
+ 'name': str(ElliptecStageParams.PORT_STATE),
116
+ 'type': 'str',
117
+ 'value': 'closed',
118
+ 'readonly': True,
119
+ },
120
+ ],
121
+ },
122
+ ],
123
+ },
124
+ {
125
+ 'name': 'Stage Management',
126
+ 'type': 'group',
127
+ 'children': [
128
+ {
129
+ 'name': str(ElliptecStageParams.MIN_ADDRESS),
130
+ 'type': 'list',
131
+ 'default': '0',
132
+ 'limits': EllDevice.VALID_ADDRESSES,
133
+ },
134
+ {
135
+ 'name': str(ElliptecStageParams.MAX_ADDRESS),
136
+ 'type': 'list',
137
+ 'default': 'F',
138
+ 'limits': EllDevice.VALID_ADDRESSES,
139
+ },
140
+ {'name': str(ElliptecStageParams.SCAN), 'type': 'action'},
141
+ ],
142
+ },
143
+ {'name': str(ElliptecStageParams.DEVICES), 'type': 'group', 'children': []},
144
+ # {'name': str(ElliptecStageParams.REMOVE), 'type': 'action'},
145
+ ]
146
+
147
+ self.param_tree = Parameter.create(name='', type='group', children=params)
148
+ # self.param_tree.sigTreeStateChanged.connect(self.change)
149
+ self.header().setSectionResizeMode(
150
+ QtWidgets.QHeaderView.ResizeMode.ResizeToContents
151
+ )
152
+
153
+ self.initializeSignals()
154
+
155
+ def initializeSignals(self):
156
+ self.get_param(ElliptecStageParams.SET_PORT).sigActivated.connect(
157
+ lambda: self.setConfig()
158
+ )
159
+ self.get_param(ElliptecStageParams.OPEN).sigActivated.connect(
160
+ lambda: self.open()
161
+ )
162
+ self.get_param(ElliptecStageParams.CLOSE).sigActivated.connect(
163
+ lambda: DevicePort.close()
164
+ )
165
+ self.get_param(ElliptecStageParams.REFRESH).sigActivated.connect(
166
+ lambda: self._refresh_ports()
167
+ )
168
+ self.get_param(ElliptecStageParams.SCAN).sigActivated.connect(
169
+ lambda: self.findDevices()
170
+ )
171
+
172
+ def isOpen(self):
173
+ return DevicePort.isOpen()
174
+
175
+ def setConfig(self):
176
+ if not DevicePort.isOpen():
177
+ DevicePort.setPortName(self.get_param_value(ElliptecStageParams.PORT))
178
+ DevicePort.setBaudRate(self.get_param_value(ElliptecStageParams.BAUDRATE))
179
+
180
+ def setPortName(self, name: str):
181
+ DevicePort.setPortName(name)
182
+
183
+ def setBaudRate(self, baudRate: int):
184
+ DevicePort.setBaudRate(baudRate)
185
+
186
+ def open(self):
187
+ res = DevicePort.open()
188
+
189
+ if res:
190
+ self.set_expanded(ElliptecStageParams.SERIAL_PORT)
191
+
192
+ def findDevices(self):
193
+ if not DevicePort.isOpen():
194
+ print('Open the serial port first!')
195
+ return
196
+
197
+ self.get_param(ElliptecStageParams.DEVICES).clearChildren()
198
+
199
+ min_address = self._get_valid_address(ElliptecStageParams.MIN_ADDRESS, '0')
200
+ max_address = self._get_valid_address(ElliptecStageParams.MAX_ADDRESS, 'F')
201
+
202
+ devices = self.controller.scanAddresses(min_address, max_address)
203
+ for device in devices:
204
+ # Configure each device found
205
+ if self.controller.configure(device):
206
+ self.configureDeviceParams(self.controller.addressedDevice(device[0]))
207
+
208
+ def configureDeviceParams(self, device: EllDevice):
209
+ if device.deviceInfo.DeviceType in [
210
+ DeviceID.DeviceTypes.Shutter2,
211
+ DeviceID.DeviceTypes.Shutter4,
212
+ DeviceID.DeviceTypes.Shutter6,
213
+ ]:
214
+ self.buildShutterParams(device)
215
+ elif device.deviceInfo.DeviceType in [
216
+ DeviceID.DeviceTypes.OpticsRotator,
217
+ DeviceID.DeviceTypes.LinearStage25mm,
218
+ DeviceID.DeviceTypes.LinearStage28mm,
219
+ DeviceID.DeviceTypes.LinearStage60mm,
220
+ DeviceID.DeviceTypes.LinearStage60mm_10,
221
+ ]:
222
+ self.buildStageParams(device)
223
+
224
+ def buildShutterParams(self, device: EllDevice):
225
+ # Implement logic to add device params
226
+ params = {
227
+ 'name': f'ELL{device.deviceInfo.DeviceType.value} - {device.address}',
228
+ 'type': 'group',
229
+ 'children': [
230
+ {'name': str(ElliptecStageParams.HOME), 'type': 'action'},
231
+ {'name': str(ElliptecStageParams.FORWARD), 'type': 'action'},
232
+ {'name': str(ElliptecStageParams.BACKWARD), 'type': 'action'},
233
+ {
234
+ 'name': str(ElliptecStageParams.POSITION),
235
+ 'type': 'float',
236
+ 'value': 0.0,
237
+ 'readonly': True,
238
+ },
239
+ {
240
+ 'name': str(ElliptecStageParams.STATUS),
241
+ 'type': 'str',
242
+ 'value': device.deviceStatus.name,
243
+ 'readonly': True,
244
+ },
245
+ ],
246
+ }
247
+
248
+ if device.deviceInfo.DeviceType in [
249
+ DeviceID.DeviceTypes.Shutter4,
250
+ DeviceID.DeviceTypes.Shutter6,
251
+ ] and isinstance(params['children'], list):
252
+ params['children'].extend(
253
+ [
254
+ {
255
+ 'name': str(ElliptecStageParams.SLOT),
256
+ 'type': 'int',
257
+ 'default': 0,
258
+ 'limits': [
259
+ 0,
260
+ 3
261
+ if device.deviceInfo.DeviceType
262
+ == DeviceID.DeviceTypes.Shutter4
263
+ else 5,
264
+ ],
265
+ },
266
+ {'name': str(ElliptecStageParams.SET_SLOT), 'type': 'action'},
267
+ ]
268
+ )
269
+ self.add_param_child(ElliptecStageParams.DEVICES, params)
270
+
271
+ self.connectDeviceSignals(device)
272
+
273
+ def buildStageParams(self, device: EllDevice):
274
+ # Implement logic to add device params
275
+ params = {
276
+ 'name': f'ELL{device.deviceInfo.DeviceType.value} - {device.address}',
277
+ 'type': 'group',
278
+ 'children': [],
279
+ }
280
+ if device.deviceInfo.DeviceType not in [
281
+ DeviceID.DeviceTypes.OpticsRotator,
282
+ DeviceID.DeviceTypes.LinearStage25mm,
283
+ DeviceID.DeviceTypes.LinearStage28mm,
284
+ DeviceID.DeviceTypes.LinearStage60mm,
285
+ DeviceID.DeviceTypes.LinearStage60mm_10,
286
+ ]:
287
+ return
288
+
289
+ device.get_position()
290
+ device.get_jogstep_size()
291
+
292
+ params['children'] = [
293
+ {'name': str(ElliptecStageParams.HOME), 'type': 'action'},
294
+ {'name': str(ElliptecStageParams.FORWARD), 'type': 'action'},
295
+ {'name': str(ElliptecStageParams.BACKWARD), 'type': 'action'},
296
+ {
297
+ 'name': str(ElliptecStageParams.POSITION),
298
+ 'type': 'float',
299
+ 'value': device.position,
300
+ 'suffix': device.deviceInfo.Units,
301
+ },
302
+ {'name': str(ElliptecStageParams.SET_POSITION), 'type': 'action'},
303
+ {
304
+ 'name': str(ElliptecStageParams.STATUS),
305
+ 'type': 'str',
306
+ 'value': device.deviceStatus.name,
307
+ 'readonly': True,
308
+ },
309
+ {
310
+ 'name': str(ElliptecStageParams.JOG_STEP_SIZE),
311
+ 'type': 'float',
312
+ 'value': device.jogstep_size,
313
+ 'limits': [0.0, device.deviceInfo.Travel],
314
+ 'suffix': device.deviceInfo.Units,
315
+ },
316
+ {'name': str(ElliptecStageParams.GET_JOG), 'type': 'action'},
317
+ {'name': str(ElliptecStageParams.SET_JOG), 'type': 'action'},
318
+ {'name': str(ElliptecStageParams.STOP_JOG), 'type': 'action'},
319
+ {
320
+ 'name': str(ElliptecStageParams.HOME_DIRECTION),
321
+ 'type': 'list',
322
+ 'default': 'Clockwise',
323
+ 'limits': [
324
+ 'Clockwise',
325
+ 'AntiClockwise',
326
+ ],
327
+ 'visible': device.deviceInfo.DeviceType
328
+ in [
329
+ DeviceID.DeviceTypes.OpticsRotator,
330
+ DeviceID.DeviceTypes.RotaryStage8,
331
+ DeviceID.DeviceTypes.RotaryStage18,
332
+ ],
333
+ },
334
+ ]
335
+ self.add_param_child(ElliptecStageParams.DEVICES, params)
336
+
337
+ self.connectDeviceSignals(device)
338
+
339
+ def connectParamSignal(self, paramPath: str, callback: Callable):
340
+ param_obj = self.get_param(paramPath)
341
+ if param_obj is not None:
342
+ param_obj.sigActivated.connect(callback)
343
+ return True
344
+
345
+ return False
346
+
347
+ def connectDeviceSignals(self, device: EllDevice):
348
+ address = device.address
349
+ dev_type = device.deviceInfo.DeviceType
350
+ stage_path = (
351
+ f'{str(ElliptecStageParams.DEVICES)}.ELL{dev_type.value} - {address}'
352
+ )
353
+
354
+ self.connectParamSignal(
355
+ f'{stage_path}.{str(ElliptecStageParams.FORWARD)}',
356
+ lambda: device.jog_forward(),
357
+ )
358
+ self.connectParamSignal(
359
+ f'{stage_path}.{str(ElliptecStageParams.BACKWARD)}',
360
+ lambda: device.jog_backward(),
361
+ )
362
+
363
+ if dev_type in [
364
+ DeviceID.DeviceTypes.Shutter2,
365
+ DeviceID.DeviceTypes.Shutter4,
366
+ DeviceID.DeviceTypes.Shutter6,
367
+ ]:
368
+ self.connectParamSignal(
369
+ f'{stage_path}.{str(ElliptecStageParams.HOME)}',
370
+ lambda: device.home(),
371
+ )
372
+
373
+ self.connectParamSignal(
374
+ f'{stage_path}.{str(ElliptecStageParams.SET_SLOT)}',
375
+ lambda: device.move_absolute(
376
+ self.get_param_value(
377
+ f'{stage_path}.{str(ElliptecStageParams.SLOT)}'
378
+ )
379
+ * SHUTTER_POSITION_FACTOR[dev_type],
380
+ ),
381
+ )
382
+ elif dev_type in [
383
+ DeviceID.DeviceTypes.OpticsRotator,
384
+ DeviceID.DeviceTypes.LinearStage25mm,
385
+ DeviceID.DeviceTypes.LinearStage28mm,
386
+ DeviceID.DeviceTypes.LinearStage60mm,
387
+ DeviceID.DeviceTypes.LinearStage60mm_10,
388
+ ]:
389
+ self.connectParamSignal(
390
+ f'{stage_path}.{str(ElliptecStageParams.STOP_JOG)}',
391
+ lambda: device.jog_stop(),
392
+ )
393
+
394
+ self.connectParamSignal(
395
+ f'{stage_path}.{str(ElliptecStageParams.SET_POSITION)}',
396
+ lambda: device.move_absolute(
397
+ self.get_param_value(
398
+ f'{stage_path}.{str(ElliptecStageParams.POSITION)}'
399
+ )
400
+ ),
401
+ )
402
+ self.connectParamSignal(
403
+ f'{stage_path}.{str(ElliptecStageParams.HOME)}',
404
+ lambda: device.home(
405
+ DeviceDirection[
406
+ self.get_param_value(
407
+ f'{stage_path}.{str(ElliptecStageParams.HOME_DIRECTION)}'
408
+ )
409
+ ]
410
+ ),
411
+ )
412
+
413
+ self.connectParamSignal(
414
+ f'{stage_path}.{str(ElliptecStageParams.GET_JOG)}',
415
+ lambda: device.get_jogstep_size(),
416
+ )
417
+ self.connectParamSignal(
418
+ f'{stage_path}.{str(ElliptecStageParams.SET_JOG)}',
419
+ lambda: device.set_jogstep_size(
420
+ self.get_param_value(
421
+ f'{stage_path}.{str(ElliptecStageParams.JOG_STEP_SIZE)}'
422
+ )
423
+ ),
424
+ )
425
+
426
+ def remove_widget(self):
427
+ if self.parent() and not DevicePort.isOpen():
428
+ self.parent().layout().removeWidget(self)
429
+ self.removed.emit(self)
430
+ self.deleteLater()
431
+ else:
432
+ print(f'Disconnect Elliptec stage before removing!')
433
+
434
+ def __str__(self):
435
+ return f'Elliptec Devices ({DevicePort.instance().serial.portName()})'
436
+
437
+ @staticmethod
438
+ def _get_available_ports():
439
+ return [
440
+ info.portName() for info in QtSerialPort.QSerialPortInfo.availablePorts()
441
+ ]
442
+
443
+ def _refresh_ports(self):
444
+ self.get_param(ElliptecStageParams.PORT).setLimits(self._get_available_ports())
445
+
446
+ def _get_valid_address(self, param: ElliptecStageParams, default: Any):
447
+ address = self.get_param_value(param)
448
+ return address if EllDevice.is_valid_address(address) else default
449
+
450
+
451
+ class ElliptecView(QtWidgets.QTabWidget):
452
+ HEADER = '> <span style="color:#0f0;">Elliptec Driver ('
453
+ HEADER += f'<span style="color:#aaf;">microEye v{__version__}</span>)</span>'
454
+
455
+ def __init__(
456
+ self,
457
+ elleptic: Optional[ELLDevices] = None,
458
+ parent: Optional[QtWidgets.QWidget] = None,
459
+ ) -> None:
460
+ super().__init__(parent)
461
+
462
+ self.initUI(elleptic)
463
+
464
+ def initUI(self, elleptic: Optional[ELLDevices] = None):
465
+ '''
466
+ Initializes the ElliptecView UI.
467
+ '''
468
+
469
+ # Set window properties
470
+ self.setWindowTitle('Elliptec Driver')
471
+
472
+ # Create the output terminal
473
+ self._output = QtWidgets.QPlainTextEdit()
474
+ self._output.setReadOnly(True)
475
+ self._output.appendHtml(self.HEADER)
476
+ self._output.setStyleSheet('QPlainTextEdit { background-color: #111;}')
477
+
478
+ # Create the ELLDevices controller
479
+ if elleptic:
480
+ self._ellDevices = elleptic
481
+ else:
482
+ self._ellDevices = ELLDevices()
483
+ self._ellDevices.MessageUpdates.outputUpdated.connect(self.handleOutputUpdate)
484
+ self._ellDevices.MessageUpdates.parameterUpdated.connect(
485
+ self.handleParameterUpdate
486
+ )
487
+ DevicePort.instance().dataSent.connect(self.handleDataSent)
488
+ DevicePort.instance().dataReceived.connect(self.handleDataReceived)
489
+
490
+ # Create the devices tab
491
+ self._elliptecStageView = ElliptecStageView(controller=self._ellDevices)
492
+
493
+ # Set up the layout
494
+ self.addTab(self._elliptecStageView, 'Devices')
495
+ self.addTab(self._output, 'Output')
496
+
497
+
498
+ def isOpen(self):
499
+ return DevicePort.isOpen()
500
+
501
+ def portName(self):
502
+ return DevicePort.instance().serial.portName()
503
+
504
+ def setPortName(self, name: str):
505
+ DevicePort.setPortName(name)
506
+
507
+ def baudRate(self):
508
+ return DevicePort.instance().serial.baudRate()
509
+
510
+ def setBaudRate(self, baudRate: int):
511
+ DevicePort.setBaudRate(baudRate)
512
+
513
+ def open(self):
514
+ DevicePort.open()
515
+
516
+ def closePort(self):
517
+ DevicePort.close()
518
+
519
+ def updateHighlight(self):
520
+ '''
521
+ Updates the highlight style of the "Connect" action.
522
+ '''
523
+ style = ''
524
+ if self.isOpen():
525
+ style = 'background-color: #004CB6'
526
+ else:
527
+ style = 'background-color: black'
528
+
529
+ next(
530
+ self._elliptecStageView.get_param(ElliptecStageParams.OPEN).items.keys()
531
+ ).button.setStyleSheet(style)
532
+
533
+ def clearTerminal(self):
534
+ '''
535
+ Clears the terminal.
536
+ '''
537
+ self._output.clear()
538
+ self._output.appendHtml(self.HEADER)
539
+
540
+ def handleDataSent(self, message: str):
541
+ '''
542
+ Handles the data sent event.
543
+ '''
544
+ self._output.appendHtml(f'<span style="color: cyan;">Tx: {message}</span>')
545
+
546
+ def handleDataReceived(self, message: str):
547
+ '''
548
+ Handles the data received event.
549
+ '''
550
+ self._output.appendHtml(f'<span style="color: green;">Rx: {message}</span>')
551
+
552
+ def handleOutputUpdate(self, messages: list[str], error: bool):
553
+ '''
554
+ Handles the output update event.
555
+ '''
556
+ for message in messages:
557
+ if 'Scanning for devices' in message:
558
+ self.clearTerminal()
559
+
560
+ self._output.appendHtml(
561
+ f'<span style="color: yellow;">Output-> {message}</span>'
562
+ )
563
+
564
+ def handleParameterUpdate(
565
+ self, update_type: MessageUpdater.UpdateTypes, address: str, data: Any
566
+ ):
567
+ if update_type == MessageUpdater.UpdateTypes.DeviceInfo and isinstance(
568
+ data, DeviceID
569
+ ):
570
+ # if data and address in _device_view_models:
571
+ # _device_view_models[address].description = data.description()
572
+ pass
573
+ elif update_type == MessageUpdater.UpdateTypes.MotorInfo and isinstance(
574
+ data, MotorInfo
575
+ ):
576
+ # if data and address in _device_view_models:
577
+ # motor_view_model = next(
578
+ # (
579
+ # motor
580
+ # for motor in _device_view_models[address].motors
581
+ # if motor.motor_id == data.motor_id
582
+ # ),
583
+ # None,
584
+ # )
585
+ # if motor_view_model:
586
+ # motor_view_model.update_info(data)
587
+ pass
588
+ elif update_type == MessageUpdater.UpdateTypes.Status and isinstance(
589
+ data, DeviceStatusValues
590
+ ):
591
+ if data:
592
+ device = self._ellDevices.addressedDevice(address)
593
+ stage_path = (
594
+ f'{str(ElliptecStageParams.DEVICES)}.'
595
+ f'ELL{device.deviceInfo.DeviceType.value} - {address}'
596
+ )
597
+
598
+ param = self._elliptecStageView.get_param(
599
+ f'{stage_path}.{str(ElliptecStageParams.STATUS)}'
600
+ )
601
+ if param:
602
+ param.setValue(data.name)
603
+
604
+ if data not in [DeviceStatusValues.OK, DeviceStatusValues.Busy]:
605
+ self._output.appendHtml(
606
+ f'<span style="color: red;">'
607
+ f'Device #{address} error: {data.name}</span>'
608
+ )
609
+ elif update_type == MessageUpdater.UpdateTypes.Position and isinstance(
610
+ data, (float, int)
611
+ ):
612
+ try:
613
+ device = self._ellDevices.addressedDevice(address)
614
+ stage_path = (
615
+ f'{str(ElliptecStageParams.DEVICES)}.'
616
+ f'ELL{device.deviceInfo.DeviceType.value} - {address}'
617
+ )
618
+
619
+ position = float(data)
620
+ param = self._elliptecStageView.get_param(
621
+ f'{stage_path}.{str(ElliptecStageParams.POSITION)}'
622
+ )
623
+ if param:
624
+ param.setValue(position)
625
+ if device.deviceInfo.DeviceType in [
626
+ DeviceID.DeviceTypes.Shutter4,
627
+ DeviceID.DeviceTypes.Shutter6,
628
+ ]:
629
+ n = (
630
+ 4
631
+ if device.deviceInfo.DeviceType == DeviceID.DeviceTypes.Shutter4
632
+ else 6
633
+ )
634
+ increment = device.deviceInfo.Travel / (n - 1)
635
+ slot = position // increment
636
+ param = self._elliptecStageView.get_param(
637
+ f'{stage_path}.{str(ElliptecStageParams.SLOT)}'
638
+ )
639
+ if param:
640
+ param.setValue(slot)
641
+ except Exception as e:
642
+ self._output.appendHtml(
643
+ f'<span style="color: red;">Device #{address} error: {e}</span>'
644
+ )
645
+ elif update_type in [
646
+ MessageUpdater.UpdateTypes.PolarizerPositions,
647
+ MessageUpdater.UpdateTypes.PaddlePosition,
648
+ ]:
649
+ pass
650
+ elif update_type == MessageUpdater.UpdateTypes.HomeOffset and isinstance(
651
+ data, (float, int)
652
+ ):
653
+ try:
654
+ home_offset = float(data)
655
+ # if address in _device_view_models:
656
+ # _device_view_models[address].update_home_offset(home_offset)
657
+ except Exception as e:
658
+ self._output.appendHtml(
659
+ f'<span style="color: red;">Device #{address} error: {e}</span>'
660
+ )
661
+ elif update_type == MessageUpdater.UpdateTypes.JogstepSize and isinstance(
662
+ data, (float, int)
663
+ ):
664
+ try:
665
+ device = self._ellDevices.addressedDevice(address)
666
+ stage_path = (
667
+ f'{str(ElliptecStageParams.DEVICES)}.'
668
+ f'ELL{device.deviceInfo.DeviceType.value} - {address}'
669
+ )
670
+
671
+ jog_step = float(data)
672
+ param = self._elliptecStageView.get_param(
673
+ f'{stage_path}.{str(ElliptecStageParams.JOG_STEP_SIZE)}'
674
+ )
675
+ if param:
676
+ param.setValue(jog_step)
677
+ # if address in _device_view_models:
678
+ # _device_view_models[address].update_jogstep_size(jog_step)
679
+ except Exception as e:
680
+ self._output.appendHtml(
681
+ f'<span style="color: red;">Device #{address} error: {e}</span>'
682
+ )
683
+
684
+ def __str__(self):
685
+ return 'Elliptec Devices'
686
+
687
+
688
+ if __name__ == '__main__':
689
+ import sys
690
+
691
+ from microEye.qt import QApplication
692
+
693
+ try:
694
+ print('Starting application...')
695
+ app = QApplication(sys.argv)
696
+
697
+ print('Creating ElliptecView...')
698
+ view = ElliptecView()
699
+
700
+ print('Showing window...')
701
+ view.show()
702
+
703
+ print('Entering event loop...')
704
+ sys.exit(app.exec())
705
+ except Exception as e:
706
+ print(f'An error occurred: {e}')