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,528 @@
1
+ import os
2
+ import typing
3
+
4
+ from microEye.hardware.cams.camera_panel import Camera_Panel
5
+ from microEye.hardware.cams.dummy.dummy_panel import Dummy_Panel
6
+ from microEye.hardware.cams.micam import miDummy
7
+ from microEye.hardware.cams.thorlabs.thorlabs import CMD, thorlabs_camera
8
+ from microEye.hardware.cams.thorlabs.thorlabs_panel import Thorlabs_Panel
9
+ from microEye.hardware.pycromanager.devices import PycroCamera, PycroCore
10
+ from microEye.hardware.pycromanager.widgets import PycroPanel
11
+ from microEye.qt import QtGui, QtWidgets, Signal
12
+
13
+ try:
14
+ from pyueye import ueye
15
+
16
+ from microEye.hardware.cams.ueye.ueye_camera import IDS_Camera
17
+ from microEye.hardware.cams.ueye.ueye_panel import IDS_Panel
18
+ except Exception:
19
+ ueye = None
20
+ IDS_Camera = None
21
+
22
+ try:
23
+ import vimba as vb
24
+
25
+ from microEye.hardware.cams.vimba.vimba_cam import get_camera_list, vimba_cam
26
+ from microEye.hardware.cams.vimba.vimba_panel import Vimba_Panel
27
+ except Exception:
28
+ vb = None
29
+
30
+ def get_camera_list():
31
+ return []
32
+
33
+
34
+ class CameraList(QtWidgets.QWidget):
35
+ '''
36
+ A widget for displaying and managing a list of cameras.
37
+ '''
38
+
39
+ cameraAdded = Signal(Camera_Panel, bool)
40
+ cameraRemoved = Signal(Camera_Panel, bool)
41
+
42
+ CAMERAS = {'uEye': [], 'Vimba': [], 'UC480': [], 'miDummy': [], 'PycroCore': []}
43
+
44
+ def __init__(self, parent: typing.Optional['QtWidgets.QWidget'] = None):
45
+ '''
46
+ Initialize the camera list widget.
47
+
48
+ Parameters
49
+ ----------
50
+ parent : typing.Optional[QtWidgets.QWidget], optional
51
+ The parent widget of this camera list widget.
52
+ '''
53
+ super().__init__(parent=parent)
54
+
55
+ self.cam_list = None
56
+ self.item_model = QtGui.QStandardItemModel()
57
+ self.cached_autofocusCam = None
58
+
59
+ # Layout
60
+ self.InitLayout()
61
+
62
+ def InitLayout(self):
63
+ '''
64
+ Initialize the layout of the widget.
65
+ '''
66
+
67
+ # main layout
68
+ self.mainLayout = QtWidgets.QVBoxLayout()
69
+
70
+ self.cam_table = QtWidgets.QTableView()
71
+ self.cam_table.setModel(self.item_model)
72
+ self.cam_table.clearSelection()
73
+ self.cam_table.horizontalHeader().setStretchLastSection(True)
74
+ self.cam_table.horizontalHeader().setSectionResizeMode(
75
+ QtWidgets.QHeaderView.ResizeMode.ResizeToContents
76
+ )
77
+ self.cam_table.setSelectionBehavior(
78
+ QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows
79
+ )
80
+ self.cam_table.setSelectionMode(
81
+ QtWidgets.QAbstractItemView.SelectionMode.SingleSelection
82
+ )
83
+
84
+ self.HL_buttons = QtWidgets.QHBoxLayout()
85
+
86
+ self.add_cam = QtWidgets.QPushButton(
87
+ 'Add Camera', clicked=lambda: self.add_camera_clicked()
88
+ )
89
+
90
+ self.remove_cam = QtWidgets.QPushButton(
91
+ 'Remove Camera', clicked=lambda: self.remove_camera_clicked()
92
+ )
93
+
94
+ self.refresh = QtWidgets.QPushButton(
95
+ 'Refresh List', clicked=lambda: self.refresh_list()
96
+ )
97
+
98
+ self.HL_buttons.addWidget(self.add_cam)
99
+ self.HL_buttons.addWidget(self.remove_cam)
100
+ self.HL_buttons.addWidget(self.refresh)
101
+
102
+ self.mainLayout.addWidget(self.cam_table)
103
+ self.mainLayout.addLayout(self.HL_buttons)
104
+
105
+ self.setLayout(self.mainLayout)
106
+
107
+ self.refresh_list()
108
+
109
+ @property
110
+ def autofocusCam(self) -> typing.Union[Camera_Panel, None]:
111
+ '''
112
+ Get the autofocus camera panel.
113
+
114
+ Returns
115
+ -------
116
+ Camera_Panel | None
117
+ The autofocus camera panel, or None if no autofocus camera is available.
118
+ '''
119
+ if self.cached_autofocusCam is None:
120
+ self.cached_autofocusCam = next(
121
+ (
122
+ cam['Panel']
123
+ for _, cam_list in CameraList.CAMERAS.items()
124
+ for cam in cam_list
125
+ if cam['IR']
126
+ ),
127
+ None,
128
+ )
129
+ return self.cached_autofocusCam
130
+
131
+ def add_camera_clicked(self):
132
+ '''
133
+ Add a camera when the "Add Camera" button is clicked.
134
+ '''
135
+ if len(self.cam_table.selectedIndexes()) > 0:
136
+ cam = self.cam_list[self.cam_table.currentIndex().row()]
137
+
138
+ # create a dialog with radio buttons
139
+ dialog, ok = QtWidgets.QInputDialog.getItem(
140
+ self,
141
+ 'Add Camera',
142
+ 'Choose camera type:',
143
+ ('Acquisition', 'Autofocus IR'),
144
+ )
145
+
146
+ if ok and dialog is not None:
147
+ if dialog == 'Acquisition':
148
+ panel = self.add_camera(cam)
149
+ if panel:
150
+ self.cameraAdded.emit(panel, False)
151
+ elif dialog == 'Autofocus IR':
152
+ if self.autofocusCam is None:
153
+ panel = self.add_camera(cam, True)
154
+ if panel:
155
+ self.cameraAdded.emit(panel, True)
156
+ else:
157
+ self._display_warning_message(
158
+ 'Autofocus IR camera has already been added!'
159
+ )
160
+
161
+ self.refresh_list()
162
+ else:
163
+ self._display_warning_message('Please select a device.')
164
+
165
+ def add_camera(self, cam, mini=False):
166
+ '''
167
+ Add a camera.
168
+
169
+ Parameters
170
+ ----------
171
+ cam : dict
172
+ The camera information dictionary.
173
+ mini : bool, optional
174
+ True to add a mini camera panel, False to add a full camera panel.
175
+
176
+ Returns
177
+ -------
178
+ Camera_Panel | None
179
+ The camera panel, or None if the camera could not be added.
180
+ '''
181
+ if cam['InUse'] == 0:
182
+ driver = cam['Driver']
183
+ if driver == 'uEye':
184
+ return self._add_ids_camera(cam, cam['Camera ID'], mini)
185
+ elif driver == 'UC480':
186
+ return self._add_thorlabs_camera(cam, cam['Camera ID'], mini)
187
+ elif driver == 'Vimba':
188
+ return self._add_vimba_camera(cam, cam['Camera ID'], mini)
189
+ elif driver == 'miDummy':
190
+ return self._add_dummy_camera(cam, mini)
191
+ elif driver == 'PycroCore':
192
+ return self._add_pycro_camera(cam, cam['Device ID'], mini)
193
+ else:
194
+ self._display_warning_message(f'Unsupported camera driver: {driver}')
195
+ else:
196
+ self._display_warning_message('Device is in use or already added.')
197
+
198
+ def _add_ids_camera(self, cam, camera_id, mini):
199
+ '''
200
+ Add an IDS camera.
201
+
202
+ Parameters
203
+ ----------
204
+ cam : dict
205
+ The camera information dictionary.
206
+ camera_id : int
207
+ The camera ID.
208
+ mini : bool
209
+ True to add a mini camera panel, False to add a full camera panel.
210
+
211
+ Returns
212
+ -------
213
+ Camera_Panel | None
214
+ The camera panel, or None if the camera could not be added.
215
+ '''
216
+ ids_cam = IDS_Camera(camera_id)
217
+ ids_cam.initialize()
218
+ ids_panel = IDS_Panel(ids_cam, mini, self.get_cam_title(cam))
219
+ CameraList.CAMERAS['uEye'].append(
220
+ {'Camera': ids_cam, 'Panel': ids_panel, 'IR': mini}
221
+ )
222
+ return ids_panel
223
+
224
+ def _add_thorlabs_camera(self, cam, camera_id, mini):
225
+ '''
226
+ Add a Thorlabs camera.
227
+
228
+ Parameters
229
+ ----------
230
+ cam : dict
231
+ The camera information dictionary.
232
+ camera_id : int
233
+ The camera ID.
234
+ mini : bool
235
+ True to add a mini camera panel, False to add a full camera panel.
236
+
237
+ Returns
238
+ -------
239
+ Camera_Panel | None
240
+ The camera panel, or None if the camera could not be added.
241
+ '''
242
+ thor_cam = thorlabs_camera(camera_id)
243
+ n_ret = thor_cam.initialize()
244
+ if n_ret == CMD.IS_SUCCESS:
245
+ thor_panel = Thorlabs_Panel(thor_cam, mini, self.get_cam_title(cam))
246
+ CameraList.CAMERAS['UC480'].append(
247
+ {'Camera': thor_cam, 'Panel': thor_panel, 'IR': mini}
248
+ )
249
+ return thor_panel
250
+ else:
251
+ self._display_warning_message('Thorlabs camera initialization failed.')
252
+
253
+ def _add_vimba_camera(self, cam, camera_id, mini):
254
+ '''
255
+ Add a Vimba camera.
256
+
257
+ Parameters
258
+ ----------
259
+ cam : dict
260
+ The camera information dictionary.
261
+ camera_id : int
262
+ The camera ID.
263
+ mini : bool
264
+ True to add a mini camera panel, False to add a full camera panel.
265
+
266
+ Returns
267
+ -------
268
+ Camera_Panel | None
269
+ The camera panel, or None if the camera could not be added.
270
+ '''
271
+ v_cam = vimba_cam(camera_id)
272
+ v_panel = Vimba_Panel(v_cam, mini, self.get_cam_title(cam))
273
+ CameraList.CAMERAS['Vimba'].append(
274
+ {'Camera': v_cam, 'Panel': v_panel, 'IR': mini}
275
+ )
276
+ return v_panel
277
+
278
+ def _add_dummy_camera(self, cam, mini):
279
+ '''
280
+ Add a dummy camera.
281
+
282
+ Parameters
283
+ ----------
284
+ cam : dict
285
+ The camera information dictionary.
286
+ mini : bool
287
+ True to add a mini camera panel, False to add a full camera panel.
288
+
289
+ Returns
290
+ -------
291
+ Camera_Panel | None
292
+ The camera panel, or None if the camera could not be added.
293
+ '''
294
+ dummy_panel = Dummy_Panel(mini, self.get_cam_title(cam))
295
+ CameraList.CAMERAS['miDummy'].append(
296
+ {'Camera': dummy_panel.cam, 'Panel': dummy_panel, 'IR': mini}
297
+ )
298
+ return dummy_panel
299
+
300
+ def _add_pycro_camera(self, cam, camera_id, mini):
301
+ '''
302
+ Add a PycroManager camera.
303
+
304
+ Parameters
305
+ ----------
306
+ cam : dict
307
+ The camera information dictionary.
308
+ camera_id : int
309
+ The camera ID / Label.
310
+ mini : bool
311
+ True to add a mini camera panel, False to add a full camera panel.
312
+
313
+ Returns
314
+ -------
315
+ Camera_Panel | None
316
+ The camera panel, or None if the camera could not be added.
317
+ '''
318
+ pycro_cam = PycroCamera(
319
+ cam['Camera ID'], port=int(cam['Device ID'].split('-')[0])
320
+ )
321
+
322
+ pycro_pan = PycroPanel(pycro_cam, mini, self.get_cam_title(cam))
323
+ CameraList.CAMERAS['PycroCore'].append(
324
+ {'Camera': pycro_cam, 'Panel': pycro_pan, 'IR': mini}
325
+ )
326
+ return pycro_pan
327
+
328
+ def _display_warning_message(self, message):
329
+ '''
330
+ Display a warning message.
331
+
332
+ Parameters
333
+ ----------
334
+ message : str
335
+ The warning message to display.
336
+ '''
337
+ QtWidgets.QMessageBox.warning(
338
+ self, 'Warning', message, QtWidgets.QMessageBox.StandardButton.Ok
339
+ )
340
+
341
+ def get_cam_title(self, cam: dict):
342
+ '''
343
+ Get the camera title.
344
+
345
+ Parameters
346
+ ----------
347
+ cam : dict
348
+ The camera information dictionary.
349
+
350
+ Returns
351
+ -------
352
+ str
353
+ The camera title.
354
+ '''
355
+ return f"{cam['Driver']} | {cam['Model']}"
356
+
357
+ def remove_camera_clicked(self):
358
+ '''
359
+ Remove a camera when the "Remove Camera" button is clicked.
360
+ '''
361
+ if len(self.cam_table.selectedIndexes()) > 0:
362
+ cam = self.cam_list[self.cam_table.currentIndex().row()]
363
+
364
+ # Display a confirmation dialog
365
+ confirm = QtWidgets.QMessageBox.question(
366
+ self,
367
+ 'Confirmation',
368
+ f'Do you want to remove this camera {self.get_cam_title(cam)}?',
369
+ QtWidgets.QMessageBox.StandardButton.Yes
370
+ | QtWidgets.QMessageBox.StandardButton.No,
371
+ QtWidgets.QMessageBox.StandardButton.No,
372
+ )
373
+
374
+ if confirm == QtWidgets.QMessageBox.StandardButton.Yes:
375
+ self.remove_camera(cam)
376
+
377
+ self.refresh_list()
378
+ else:
379
+ self._display_warning_message('Please select a device.')
380
+
381
+ def remove_camera(self, cam):
382
+ '''
383
+ Remove a camera.
384
+
385
+ Parameters
386
+ ----------
387
+ cam : dict
388
+ The camera information dictionary.
389
+ '''
390
+ cams: list[dict] = CameraList.CAMERAS.get(cam['Driver'], [])
391
+ if cams:
392
+ for item in cams:
393
+ pan: Camera_Panel = item['Panel']
394
+ if pan.title() == self.get_cam_title(cam):
395
+ if pan.cam.acquisition:
396
+ self._display_warning_message(
397
+ 'Please stop acquisition before removing!'
398
+ )
399
+ else:
400
+ if isinstance(pan.cam, (IDS_Camera, thorlabs_camera)):
401
+ pan.cam.free_memory()
402
+ pan.cam.dispose()
403
+ if isinstance(item['Camera'], miDummy):
404
+ miDummy.instances.remove(item['Camera'])
405
+
406
+ pan._dispose_cam = True
407
+ if pan.acq_job is not None:
408
+ pan.acq_job.stop_threads = True
409
+
410
+ cams.remove(item)
411
+ if item['IR']:
412
+ pan.setParent(None)
413
+ else:
414
+ pan.close()
415
+ pan.setParent(None)
416
+ self.cameraRemoved.emit(pan, item['IR'])
417
+ return
418
+ else:
419
+ self._display_warning_message('Device/Panel not found!')
420
+
421
+ def removeAllCameras(self):
422
+ '''
423
+ Remove all cameras.
424
+ Stops any active acquisitions and properly disposes of camera resources.
425
+ '''
426
+ for _, camera_list in self.CAMERAS.items():
427
+ # Create a copy of the list since we'll be modifying it during iteration
428
+ for camera_info in camera_list[:]:
429
+ panel: Camera_Panel = camera_info['Panel']
430
+
431
+ # Stop acquisition if running
432
+ if panel.cam.acquisition:
433
+ panel.stop()
434
+
435
+ # Clean up based on camera type
436
+ if isinstance(panel.cam, (IDS_Camera, thorlabs_camera)):
437
+ panel.cam.free_memory()
438
+ panel.cam.dispose()
439
+
440
+ # Remove dummy camera instances
441
+ if isinstance(camera_info['Camera'], miDummy):
442
+ miDummy.instances.remove(camera_info['Camera'])
443
+
444
+ # Stop any acquisition jobs
445
+ if panel.acq_job is not None:
446
+ panel.acq_job.stop_threads = True
447
+
448
+ # Remove from parent and clean up panel
449
+ panel._dispose_cam = True
450
+ if camera_info['IR']:
451
+ panel.setParent(None)
452
+ else:
453
+ panel.close()
454
+ panel.setParent(None)
455
+
456
+ # Remove from camera list
457
+ camera_list.remove(camera_info)
458
+
459
+ def refresh_list(self):
460
+ '''
461
+ Refresh the camera list.
462
+ '''
463
+ self.cam_list = []
464
+
465
+ # Add miDummy to the list
466
+ self.cam_list += miDummy.get_camera_list()
467
+
468
+ if ueye is not None:
469
+ self.cam_list += IDS_Camera.get_camera_list()
470
+
471
+ if os.path.exists(thorlabs_camera.uc480_file):
472
+ self.cam_list += thorlabs_camera.get_camera_list()
473
+
474
+ if vb is not None:
475
+ self.cam_list += get_camera_list()
476
+
477
+ if hasattr(PycroCore, 'get_camera_list'):
478
+ self.cam_list += PycroCore.get_camera_list()
479
+
480
+ if self.cam_list is None:
481
+ self.cam_list = []
482
+ print('No cameras connected.')
483
+
484
+ self.item_model = QtGui.QStandardItemModel(len(self.cam_list), 8)
485
+
486
+ self.item_model.setHorizontalHeaderLabels(
487
+ [
488
+ 'In Use',
489
+ 'Camera ID',
490
+ 'Device ID',
491
+ 'Model',
492
+ 'Serial',
493
+ 'Status',
494
+ 'Sensor ID',
495
+ 'Driver',
496
+ ]
497
+ )
498
+
499
+ for i in range(len(self.cam_list)):
500
+ self.item_model.setItem(
501
+ i, 0, QtGui.QStandardItem(str(self.cam_list[i]['InUse']))
502
+ )
503
+ self.item_model.setItem(
504
+ i, 1, QtGui.QStandardItem(str(self.cam_list[i]['Camera ID']))
505
+ )
506
+ self.item_model.setItem(
507
+ i, 2, QtGui.QStandardItem(str(self.cam_list[i]['Device ID']))
508
+ )
509
+ self.item_model.setItem(
510
+ i, 3, QtGui.QStandardItem(self.cam_list[i]['Model'])
511
+ )
512
+ self.item_model.setItem(
513
+ i, 4, QtGui.QStandardItem(self.cam_list[i]['Serial'])
514
+ )
515
+ self.item_model.setItem(
516
+ i, 5, QtGui.QStandardItem(str(self.cam_list[i]['Status']))
517
+ )
518
+ self.item_model.setItem(
519
+ i, 6, QtGui.QStandardItem(str(self.cam_list[i]['Sensor ID']))
520
+ )
521
+ self.item_model.setItem(
522
+ i, 7, QtGui.QStandardItem(str(self.cam_list[i]['Driver']))
523
+ )
524
+
525
+ self.cam_table.setModel(self.item_model)
526
+
527
+ # Update the cached_autofocusCam value
528
+ self.cached_autofocusCam = None