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,791 @@
1
+ from typing import Optional
2
+
3
+ from pyqtgraph.parametertree import Parameter
4
+
5
+ from microEye.hardware.lasers.io_params import LaserState, MB_Params
6
+ from microEye.qt import QtCore, QtSerialPort, QtWidgets, Signal
7
+ from microEye.utils import StartGUI, Tree
8
+
9
+
10
+ class io_combiner(QtSerialPort.QSerialPort):
11
+ '''
12
+ Class representing a laser combiner MatchBox device | Inherits QSerialPort
13
+ '''
14
+
15
+ INFO = b'r i'
16
+ '''Get MatchBox Info
17
+ '''
18
+ ON = b'e 1'
19
+ '''Enable the combiner
20
+ '''
21
+ OFF = b'e 0'
22
+ '''Disable the combiner
23
+ '''
24
+ READ = b'r r'
25
+ SETTINGS = b'r s'
26
+
27
+ ENABLE_1 = b'L1E'
28
+ '''Enable 1st Laser Diode
29
+ '''
30
+ ENABLE_2 = b'L2E'
31
+ '''Enable 2nd Laser Diode
32
+ '''
33
+ ENABLE_3 = b'L3E'
34
+ '''Enable 3rd Laser Diode
35
+ '''
36
+ ENABLE_4 = b'L4E'
37
+ '''Enable 4th Laser Diode
38
+ '''
39
+ DISABLE_1 = b'L1D'
40
+ '''Disable 1st Laser Diode
41
+ '''
42
+ DISABLE_2 = b'L2D'
43
+ '''Disable 2nd Laser Diode
44
+ '''
45
+ DISABLE_3 = b'L3D'
46
+ '''Disable 3rd Laser Diode
47
+ '''
48
+ DISABLE_4 = b'L4D'
49
+ '''Disable 4th Laser Diode
50
+ '''
51
+
52
+ GET_WAVELENGTHS = b'Ln?'
53
+ '''Get available wavelengths
54
+ '''
55
+
56
+ MAX_CUR = b'Lm?'
57
+ '''Get maximum current
58
+ '''
59
+ CUR_SET = b'Lc?'
60
+ '''Get the current set value
61
+ '''
62
+ CUR_CUR = b'Lr'
63
+ '''Get the current reading
64
+ '''
65
+ STATUS = b'Le'
66
+ '''Get the laser diodes enabled(1)/disabled(0) states
67
+ '''
68
+ START = b'c u 2 35488'
69
+ '''TBA
70
+ '''
71
+
72
+ DataReady = Signal(str, bytes)
73
+
74
+ Max_Power = 0.0
75
+
76
+ R_Diode_Temp = 0.0
77
+ R_Crystal_Temp = 0.0
78
+ R_Body_Temp = 0.0
79
+
80
+ R_LD_Current = '0.0mA'
81
+
82
+ R_Crystal_TEC_Load = '0%'
83
+ R_LD_TEC_Load = '0%'
84
+
85
+ R_Status = 'OFF'
86
+
87
+ R_Fan_Load = '0%'
88
+
89
+ R_Input_Voltage = '5V'
90
+
91
+ S_Crystal_Temp = 0.0
92
+ S_Diode_Temp = 0.0
93
+
94
+ S_LD_Current = '0.0mA'
95
+ S_Feedback_DAC = 0
96
+
97
+ S_Power = 0.0
98
+
99
+ S_LD_MaxCurrent = 0.0
100
+
101
+ S_Autostart_Mode = 'OFF'
102
+
103
+ S_Access_Level = 1
104
+
105
+ S_Fan_Temp = 0.0
106
+
107
+ Firmware = ''
108
+ Serial = ''
109
+ Model = ''
110
+ Operation_Time = ''
111
+ ON_Times = ''
112
+
113
+ Current = [0, 0, 0, 0]
114
+ Setting = [0, 0, 0, 0]
115
+ Max = [0, 0, 0, 0]
116
+ Wavelengths = [0, 0, 0, 0]
117
+
118
+ def SendCommand(self, command, log_print: bool = True, delay: int = 1):
119
+ '''Sends a specific command to the device and waits for
120
+ the response then emits the DataReady signal.
121
+ The DataReady signal passes the response and command.
122
+
123
+ Parameters
124
+ ----------
125
+ command : [bytes]
126
+ command to be sent, please check the constants
127
+ implemented in the MatchBox class.
128
+ '''
129
+ if self.isOpen():
130
+ self.readAll()
131
+ self.write(command)
132
+ self.waitForBytesWritten(500)
133
+ QtCore.QThread.msleep(delay)
134
+ while self.bytesAvailable() < 5:
135
+ self.waitForReadyRead(500)
136
+
137
+ response = (
138
+ str(
139
+ self.readAll().replace(b'\xff', b''),
140
+ encoding='utf-8',
141
+ errors='replace',
142
+ )
143
+ .strip('\r\n')
144
+ .strip()
145
+ )
146
+ if log_print:
147
+ print(response, command)
148
+
149
+ self.DataReady.emit(response, command)
150
+
151
+ return response
152
+
153
+ def OpenCOM(self):
154
+ '''Opens the serial port and initializes the combiner.'''
155
+ if not self.isOpen():
156
+ self.open(QtCore.QIODevice.OpenModeFlag.ReadWrite)
157
+ self.flush()
158
+
159
+ if self.isOpen():
160
+ self.SendCommand(io_combiner.ON)
161
+ self.SendCommand(io_combiner.START)
162
+ # self.SendCommand(io_single_laser.STATUS)
163
+ self.GetInfo()
164
+ self.GetWavelengths()
165
+ self.SetCurrent(1, 0)
166
+ self.SetCurrent(2, 0)
167
+ self.SetCurrent(3, 0)
168
+ self.SetCurrent(4, 0)
169
+ self.GetMaxCurrent()
170
+
171
+ def CloseCOM(self):
172
+ '''Closes the serial port.'''
173
+ if self.isOpen():
174
+ self.SendCommand(io_combiner.OFF)
175
+ self.waitForBytesWritten(500)
176
+
177
+ self.close()
178
+
179
+ def GetMaxCurrent(self):
180
+ if self.isOpen():
181
+ res = self.SendCommand(io_combiner.MAX_CUR)
182
+
183
+ if not ('<ERR>' in res or '<ACK>' in res):
184
+ values = res.strip('<').strip('>').split(' ')
185
+ if len(values) >= 4:
186
+ self.Max = list(map(int, values))
187
+
188
+ def GetCurrent(self, log_print: bool = False):
189
+ if self.isOpen():
190
+ res = self.SendCommand(io_combiner.CUR_CUR, log_print)
191
+
192
+ if not ('<ERR>' in res or '<ACK>' in res):
193
+ values = res.strip('<').strip(' mA>').split(' ')
194
+ if len(values) >= 4:
195
+ self.Current = list(map(float, values))
196
+
197
+ def GetSetCurrent(self, log_print: bool = False):
198
+ if self.isOpen():
199
+ res = self.SendCommand(io_combiner.CUR_SET, log_print)
200
+
201
+ if not ('<ERR>' in res or '<ACK>' in res):
202
+ values = res.strip('<').strip('>').split(' ')
203
+ if len(values) >= 4:
204
+ self.Setting = list(map(int, values))
205
+
206
+ def GetWavelengths(self):
207
+ if self.isOpen():
208
+ res = self.SendCommand(io_combiner.GET_WAVELENGTHS)
209
+
210
+ if not ('<ERR>' in res or '<ACK>' in res):
211
+ values = res.strip('<').strip(' >').strip().split(' ')
212
+ self.Wavelengths = []
213
+ if len(values) == 3:
214
+ self.Wavelengths.append(0)
215
+ for x in range(len(values)):
216
+ if values[x].isdigit():
217
+ self.Wavelengths.append(int(values[x]))
218
+ else:
219
+ self.Wavelengths.append(0)
220
+ # if len(values) >= 3:
221
+ # self.Wavelengths = list(map(int, values))
222
+
223
+ def GetReadings(self, log_print: bool = True):
224
+ if self.isOpen():
225
+ res = self.SendCommand(io_combiner.READ, log_print)
226
+
227
+ if not ('<ERR>' in res or '<ACK>' in res):
228
+ readings = res.split(' ')
229
+ if len(readings) >= 10:
230
+ self.R_Diode_Temp = float(readings[1])
231
+ self.R_Crystal_Temp = float(readings[2])
232
+ self.R_Body_Temp = float(readings[3])
233
+ self.R_LD_Current = readings[4]
234
+ self.R_Crystal_TEC_Load = readings[5]
235
+ self.R_LD_TEC_Load = readings[6]
236
+ self.R_Status = readings[7]
237
+ self.R_Fan_Load = readings[8]
238
+ self.R_Input_Voltage = readings[9]
239
+
240
+ return {
241
+ MB_Params.LD_TEMP: self.R_Diode_Temp,
242
+ MB_Params.CRYSTAL_TEMP: self.R_Crystal_Temp,
243
+ MB_Params.BODY_TEMP: self.R_Body_Temp,
244
+ MB_Params.LD_CURRENT: self.R_LD_Current,
245
+ MB_Params.CRYSTAL_TEC_LOAD: self.R_Crystal_TEC_Load,
246
+ MB_Params.LD_TEC_LOAD: self.R_LD_TEC_Load,
247
+ MB_Params.STATUS: self.R_Status,
248
+ MB_Params.FAN_LOAD: self.R_Fan_Load,
249
+ MB_Params.IN_VOLTAGE: self.R_Input_Voltage,
250
+ }
251
+
252
+ return {}
253
+
254
+ def GetSettings(self, log_print: bool = True):
255
+ if self.isOpen():
256
+ res = self.SendCommand(io_combiner.SETTINGS, log_print)
257
+
258
+ if not ('<ERR>' in res or '<ACK>' in res):
259
+ readings = res.split(' ')
260
+ if len(readings) >= 10:
261
+ self.S_Crystal_Temp = float(readings[1]) / 100
262
+ self.S_Diode_Temp = float(readings[2]) / 100
263
+ self.S_LD_Current = float(readings[3])
264
+ self.S_Feedback_DAC = float(readings[4])
265
+ self.S_Power = float(readings[5])
266
+ self.S_LD_MaxCurrent = float(readings[6])
267
+ self.S_Autostart_Mode = readings[7]
268
+ self.S_Access_Level = int(readings[8])
269
+ self.S_Fan_Temp = float(readings[9]) / 100
270
+
271
+ return {
272
+ MB_Params.CRYSTAL_TEMP_SET: self.S_Crystal_Temp,
273
+ MB_Params.LD_TEMP_SET: self.S_Diode_Temp,
274
+ MB_Params.LD_CURRENT_SET: self.S_LD_Current,
275
+ MB_Params.FEEDBACK_DAC: self.S_Feedback_DAC,
276
+ MB_Params.POWER_READ: self.S_Power,
277
+ MB_Params.LD_CURRENT_MAX: self.S_LD_MaxCurrent,
278
+ MB_Params.AUTO_MODE: self.S_Autostart_Mode,
279
+ MB_Params.ACCESS_LEVEL: self.S_Access_Level,
280
+ MB_Params.FAN_TEMP_SET: self.S_Fan_Temp,
281
+ }
282
+
283
+ return {}
284
+
285
+ def SetCurrent(self, index: int, value: int) -> bool:
286
+ if self.isOpen():
287
+ if not isinstance(value, int):
288
+ raise TypeError('Current must be an int!')
289
+ if value < 0 or value > self.Max[index - 1]:
290
+ raise ValueError(f'Current must be between 0 and {self.Max[index-1]}')
291
+
292
+ res = self.SendCommand(f'Lc{index:.0f} {value:.0f}'.encode())
293
+
294
+ return '<ACK>' in res
295
+
296
+ def SetDisabled(self, index: int) -> bool:
297
+ if self.isOpen():
298
+ res = self.SendCommand(f'L{index:.0f}D'.encode())
299
+
300
+ return '<ACK>' in res
301
+
302
+ def SetEnabled(self, index: int) -> bool:
303
+ if self.isOpen():
304
+ res = self.SendCommand(f'L{index:.0f}E'.encode())
305
+
306
+ return '<ACK>' in res
307
+
308
+ def GetInfo(self):
309
+ if self.isOpen():
310
+ res = self.SendCommand(io_combiner.INFO, delay=50)
311
+
312
+ if not ('<ERR>' in res or '<ACK>' in res):
313
+ info = res.split('\r\n')
314
+
315
+ self.Firmware = info[0]
316
+ self.Serial = info[1].split(':')[1]
317
+ self.Model = info[2].split(':')[1]
318
+ self.Operation_Time = info[3]
319
+ self.ON_Times = info[4]
320
+
321
+
322
+ class LaserSwitches:
323
+ def __init__(self, Laser: io_combiner, index=1, wavelength=638) -> None:
324
+ super().__init__()
325
+
326
+ self.Laser = Laser
327
+ self.index = index
328
+ self.wavelength = wavelength
329
+ self.state = LaserState.OFF
330
+ self.max_current = Laser.Max[index - 1]
331
+
332
+ self.STATE = f'Options.{self.wavelength:d}nm State'
333
+ self.CURRENT = f'Options.{self.wavelength:d}nm Current [mA]'
334
+
335
+ def laser_state_changed(self, param: Parameter, value):
336
+ '''Sends enable/disable signals to the
337
+ laser combiner according to selected setting
338
+
339
+ Parameters
340
+ ----------
341
+ object : [QRadioButton]
342
+ the radio button toggled
343
+ '''
344
+ self.state = value
345
+ if self.state == LaserState.OFF:
346
+ self.Laser.SetDisabled(self.index)
347
+ else:
348
+ self.Laser.SetEnabled(self.index)
349
+
350
+ def SetCurrent(self, value):
351
+ self.Laser.SetCurrent(self.index, value)
352
+
353
+ def GetRelayState(self):
354
+ return f'L{self.wavelength:d}{self.state}'
355
+
356
+ def add_params(self, parent: Tree):
357
+ params = [
358
+ {
359
+ 'name': self.STATE.split('.')[1],
360
+ 'type': 'list',
361
+ 'value': LaserState.OFF,
362
+ 'limits': LaserState.get_list(),
363
+ },
364
+ {
365
+ 'name': self.CURRENT.split('.')[1],
366
+ 'type': 'int',
367
+ 'value': 0,
368
+ 'limits': [0, self.max_current],
369
+ },
370
+ ]
371
+ for param in params:
372
+ parent.add_param_child(MB_Params.OPTIONS, param)
373
+
374
+ parent.get_param(self.STATE).sigValueChanged.connect(self.laser_state_changed)
375
+
376
+
377
+ class CombinerLaserWidget(Tree):
378
+ PARAMS = MB_Params
379
+ removed = Signal(object)
380
+
381
+ def __init__(self, parent: Optional['QtWidgets.QWidget'] = None):
382
+ '''Initializes a new CombinerLaserWidget instance.
383
+
384
+ Parameters
385
+ ----------
386
+ parent : Optional[QWidget]
387
+ The parent widget for this CombinerLaserWidget instance.
388
+
389
+ Attributes
390
+ ----------
391
+ Laser : io_combiner
392
+ The `io_combiner` instance used to communicate with the device.
393
+ '''
394
+ super().__init__()
395
+
396
+ self.Laser = io_combiner()
397
+
398
+ self._laserSwitches: list[LaserSwitches] = []
399
+
400
+ # Statues Bar Timer
401
+ self.timer = QtCore.QTimer()
402
+ self.timer.setInterval(500)
403
+ self.timer.timeout.connect(self.update_stats)
404
+ self.timer.start()
405
+
406
+ def create_parameters(self):
407
+ '''
408
+ Create the parameter tree structure.
409
+ '''
410
+ params = [
411
+ {
412
+ 'name': str(MB_Params.MODEL),
413
+ 'type': 'str',
414
+ 'value': 'N/A',
415
+ 'readonly': True,
416
+ },
417
+ {
418
+ 'name': str(MB_Params.WAVELENGTHS),
419
+ 'type': 'str',
420
+ 'value': 'N/A',
421
+ 'readonly': True,
422
+ },
423
+ {
424
+ 'name': str(MB_Params.SERIAL_PORT),
425
+ 'type': 'group',
426
+ 'expanded' : False,
427
+ 'children': [
428
+ {
429
+ 'name': str(MB_Params.PORT),
430
+ 'type': 'list',
431
+ 'limits': [
432
+ info.portName()
433
+ for info in QtSerialPort.QSerialPortInfo.availablePorts()
434
+ ],
435
+ },
436
+ {
437
+ 'name': str(MB_Params.BAUDRATE),
438
+ 'type': 'list',
439
+ 'value': 115200,
440
+ 'limits': [
441
+ baudrate
442
+ for baudrate in \
443
+ QtSerialPort.QSerialPortInfo.standardBaudRates()
444
+ ],
445
+ },
446
+ {'name': str(MB_Params.SET_PORT), 'type': 'action'},
447
+ {'name': str(MB_Params.OPEN), 'type': 'action'},
448
+ {'name': str(MB_Params.CLOSE), 'type': 'action'},
449
+ {
450
+ 'name': str(MB_Params.PORT_STATE),
451
+ 'type': 'str',
452
+ 'value': 'closed',
453
+ 'readonly': True,
454
+ },
455
+ ],
456
+ },
457
+ {
458
+ 'name': str(MB_Params.OPTIONS),
459
+ 'type': 'group',
460
+ 'children': [
461
+ {'name': str(MB_Params.SET_CURRENT), 'type': 'action'},
462
+ ],
463
+ },
464
+ {
465
+ 'name': str(MB_Params.READINGS),
466
+ 'type': 'group',
467
+ 'children': [
468
+ {
469
+ 'name': str(MB_Params.POWER_READ),
470
+ 'type': 'str',
471
+ 'value': '0',
472
+ 'readonly': True,
473
+ },
474
+ {
475
+ 'name': str(MB_Params.LD_CURRENT),
476
+ 'type': 'str',
477
+ 'value': '0',
478
+ 'readonly': True,
479
+ },
480
+ {
481
+ 'name': str(MB_Params.LD_CURRENT_SET),
482
+ 'type': 'str',
483
+ 'value': '0',
484
+ 'readonly': True,
485
+ },
486
+ {
487
+ 'name': str(MB_Params.LD_CURRENT_MAX),
488
+ 'type': 'str',
489
+ 'value': '0',
490
+ 'readonly': True,
491
+ },
492
+ {
493
+ 'name': str(MB_Params.LD_TEMP),
494
+ 'type': 'str',
495
+ 'value': '0',
496
+ 'readonly': True,
497
+ },
498
+ {
499
+ 'name': str(MB_Params.LD_TEMP_SET),
500
+ 'type': 'str',
501
+ 'value': '0',
502
+ 'readonly': True,
503
+ },
504
+ {
505
+ 'name': str(MB_Params.LD_TEC_LOAD),
506
+ 'type': 'str',
507
+ 'value': '0',
508
+ 'readonly': True,
509
+ },
510
+ {
511
+ 'name': str(MB_Params.CRYSTAL_TEMP),
512
+ 'type': 'str',
513
+ 'value': '0',
514
+ 'readonly': True,
515
+ },
516
+ {
517
+ 'name': str(MB_Params.CRYSTAL_TEMP_SET),
518
+ 'type': 'str',
519
+ 'value': '0',
520
+ 'readonly': True,
521
+ },
522
+ {
523
+ 'name': str(MB_Params.CRYSTAL_TEC_LOAD),
524
+ 'type': 'str',
525
+ 'value': '0',
526
+ 'readonly': True,
527
+ },
528
+ {
529
+ 'name': str(MB_Params.BODY_TEMP),
530
+ 'type': 'str',
531
+ 'value': '0',
532
+ 'readonly': True,
533
+ },
534
+ {
535
+ 'name': str(MB_Params.FAN_TEMP_SET),
536
+ 'type': 'str',
537
+ 'value': '0',
538
+ 'readonly': True,
539
+ },
540
+ {
541
+ 'name': str(MB_Params.FAN_LOAD),
542
+ 'type': 'str',
543
+ 'value': '0',
544
+ 'readonly': True,
545
+ },
546
+ {
547
+ 'name': str(MB_Params.FEEDBACK_DAC),
548
+ 'type': 'str',
549
+ 'value': '0',
550
+ 'readonly': True,
551
+ },
552
+ {
553
+ 'name': str(MB_Params.AUTO_MODE),
554
+ 'type': 'str',
555
+ 'value': '0',
556
+ 'readonly': True,
557
+ },
558
+ {
559
+ 'name': str(MB_Params.ACCESS_LEVEL),
560
+ 'type': 'str',
561
+ 'value': '0',
562
+ 'readonly': True,
563
+ },
564
+ {
565
+ 'name': str(MB_Params.STATUS),
566
+ 'type': 'str',
567
+ 'value': '0',
568
+ 'readonly': True,
569
+ },
570
+ {
571
+ 'name': str(MB_Params.IN_VOLTAGE),
572
+ 'type': 'str',
573
+ 'value': '0',
574
+ 'readonly': True,
575
+ },
576
+ ],
577
+ },
578
+ {
579
+ 'name': str(MB_Params.INFO),
580
+ 'type': 'group',
581
+ 'children': [
582
+ {
583
+ 'name': str(MB_Params.FIRMWARE),
584
+ 'type': 'str',
585
+ 'value': 'N/A',
586
+ 'readonly': True,
587
+ },
588
+ {
589
+ 'name': str(MB_Params.SERIAL),
590
+ 'type': 'str',
591
+ 'value': 'N/A',
592
+ 'readonly': True,
593
+ },
594
+ {
595
+ 'name': str(MB_Params.OPERATION_TIME),
596
+ 'type': 'str',
597
+ 'value': 'N/A',
598
+ 'readonly': True,
599
+ },
600
+ {
601
+ 'name': str(MB_Params.ON_TIMES),
602
+ 'type': 'str',
603
+ 'value': 'N/A',
604
+ 'readonly': True,
605
+ },
606
+ ],
607
+ },
608
+ {'name': str(MB_Params.REMOVE), 'type': 'action'},
609
+ ]
610
+
611
+ self.param_tree = Parameter.create(name='', type='group', children=params)
612
+ self.param_tree.sigTreeStateChanged.connect(self.change)
613
+ self.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
614
+
615
+ self.get_param(MB_Params.SET_PORT).sigActivated.connect(self.set_config)
616
+ self.get_param(MB_Params.OPEN).sigActivated.connect(self.laser_connect)
617
+ self.get_param(MB_Params.CLOSE).sigActivated.connect(
618
+ lambda: self.Laser.CloseCOM()
619
+ )
620
+ self.get_param(MB_Params.SET_CURRENT).sigActivated.connect(self.set_current)
621
+
622
+ self.get_param(MB_Params.REMOVE).sigActivated.connect(self.remove_widget)
623
+
624
+ def laser_connect(self):
625
+ '''Connects to the MatchBox device.
626
+
627
+ This method opens the serial port and initializes the laser.
628
+ '''
629
+ self.Laser.OpenCOM()
630
+
631
+ self.set_param_value(MB_Params.MODEL, self.Laser.Model)
632
+ self.set_param_value(MB_Params.WAVELENGTHS, str(self.Laser.Wavelengths))
633
+ self.set_param_value(MB_Params.FIRMWARE, self.Laser.Firmware)
634
+ self.set_param_value(MB_Params.SERIAL, self.Laser.Serial)
635
+
636
+ if not self._laserSwitches:
637
+ for idx in range(len(self.Laser.Wavelengths)):
638
+ if self.Laser.Wavelengths[idx] > 0:
639
+ switch = LaserSwitches(
640
+ self.Laser, idx + 1, self.Laser.Wavelengths[idx]
641
+ )
642
+ switch.add_params(self)
643
+ self._laserSwitches.append(switch)
644
+
645
+ def set_config(self):
646
+ '''Sets the serial port configuration.
647
+
648
+ This method sets the serial port configuration based on the current
649
+ settings in the parameter tree.
650
+ '''
651
+ if not self.Laser.isOpen():
652
+ self.Laser.setPortName(self.get_param_value(MB_Params.PORT))
653
+ self.Laser.setBaudRate(self.get_param_value(MB_Params.BAUDRATE))
654
+
655
+ def set_current(self):
656
+ for switch in self._laserSwitches:
657
+ value = self.get_param(switch.CURRENT).value()
658
+ switch.SetCurrent(value)
659
+
660
+ def update_stats(self):
661
+ """
662
+ Updates the statistics displayed in the MatchBox GUI.
663
+
664
+ This method retrieves the current readings and settings from the device, and
665
+ updates the corresponding parameter tree items with the new values. It also
666
+ sets the limits of the power parameter based on the maximum power that can be
667
+ set for the laser diode.
668
+
669
+ If the laser is not connected, the method sets the 'Port State' parameter to
670
+ 'closed'.
671
+ """
672
+ if self.Laser.isOpen():
673
+ readings = self.Laser.GetReadings(False)
674
+ settings = self.Laser.GetSettings(False)
675
+ self.Laser.GetCurrent()
676
+
677
+ for key, value in readings.items():
678
+ if isinstance(value, (int, float)):
679
+ self.set_param_value(key, f'{value:.2f}')
680
+ elif isinstance(value, str):
681
+ self.set_param_value(key, value)
682
+
683
+ for key, value in settings.items():
684
+ if isinstance(value, (int, float)):
685
+ self.set_param_value(key, f'{value:.2f}')
686
+ elif isinstance(value, str):
687
+ self.set_param_value(key, value)
688
+
689
+ self.set_param_value(
690
+ MB_Params.LD_CURRENT,
691
+ '{:.2f} mA, {:.2f} mA, {:.2f} mA, {:.2f} mA'.format(
692
+ *self.Laser.Current
693
+ ),
694
+ )
695
+ self.set_param_value(
696
+ MB_Params.LD_CURRENT_MAX,
697
+ '{:.2f} mA, {:.2f} mA, {:.2f} mA, {:.2f} mA'.format(*self.Laser.Max),
698
+ )
699
+ self.set_param_value(
700
+ MB_Params.LD_CURRENT_SET,
701
+ '{:.2f} mA, {:.2f} mA, {:.2f} mA, {:.2f} mA'.format(
702
+ *self.Laser.Setting
703
+ ),
704
+ )
705
+
706
+ self.set_param_value(MB_Params.OPERATION_TIME, self.Laser.Operation_Time)
707
+ self.set_param_value(MB_Params.ON_TIMES, self.Laser.ON_Times)
708
+
709
+ self.set_param_value(MB_Params.PORT_STATE, 'open')
710
+ else:
711
+ self.set_param_value(MB_Params.PORT_STATE, 'closed')
712
+
713
+ self.RefreshPorts()
714
+
715
+ def RefreshPorts(self):
716
+ """
717
+ Refreshes the available serial ports list in the GUI.
718
+
719
+ This method updates the list of available serial ports in the GUI by fetching
720
+ the current list of available ports and setting it as the options for the
721
+ 'Serial Port' parameter in the parameter tree.
722
+ """
723
+ if not self.Laser.isOpen():
724
+ self.get_param(MB_Params.PORT).setLimits(
725
+ [
726
+ info.portName()
727
+ for info in QtSerialPort.QSerialPortInfo.availablePorts()
728
+ ]
729
+ )
730
+
731
+ def GetRelayState(self):
732
+ states = ''
733
+ for switch in self._laserSwitches:
734
+ states += switch.GetRelayState()
735
+ return states
736
+
737
+ def get_config(self) -> dict:
738
+ '''
739
+ Returns the current configuration of the laser device as a dictionary.
740
+ '''
741
+ return {
742
+ 'port': self.Laser.portName(),
743
+ 'baudrate': self.Laser.baudRate(),
744
+ 'class': self.__class__.__name__,
745
+ }
746
+
747
+ def load_config(self, config: dict) -> bool:
748
+ '''
749
+ Loads the configuration from the given dictionary.
750
+ '''
751
+ port = config.get('port')
752
+ baudrate = config.get('baudrate')
753
+ if port:
754
+ self.Laser.setPortName(port)
755
+ self.set_param_value(MB_Params.PORT, port)
756
+ if baudrate:
757
+ self.Laser.setBaudRate(baudrate)
758
+ self.set_param_value(MB_Params.BAUDRATE, baudrate)
759
+
760
+ def remove_widget(self):
761
+ if self.parent() and not self.Laser.isOpen():
762
+ self.parent().removeWidget(self)
763
+ self.removed.emit(self)
764
+ self.deleteLater()
765
+ else:
766
+ print(f'Disconnect device {self.Laser.Model} before removing!')
767
+
768
+ def __str__(self):
769
+ return self.Laser.Model.strip() or self.__class__.__name__
770
+
771
+ def StartGUI():
772
+ '''Initializes a new QApplication and CombinerLaserWidget.
773
+
774
+ Use
775
+ -------
776
+ app, window = CombinerLaserWidget.StartGUI()
777
+
778
+ app.exec()
779
+
780
+ Returns
781
+ -------
782
+ tuple (QApplication, CombinerLaserWidget)
783
+ Returns a tuple with QApp and CombinerLaserWidget main window.
784
+ '''
785
+ return StartGUI(CombinerLaserWidget)
786
+
787
+
788
+ if __name__ == '__main__':
789
+ app, widget = CombinerLaserWidget.StartGUI()
790
+
791
+ app.exec()