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