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,607 @@
1
+ import re
2
+ from enum import Enum
3
+ from typing import Optional, Union, overload
4
+
5
+ from pyqtgraph.parametertree import Parameter
6
+
7
+ from microEye.hardware.stages.stabilizer import FocusStabilizer
8
+ from microEye.hardware.stages.stage import stage
9
+ from microEye.qt import QtCore, QtSerialPort, QtWidgets, Signal
10
+ from microEye.utils import Tree
11
+
12
+
13
+ class StageParams(Enum):
14
+ '''
15
+ Enum class defining Stage parameters.
16
+ '''
17
+
18
+ MODEL = 'Model'
19
+
20
+ HOME = 'Home'
21
+ REFRESH = 'Refresh'
22
+ JUMP_P = 'Jump +'
23
+ JUMP_N = 'Jump -'
24
+ STEP_P = 'Step +'
25
+ STEP_N = 'Step -'
26
+
27
+ SERIAL_PORT = 'Serial Port'
28
+ PORT = 'Serial Port.Port'
29
+ BAUDRATE = 'Serial Port.Baudrate'
30
+ SET_PORT = 'Serial Port.Set Config'
31
+ OPEN = 'Serial Port.Connect'
32
+ CLOSE = 'Serial Port.Disconnect'
33
+ PORT_STATE = 'Serial Port.State'
34
+
35
+ OPTIONS = 'Options'
36
+ STEP = 'Options.Step Size [nm]'
37
+ JUMP = 'Options.Jump Size [um]'
38
+
39
+ READINGS = 'Readings'
40
+ Z_POSITION = 'Readings.Z Position [um]'
41
+
42
+ INFO = 'Info'
43
+
44
+ REMOVE = 'Remove Device'
45
+
46
+ def __str__(self):
47
+ '''
48
+ Return the last part of the enum value (Param name).
49
+ '''
50
+ return self.value.split('.')[-1]
51
+
52
+ def get_path(self):
53
+ '''
54
+ Return the full parameter path.
55
+ '''
56
+ return self.value.split('.')
57
+
58
+
59
+ class PzFocSignals(QtCore.QObject):
60
+ positionChanged = Signal(float)
61
+
62
+
63
+ class PzFoc:
64
+ '''PiezoConcept FOC 1-axis stage adapter.'''
65
+
66
+ def __init__(self, max_um: int = 100):
67
+ '''
68
+ Initializes a PiezoConcept FOC 1-axis stage adapter.
69
+ '''
70
+ self.signals = PzFocSignals()
71
+
72
+ self.ZPosition = 50000
73
+ self.LastCmd = ''
74
+ self.Received = ''
75
+
76
+ self.max_um = max_um
77
+
78
+ self.serial = QtSerialPort.QSerialPort(None, readyRead=self.rx_piezo)
79
+ self.serial.setBaudRate(115200)
80
+ self.serial.setPortName('COM5')
81
+
82
+ @property
83
+ def max_nm(self) -> int:
84
+ '''Maximum stage position in nanometers.'''
85
+ return self.max_um * 1000
86
+
87
+ def isOpen(self):
88
+ '''Returns True if connected.'''
89
+ return self.serial.isOpen()
90
+
91
+ def open(self):
92
+ '''Opens the serial port.'''
93
+ if not self.isOpen():
94
+ self.serial.open(QtCore.QIODevice.OpenModeFlag.ReadWrite)
95
+
96
+ def close(self):
97
+ '''Closes the supplied serial port.'''
98
+ if self.isOpen():
99
+ self.serial.close()
100
+
101
+ def setPortName(self, name: str):
102
+ '''Sets the serial port name.'''
103
+ if not self.isOpen():
104
+ self.serial.setPortName(name)
105
+
106
+ def setBaudRate(self, baudRate: int):
107
+ '''Sets the serial port baudrate.'''
108
+ if not self.isOpen():
109
+ self.serial.setBaudRate(baudRate)
110
+
111
+ def write(self, value):
112
+ '''
113
+ Writes data to the serial port.
114
+
115
+ Parameters
116
+ ----------
117
+ data : QByteArray | bytes | bytearray
118
+ The data to be written to the serial port.
119
+
120
+ Returns
121
+ -------
122
+ int
123
+ The number of bytes written to the serial port, or a negative value
124
+ if an error occurs.
125
+
126
+ Raises
127
+ ------
128
+ IOError
129
+ If the serial port is not open.
130
+ '''
131
+ return self.serial.write(value)
132
+
133
+ def GETZ(self):
134
+ '''Gets the current stage position along the Z axis.'''
135
+ if self.isOpen():
136
+ self.write(b'GET_Z\n')
137
+ self.LastCmd = 'GETZ'
138
+
139
+ def HOME(self):
140
+ '''Centers the stage position along the Z axis.'''
141
+ if self.isOpen():
142
+ self.ZPosition = self.max_nm // 2
143
+ self.write(f'MOVEZ {self.max_um // 2}u\n'.encode())
144
+ self.LastCmd = 'MOVEZ'
145
+
146
+ def ABSOLUTE(self, pos: int):
147
+ '''
148
+ Moves the stage to an absolute position.
149
+ '''
150
+ if self.isOpen():
151
+ self.ZPosition = min(max(pos, 0), self.max_nm)
152
+ self.write(f'MOVEZ {pos}u\n'.encode())
153
+ self.LastCmd = 'MOVEZ'
154
+
155
+ def REFRESH(self):
156
+ '''Refresh the stage position
157
+ to the set value in case of discrepancy.
158
+ '''
159
+ if self.isOpen():
160
+ self.write(('MOVEZ ' + str(self.ZPosition) + 'n\n').encode('utf-8'))
161
+ self.LastCmd = 'MOVEZ'
162
+
163
+ def UP(self, step: int):
164
+ '''
165
+ Moves the stage up by a specified number of steps.
166
+
167
+ Parameters
168
+ ----------
169
+ step : int
170
+ The number of steps to move up in nanometers.
171
+
172
+ Raises
173
+ ------
174
+ IOError
175
+ If the serial port is not open or an error occurs while writing.
176
+ '''
177
+ if self.isOpen():
178
+ self.ZPosition = min(max(self.ZPosition + step, 0), self.max_nm)
179
+ self.write(('MOVEZ ' + str(self.ZPosition) + 'n\n').encode('utf-8'))
180
+ self.LastCmd = 'MOVEZ'
181
+
182
+ def DOWN(self, step: int):
183
+ '''
184
+ Moves the stage down by a specified number of steps.
185
+
186
+ Parameters
187
+ ----------
188
+ step : int
189
+ The number of steps to move down in nanometers.
190
+
191
+ Raises
192
+ ------
193
+ IOError
194
+ If the serial port is not open or an error occurs while writing.
195
+ '''
196
+ if self.isOpen():
197
+ self.ZPosition = min(max(self.ZPosition - step, 0), self.max_nm)
198
+ self.write(('MOVEZ ' + str(self.ZPosition) + 'n\n').encode('utf-8'))
199
+ self.LastCmd = 'MOVEZ'
200
+
201
+ def rx_piezo(self):
202
+ '''
203
+ Reads data from the serial port and processes it.
204
+
205
+ This method is called automatically when new data is available in the
206
+ serial port input buffer. It reads the data, parses the response, and
207
+ updates the appropriate attributes of the `PzFoc` object as needed.
208
+
209
+ Raises
210
+ ------
211
+ IOError
212
+ If the serial port is not open, or an error occurs while reading.
213
+ '''
214
+ self.Received = str(self.serial.readAll(), encoding='utf8')
215
+ if self.LastCmd != 'GETZ':
216
+ self.GETZ()
217
+ else:
218
+ match = re.search(r' *(\d+\.\d+).*um.*', self.Received)
219
+ if match:
220
+ self.ZPosition = int(float(match.group(1)) * 1000)
221
+ self.signals.positionChanged.emit(float(match.group(1)))
222
+
223
+ def getQWidget(self):
224
+ '''Generates a PzFocView with stage controls.'''
225
+ return PzFocView(stage=self)
226
+
227
+
228
+ class PzFocView(Tree):
229
+ PARAMS = StageParams
230
+ removed = Signal(object)
231
+
232
+ def __init__(
233
+ self, parent: Optional['QtWidgets.QWidget'] = None, stage: PzFoc = None
234
+ ):
235
+ '''
236
+ Initialize the PzFocView instance.
237
+
238
+ This method initializes the `PzFocView` instance, sets up the stage signals,
239
+ creates the parameter tree, and sets up the GUI elements.
240
+
241
+ Parameters
242
+ ----------
243
+ parent : Optional[QWidget]
244
+ The parent widget.
245
+ stage : Optional[`PzFoc`]
246
+ The stage to be controlled by the GUI. If None, a new stage instance is
247
+ created.
248
+ '''
249
+ self.stage = stage if stage else PzFoc()
250
+
251
+ super().__init__(parent=parent)
252
+
253
+ def create_parameters(self):
254
+ '''
255
+ Create the parameter tree structure.
256
+
257
+ This method creates and sets up the parameter tree structure for the
258
+ `PzFocView` class.
259
+ '''
260
+ params = [
261
+ {
262
+ 'name': str(StageParams.MODEL),
263
+ 'type': 'str',
264
+ 'value': 'PiezoConcept FOC 1-axis',
265
+ 'readonly': True,
266
+ },
267
+ {'name': str(StageParams.HOME), 'type': 'action'},
268
+ {'name': str(StageParams.REFRESH), 'type': 'action'},
269
+ {'name': str(StageParams.JUMP_P), 'type': 'action'},
270
+ {'name': str(StageParams.STEP_P), 'type': 'action'},
271
+ {'name': str(StageParams.STEP_N), 'type': 'action'},
272
+ {'name': str(StageParams.JUMP_N), 'type': 'action'},
273
+ {
274
+ 'name': str(StageParams.SERIAL_PORT),
275
+ 'type': 'group',
276
+ 'expanded': False,
277
+ 'children': [
278
+ {
279
+ 'name': str(StageParams.PORT),
280
+ 'type': 'list',
281
+ 'limits': [
282
+ info.portName()
283
+ for info in QtSerialPort.QSerialPortInfo.availablePorts()
284
+ ],
285
+ },
286
+ {
287
+ 'name': str(StageParams.BAUDRATE),
288
+ 'type': 'list',
289
+ 'value': 115200,
290
+ 'limits': [
291
+ bd
292
+ for bd in QtSerialPort.QSerialPortInfo.standardBaudRates()
293
+ ],
294
+ },
295
+ {'name': str(StageParams.SET_PORT), 'type': 'action'},
296
+ {'name': str(StageParams.OPEN), 'type': 'action'},
297
+ {'name': str(StageParams.CLOSE), 'type': 'action'},
298
+ {
299
+ 'name': str(StageParams.PORT_STATE),
300
+ 'type': 'str',
301
+ 'value': 'closed',
302
+ 'readonly': True,
303
+ },
304
+ ],
305
+ },
306
+ {
307
+ 'name': str(StageParams.READINGS),
308
+ 'type': 'group',
309
+ 'children': [
310
+ {
311
+ 'name': str(StageParams.Z_POSITION),
312
+ 'type': 'float',
313
+ 'value': 0.0,
314
+ 'readonly': True,
315
+ 'decimals': 6,
316
+ },
317
+ ],
318
+ },
319
+ {
320
+ 'name': str(StageParams.OPTIONS),
321
+ 'type': 'group',
322
+ 'children': [
323
+ {
324
+ 'name': str(StageParams.STEP),
325
+ 'type': 'int',
326
+ 'value': 100,
327
+ 'limits': [1, 1000],
328
+ 'step': 5,
329
+ },
330
+ {
331
+ 'name': str(StageParams.JUMP),
332
+ 'type': 'int',
333
+ 'value': 1,
334
+ 'limits': [1, 100],
335
+ 'step': 5,
336
+ },
337
+ ],
338
+ },
339
+ {'name': str(StageParams.REMOVE), 'type': 'action'},
340
+ ]
341
+
342
+ self.param_tree = Parameter.create(name='', type='group', children=params)
343
+ self.param_tree.sigTreeStateChanged.connect(self.change)
344
+ self.header().setSectionResizeMode(
345
+ QtWidgets.QHeaderView.ResizeMode.ResizeToContents
346
+ )
347
+
348
+ def remove_widget(self):
349
+ '''
350
+ Remove the widget from the parent layout.
351
+
352
+ This method removes the widget from the parent layout and deletes it.
353
+ '''
354
+ if self.parent() and not self.stage.isOpen():
355
+ self.parent().removeWidget(self)
356
+ self.removed.emit(self)
357
+ self.deleteLater()
358
+ else:
359
+ print(f'Disconnect FOC stage before removing!')
360
+
361
+ def __str__(self):
362
+ return f'Piezo Concept FOC Stage ({self.stage.serial.portName()})'
363
+
364
+
365
+ class PzFocController:
366
+ def __init__(self, stage: PzFoc = None, view: PzFocView = None):
367
+ '''
368
+ Initialize the PzFocController class.
369
+
370
+ This class controls the interaction between the PzFoc stage
371
+ and the PzFocView widget.
372
+
373
+ Parameters
374
+ ----------
375
+ stage: pz_foc
376
+ The stage to be controlled.
377
+ Default is a new PzFoc instance.
378
+ view: FOC_View
379
+ The GUI/view for the stage.
380
+ Default is a new PzFocView instance with the stage as an argument.
381
+ '''
382
+ self.stage = stage if stage else PzFoc()
383
+ self.view = view if view else PzFocView(stage=self.stage)
384
+
385
+ self.view.get_param(StageParams.JUMP_P).sigActivated.connect(
386
+ lambda: self.moveStage(
387
+ True, self.view.get_param_value(StageParams.JUMP) * 1000, True
388
+ )
389
+ )
390
+ self.view.get_param(StageParams.JUMP_N).sigActivated.connect(
391
+ lambda: self.moveStage(
392
+ False, self.view.get_param_value(StageParams.JUMP) * 1000, True
393
+ )
394
+ )
395
+ self.view.get_param(StageParams.STEP_P).sigActivated.connect(
396
+ lambda: self.moveStage(
397
+ True, self.view.get_param_value(StageParams.STEP), True
398
+ )
399
+ )
400
+ self.view.get_param(StageParams.STEP_N).sigActivated.connect(
401
+ lambda: self.moveStage(
402
+ False, self.view.get_param_value(StageParams.STEP), True
403
+ )
404
+ )
405
+
406
+ self.view.get_param(StageParams.SET_PORT).sigActivated.connect(self.set_config)
407
+ self.view.get_param(StageParams.OPEN).sigActivated.connect(
408
+ lambda: self.stage.open()
409
+ )
410
+ self.view.get_param(StageParams.CLOSE).sigActivated.connect(
411
+ lambda: self.stage.close()
412
+ )
413
+
414
+ self.view.get_param(StageParams.HOME).sigActivated.connect(self.stage.HOME)
415
+ self.view.get_param(StageParams.REFRESH).sigActivated.connect(
416
+ self.stage.REFRESH
417
+ )
418
+
419
+ self.stage.signals.positionChanged.connect(
420
+ lambda value: self.set_param_value(StageParams.Z_POSITION, value)
421
+ )
422
+
423
+ def set_config(self):
424
+ '''Sets the serial port configuration.
425
+
426
+ This method sets the serial port configuration based on the current
427
+ settings in the parameter tree.
428
+ '''
429
+ if not self.stage.isOpen():
430
+ self.stage.setPortName(self.view.get_param_value(StageParams.PORT))
431
+ self.stage.setBaudRate(self.view.get_param_value(StageParams.BAUDRATE))
432
+
433
+ def setPortName(self, value: str):
434
+ '''
435
+ Set the name of the serial port.
436
+
437
+ Parameters
438
+ ----------
439
+ value : str
440
+ The name of the serial port.
441
+ '''
442
+ self.stage.serial.setPortName(value)
443
+ self.view.set_param_value(StageParams.PORT, value)
444
+
445
+ def setBaudRate(self, value: int):
446
+ '''
447
+ Set the baud rate of the serial port.
448
+
449
+ Parameters
450
+ ----------
451
+ value : int
452
+ The baud rate of the serial port.
453
+ '''
454
+ self.stage.serial.setBaudRate(value)
455
+ self.view.set_param_value(StageParams.BAUDRATE, value)
456
+
457
+ def connect(self):
458
+ '''
459
+ Opens the stage serial port.
460
+ '''
461
+ self.stage.open()
462
+
463
+ def disconnect(self):
464
+ '''
465
+ Closes the stage serial port.
466
+ '''
467
+ self.stage.close()
468
+
469
+ def isOpen(self):
470
+ '''
471
+ Check if the stage is open.
472
+
473
+ Returns
474
+ -------
475
+ bool
476
+ True if the stage is open, False otherwise.
477
+ '''
478
+ return self.stage.isOpen()
479
+
480
+ def getStep(self):
481
+ '''
482
+ Get the current step size.
483
+
484
+ Returns
485
+ -------
486
+ float
487
+ The current step size.
488
+ '''
489
+ return self.view.get_param_value(StageParams.STEP)
490
+
491
+ def setStep(self, value: float, incerement: bool = False):
492
+ '''
493
+ Set the step size.
494
+
495
+ Parameters
496
+ ----------
497
+ value : float
498
+ The new step size value.
499
+ incerement : bool, optional
500
+ If True, increment the current step size value by the given value, otherwise
501
+ set the step size to the given value.
502
+ '''
503
+ if incerement:
504
+ step = self.view.get_param_value(StageParams.STEP)
505
+ self.view.set_param_value(StageParams.STEP, step + value)
506
+ else:
507
+ self.view.set_param_value(StageParams.STEP, value)
508
+
509
+ def moveAbsolute(self, pos: int):
510
+ '''
511
+ Move the stage to an absolute position.
512
+
513
+ Parameters
514
+ ----------
515
+ pos : int
516
+ The absolute position to move to in nanometers.
517
+ '''
518
+ focusStabilizer = FocusStabilizer.instance()
519
+ if focusStabilizer is not None and focusStabilizer.isFocusStabilized():
520
+ return
521
+
522
+ self.stage.ABSOLUTE(pos)
523
+
524
+ def moveStage(self, dir: bool, step_arg: Union[int, bool], interface: bool = False):
525
+ '''
526
+ Move the stage in a specified direction by a specified
527
+ number of steps in nanometers. Optional boolean argument
528
+ to specify jump or step move. If FocusStabilizer is stabilizing
529
+ and use calibration is set to True, moves the center pixel value
530
+ instead of the Z position when `interface` is True.
531
+
532
+ Parameters
533
+ ----------
534
+ dir: bool
535
+ Direction of the movement. If True, moves the stage up, else moves it down.
536
+ step_arg: Union[int, bool]
537
+ Number of steps to move in the specified direction.
538
+ If provided as a `bool`:
539
+ - `True`: Moves the stage by the value of the JUMP parameter.
540
+ - `False`: Moves the stage by the value of the STEP parameter.
541
+ interface : bool, optional
542
+ If True, moves the center pixel value instead of the Z position
543
+ when FocusStabilizer is stabilizing and use calibration is set to True.
544
+ '''
545
+ if isinstance(step_arg, bool):
546
+ if step_arg:
547
+ step_arg = 1000 * self.view.get_param_value(StageParams.JUMP)
548
+ else:
549
+ step_arg = self.view.get_param_value(StageParams.STEP)
550
+
551
+ focusStabilizer = FocusStabilizer.instance()
552
+ if (
553
+ focusStabilizer is not None
554
+ and focusStabilizer.isFocusStabilized()
555
+ and focusStabilizer.useCal()
556
+ and interface
557
+ ):
558
+ sign = 1 if dir else -1
559
+ focusStabilizer.setPeakPosition(
560
+ focusStabilizer.pixelCalCoeff() * step_arg * sign, True
561
+ )
562
+ else:
563
+ self.stage.UP(step_arg) if dir else self.stage.DOWN(step_arg)
564
+
565
+ def updatePortState(self):
566
+ '''
567
+ Update the port state in the parameter tree.
568
+
569
+ This method updates the port state in the parameter tree
570
+ to reflect the current state of the serial port.
571
+ '''
572
+ self.view.set_param_value(
573
+ StageParams.PORT_STATE, 'open' if self.isOpen() else 'closed'
574
+ )
575
+
576
+ def refreshPorts(self):
577
+ """
578
+ Refreshes the available serial ports list in the GUI.
579
+
580
+ This method updates the list of available serial ports in the GUI by fetching
581
+ the current list of available ports and setting it as the options for the
582
+ 'QSerial Port' parameter in the parameter tree.
583
+ """
584
+ if not self.isOpen():
585
+ self.view.get_param(StageParams.PORT).setLimits(
586
+ [
587
+ info.portName()
588
+ for info in QtSerialPort.QSerialPortInfo.availablePorts()
589
+ ]
590
+ )
591
+
592
+ def get_config(self) -> dict:
593
+ '''Get the current configuration of the stage.'''
594
+ return {
595
+ 'port': self.stage.serial.portName(),
596
+ 'baudrate': self.stage.serial.baudRate(),
597
+ 'max_um': self.stage.max_um,
598
+ }
599
+
600
+ def load_config(self, config: dict) -> None:
601
+ '''Load the configuration of the stage from a dictionary.'''
602
+ self.stage.setPortName(config.get('port', 'COM5'))
603
+ self.stage.setBaudRate(config.get('baudrate', 115200))
604
+ self.stage.max_um = config.get('max_um', 100)
605
+
606
+ def __str__(self):
607
+ return 'Piezo Concept FOC Stage Controller'