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,244 @@
1
+ import json
2
+ import sys
3
+
4
+ from microEye import __version__
5
+ from microEye.hardware.protocols.actions import (
6
+ ForLoop,
7
+ FunctionCall,
8
+ ParameterAdjustmentAction,
9
+ )
10
+ from microEye.hardware.protocols.scene_manager import SceneManager
11
+ from microEye.qt import QApplication, Qt, QtCore, QtGui, QtWidgets
12
+
13
+
14
+ class ExperimentDesignerView(QtWidgets.QGraphicsView):
15
+ '''
16
+ Custom QGraphicsView with zooming and key event handling.
17
+
18
+ Subclass of QGraphicsView.
19
+
20
+ Attributes
21
+ ----------
22
+ root : ActionGroupItem
23
+ The root item in the scene.
24
+ '''
25
+
26
+ def __init__(self):
27
+ '''
28
+ Initialize a new ExperimentDesignerView instance.
29
+ '''
30
+ super().__init__()
31
+ self.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
32
+ self.setRenderHint(QtGui.QPainter.RenderHint.TextAntialiasing, True)
33
+ self.setRenderHint(QtGui.QPainter.RenderHint.SmoothPixmapTransform, True)
34
+
35
+ # Create an instance of the SceneManager
36
+ self.scene_manager = SceneManager()
37
+ self.setScene(self.scene_manager.scene)
38
+
39
+ # Set dark background color for the view
40
+ background_color = QtGui.QColor('#333333') # Adjust the color as needed
41
+ self.setBackgroundBrush(background_color)
42
+
43
+ def wheelEvent(self, event: QtWidgets.QGraphicsSceneWheelEvent):
44
+ '''
45
+ Handle wheel events for zooming.
46
+
47
+ Parameters
48
+ ----------
49
+ event : QGraphicsSceneWheelEvent
50
+ The wheel event.
51
+ '''
52
+ zoom_factor = 1.2
53
+
54
+ if event.angleDelta().y() > 0:
55
+ self.scale(zoom_factor, zoom_factor)
56
+ else:
57
+ self.scale(1 / zoom_factor, 1 / zoom_factor)
58
+
59
+ super().wheelEvent(event)
60
+
61
+ def keyPressEvent(self, event):
62
+ '''
63
+ Handle key press events for swapping and deletion.
64
+
65
+ Parameters
66
+ ----------
67
+ event : QKeyEvent
68
+ The key event.
69
+ '''
70
+ self.scene_manager.handleKeyPress(event)
71
+
72
+ super().keyPressEvent(event)
73
+
74
+ def contextMenuEvent(self, event):
75
+ '''
76
+ Handle context menu events.
77
+
78
+ Parameters
79
+ ----------
80
+ event : QContextMenuEvent
81
+ The context menu event.
82
+ '''
83
+ context_menu = QtWidgets.QMenu()
84
+
85
+ # Add option to add different types
86
+ add_menu = context_menu.addMenu('Add')
87
+ # add_menu.addAction(
88
+ # 'Function Call', lambda: self.scene_manager.add_action(FunctionCall())
89
+ # )
90
+ add_menu.addAction('For Loop', lambda: self.scene_manager.add_action(ForLoop()))
91
+ add_menu.addAction(
92
+ 'Parameter Adjustment',
93
+ lambda: self.scene_manager.add_action(ParameterAdjustmentAction()),
94
+ )
95
+
96
+ # Add Move submenu
97
+ move_submenu = context_menu.addMenu('Move')
98
+ move_submenu.addAction(
99
+ 'Up',
100
+ lambda: self.keyPressEvent(
101
+ QtGui.QKeyEvent(
102
+ QtCore.QEvent.Type.KeyPress,
103
+ Qt.Key.Key_PageUp,
104
+ Qt.KeyboardModifier.NoModifier,
105
+ 0,
106
+ 0,
107
+ 0,
108
+ )
109
+ ),
110
+ )
111
+ move_submenu.addAction(
112
+ 'Down',
113
+ lambda: self.keyPressEvent(
114
+ QtGui.QKeyEvent(
115
+ QtCore.QEvent.Type.KeyPress,
116
+ Qt.Key.Key_PageDown,
117
+ Qt.KeyboardModifier.NoModifier,
118
+ 0,
119
+ 0,
120
+ 0,
121
+ )
122
+ ),
123
+ )
124
+
125
+ context_menu.addAction(
126
+ 'Delete',
127
+ lambda: self.keyPressEvent(
128
+ QtGui.QKeyEvent(
129
+ QtCore.QEvent.Type.KeyPress,
130
+ Qt.Key.Key_Delete,
131
+ Qt.KeyboardModifier.NoModifier,
132
+ 0,
133
+ 0,
134
+ 0,
135
+ )
136
+ ),
137
+ )
138
+
139
+ # Add Execute action
140
+ context_menu.addAction('Execute', self.scene_manager.execute_actions)
141
+ context_menu.addAction('Stop Execution', self.scene_manager.stop_execution)
142
+
143
+ # Add Export and Import actions
144
+ context_menu.addAction('Export (protocol)', self.export_protocol)
145
+ context_menu.addAction('Import (protocol)', self.import_protocol)
146
+ context_menu.addAction('Import & Execute', self.import_execute_protocol)
147
+
148
+ # Show the context menu
149
+ context_menu.exec(event.globalPos())
150
+
151
+ def export_protocol(self):
152
+ '''
153
+ Handle exporting the protocol to a JSON file.
154
+ '''
155
+ file_dialog = QtWidgets.QFileDialog()
156
+ file_name, _ = file_dialog.getSaveFileName(
157
+ self, 'Export Protocol', '', 'JSON Files (*.json)'
158
+ )
159
+
160
+ if file_name:
161
+ with open(file_name, 'w') as file:
162
+ protocol_data = self.scene_manager.serialize_protocol()
163
+ json.dump(protocol_data, file, indent=4)
164
+
165
+ def import_protocol(self):
166
+ '''
167
+ Handle importing the protocol from a JSON file.
168
+ '''
169
+ file_dialog = QtWidgets.QFileDialog()
170
+ file_name, _ = file_dialog.getOpenFileName(
171
+ self, 'Import Protocol', '', 'JSON Files (*.json)'
172
+ )
173
+
174
+ if not file_name:
175
+ return False
176
+
177
+ with open(file_name) as file:
178
+ protocol_data = json.load(file)
179
+ self.scene_manager.load_actions(protocol_data)
180
+
181
+ return True
182
+
183
+ def import_execute_protocol(self):
184
+ '''
185
+ Handle importing and executing the protocol from a JSON file.
186
+ '''
187
+ if self.import_protocol():
188
+ self.scene_manager.execute_actions()
189
+
190
+ class ExperimentDesigner(QtWidgets.QWidget):
191
+ HEADER = '> <span style="color:#0f0;">Experiment Designer ('
192
+ HEADER += f'<span style="color:#aaf;">microEye v{__version__}</span>)</span>'
193
+
194
+ def __init__(self):
195
+ super().__init__()
196
+ self.initUI()
197
+
198
+ def initUI(self):
199
+ # Create the ExperimentDesignerView
200
+ self.experiment_view = ExperimentDesignerView()
201
+
202
+ # Create the text terminal
203
+ self.text_terminal = QtWidgets.QPlainTextEdit()
204
+ self.text_terminal.setReadOnly(True)
205
+ self.text_terminal.appendHtml(self.HEADER)
206
+ self.text_terminal.setStyleSheet(
207
+ 'QPlainTextEdit { background-color: #111;}'
208
+ )
209
+
210
+ self.experiment_view.scene_manager.updateTerminal.connect(self.updateTerminal)
211
+ self.experiment_view.scene_manager.clearTerminal.connect(self.clearTerminal)
212
+
213
+ # Set up the layout
214
+ layout = QtWidgets.QHBoxLayout()
215
+ layout.addWidget(self.experiment_view, 2)
216
+ layout.addWidget(self.text_terminal, 1)
217
+ self.setLayout(layout)
218
+
219
+ # Set window properties
220
+ self.setWindowTitle('Experiment Designer')
221
+
222
+ def updateTerminal(self, text: str, level: int):
223
+ indent = '>' * (level + 1) + ' '
224
+ if ':' in text and len(text.split(':')) == 2:
225
+ text = text.split(':')
226
+ self.text_terminal.appendHtml(
227
+ f'{indent}<span style="color: #0f0;">{text[0]}: </span>'
228
+ f'<span style="color: #aaa;">{text[1]}</span>'
229
+ )
230
+ else:
231
+ self.text_terminal.appendHtml(
232
+ f'{indent}<span style="color: #0f0;">{text}</span>'
233
+ )
234
+
235
+ def clearTerminal(self):
236
+ self.text_terminal.clear()
237
+ self.text_terminal.appendHtml(self.HEADER)
238
+
239
+
240
+ if __name__ == '__main__':
241
+ app = QApplication(sys.argv)
242
+ editor = ExperimentDesigner()
243
+ editor.show()
244
+ sys.exit(app.exec())
@@ -0,0 +1,191 @@
1
+ import threading
2
+ import traceback
3
+ from typing import Any
4
+
5
+ from microEye.hardware.protocols.actions import ActionGroup, BaseAction
6
+ from microEye.hardware.protocols.actions_items import (
7
+ ActionGroupItem,
8
+ BaseActionItem,
9
+ get_action_item,
10
+ )
11
+ from microEye.hardware.protocols.serialization import (
12
+ deserialize_action,
13
+ serialize_action,
14
+ )
15
+ from microEye.qt import Qt, QtCore, QtGui, QtWidgets, Signal
16
+ from microEye.utils.thread_worker import QThreadWorker
17
+
18
+
19
+ class SceneManager(QtCore.QObject):
20
+ '''
21
+ Class responsible for managing the scene and its layout.
22
+ '''
23
+
24
+ updateTerminal = Signal(str, int)
25
+ clearTerminal = Signal()
26
+
27
+ def __init__(self):
28
+ '''
29
+ Initialize a new SceneManager instance.
30
+ '''
31
+ super().__init__()
32
+ self.scene = QtWidgets.QGraphicsScene()
33
+ self.action = ActionGroup()
34
+ self.action.name = 'Main'
35
+ self.root = ActionGroupItem(self.action)
36
+ self.scene.addItem(self.root)
37
+
38
+ self.worker = None
39
+
40
+ def add_action(self, action: BaseAction):
41
+ '''
42
+ Add an action to the scene.
43
+
44
+ Parameters
45
+ ----------
46
+ action : BaseAction
47
+ The action to be added.
48
+ '''
49
+ selected_items = self.scene.selectedItems()
50
+
51
+ if len(selected_items) == 1:
52
+ action_item = get_action_item(action)
53
+
54
+ if isinstance(selected_items[0], ActionGroupItem):
55
+ selected_item = selected_items[0]
56
+ selected_item.add_child_item(action_item)
57
+ else:
58
+ selected_item = selected_items[0]
59
+ parent_item = selected_item.parentItem()
60
+ if isinstance(parent_item, ActionGroupItem):
61
+ parent_item.add_child_item(action_item)
62
+
63
+ def handleKeyPress(self, event: QtGui.QKeyEvent):
64
+ selected_items = self.scene.selectedItems()
65
+
66
+ if len(selected_items) == 1 and isinstance(selected_items[0], BaseActionItem):
67
+ selected_item = selected_items[0]
68
+ parent_item = selected_item.parentItem()
69
+ if parent_item is not None and isinstance(parent_item, ActionGroupItem):
70
+ index_self = parent_item.child_items.index(selected_item)
71
+
72
+ if event.key() == Qt.Key.Key_PageUp:
73
+ # Move item up
74
+ if index_self > 0:
75
+ parent_item.swap_children(index_self, index_self - 1)
76
+ elif event.key() == Qt.Key.Key_PageDown:
77
+ # Move item down
78
+ if index_self < len(parent_item.child_items) - 1:
79
+ parent_item.swap_children(index_self, index_self + 1)
80
+ elif event.key() == Qt.Key.Key_Delete:
81
+ # Delete item and its children
82
+ parent_item.remove_child_item(selected_item)
83
+ self.scene.removeItem(selected_item)
84
+
85
+ def remove_action(self, action: BaseAction):
86
+ '''
87
+ Remove an action from the scene.
88
+
89
+ Parameters
90
+ ----------
91
+ action : BaseAction
92
+ The action to be removed.
93
+ '''
94
+ action_item = self.find_action_item(action)
95
+ if action_item:
96
+ parent_item = action_item.parentItem()
97
+ if parent_item:
98
+ parent_item.remove_child_item(action_item)
99
+ self.scene.removeItem(action_item)
100
+
101
+ def find_action_item(self, action: BaseAction) -> ActionGroupItem:
102
+ '''
103
+ Find the action item corresponding to the given action.
104
+
105
+ Parameters
106
+ ----------
107
+ action : BaseAction
108
+ The action to find.
109
+
110
+ Returns
111
+ -------
112
+ ActionGroupItem
113
+ The action item corresponding to the given action, or None if not found.
114
+ '''
115
+ for item in self.scene.items():
116
+ if isinstance(item, ActionGroupItem) and item.action == action:
117
+ return item
118
+ return None
119
+
120
+ def execute_actions(self):
121
+ '''
122
+ Execute the actions in the scene.
123
+ '''
124
+ if isinstance(self.root, ActionGroupItem) and (
125
+ self.worker is None or self.worker.is_set()
126
+ ):
127
+ self.clearTerminal.emit()
128
+
129
+ def actions(root: ActionGroupItem, updateTerminal, **kwargs):
130
+ try:
131
+ time = QtCore.QDateTime.currentDateTime().date()
132
+ root.action.execute(
133
+ output=updateTerminal, level=0, event=kwargs.get('event'),
134
+ year=time.year(), month=time.month(), day=time.day()
135
+ )
136
+ except Exception:
137
+ updateTerminal.emit(traceback.format_exc(), 0)
138
+ finally:
139
+ updateTerminal.emit('Done!', 0)
140
+
141
+ self.worker = QThreadWorker(
142
+ actions, self.root, self.updateTerminal
143
+ )
144
+ # Execute
145
+ QtCore.QThreadPool.globalInstance().start(self.worker)
146
+
147
+ def stop_execution(self):
148
+ if self.worker:
149
+ self.worker.stop() # This will signal the thread to stop
150
+
151
+ # self.updateTerminal.emit('Execution stopped.', 0)
152
+
153
+ def load_actions(self, actions: list[dict[str, Any]]):
154
+ '''
155
+ Load actions from a list of serialized action data.
156
+
157
+ Parameters
158
+ ----------
159
+ actions : list[dict[str, Any]]
160
+ A list of serialized action data.
161
+ '''
162
+ self.clear_scene()
163
+ for action_data in actions:
164
+ action, action_item = deserialize_action(action_data)
165
+ self.root.add_child_item(action_item)
166
+
167
+ def clear_scene(self):
168
+ '''
169
+ Clear the scene by removing all items.
170
+ '''
171
+ self.scene.clear()
172
+ BaseAction.id_counter = 1
173
+ self.action = ActionGroup()
174
+ self.action.name = 'Main'
175
+ self.root = ActionGroupItem(self.action)
176
+ self.scene.addItem(self.root)
177
+
178
+ def serialize_protocol(self) -> list[dict[str, Any]]:
179
+ '''
180
+ Serialize the protocol to a JSON-serializable data structure.
181
+
182
+ Returns
183
+ -------
184
+ list[dict[str, Any]]
185
+ A list of serialized action data.
186
+ '''
187
+ protocol_data = []
188
+ for action in self.root.action.child_actions:
189
+ action_data = serialize_action(action)
190
+ protocol_data.append(action_data)
191
+ return protocol_data
@@ -0,0 +1,97 @@
1
+ from typing import Any
2
+
3
+ from microEye.hardware.protocols.actions import (
4
+ BaseAction,
5
+ ForLoop,
6
+ FunctionCall,
7
+ ParameterAdjustmentAction,
8
+ )
9
+ from microEye.hardware.protocols.actions_items import (
10
+ ActionGroupItem,
11
+ get_action_item,
12
+ )
13
+
14
+
15
+ def serialize_action(action: BaseAction) -> dict[str, Any]:
16
+ '''
17
+ Serialize an action to a JSON-serializable data structure.
18
+
19
+ Parameters
20
+ ----------
21
+ action : BaseAction
22
+ The action to be serialized.
23
+
24
+ Returns
25
+ -------
26
+ dict[str, Any]
27
+ A dictionary representing the serialized action data.
28
+ '''
29
+ action_data = {
30
+ 'type': type(action).__name__,
31
+ 'id': action.id,
32
+ 'name': action.name,
33
+ }
34
+
35
+ if isinstance(action, FunctionCall):
36
+ action_data['function'] = action.function.__name__
37
+ action_data['function_args'] = list(action.function_args)
38
+ elif isinstance(action, ForLoop):
39
+ action_data['repeat_count'] = action.repeat_count
40
+ action_data['child_actions'] = [
41
+ serialize_action(child) for child in action.child_actions
42
+ ]
43
+ elif isinstance(action, ParameterAdjustmentAction):
44
+ action_data['target_object'] = action.target_object
45
+ action_data['parameter_name'] = action.parameter_name
46
+ action_data['parameter_value'] = action.parameter_value
47
+ action_data['delay'] = action.delay
48
+ action_data['is_expression'] = action.is_expression
49
+
50
+ return action_data
51
+
52
+
53
+ def deserialize_action(action_data: dict[str, Any]) -> tuple[BaseAction, Any]:
54
+ '''
55
+ Deserialize an action from the JSON data structure.
56
+
57
+ Parameters
58
+ ----------
59
+ action_data : dict[str, Any]
60
+ The dictionary containing the serialized action data.
61
+
62
+ Returns
63
+ -------
64
+ tuple[BaseAction, Any]
65
+ A tuple containing the deserialized action instance and its corresponding
66
+ action item.
67
+ '''
68
+ action_type = globals()[action_data['type']]
69
+
70
+ if action_type == FunctionCall:
71
+ function = globals()[action_data['function']]
72
+ args = tuple(action_data['function_args'])
73
+ action = FunctionCall(function, args)
74
+ action_item = get_action_item(action)
75
+ elif action_type == ForLoop:
76
+ action = ForLoop(action_data['repeat_count'])
77
+ action_item = ActionGroupItem(action)
78
+ for child_data in action_data['child_actions']:
79
+ child_action, child_item = deserialize_action(child_data)
80
+ action_item.add_child_item(child_item)
81
+ elif action_type == ParameterAdjustmentAction:
82
+ action = ParameterAdjustmentAction(
83
+ target_object=action_data.get('target_object'),
84
+ parameter_name=action_data.get('parameter_name'),
85
+ parameter_value=action_data.get('parameter_value'),
86
+ delay=action_data.get('delay', 0.0),
87
+ is_expression=action_data.get('is_expression', False)
88
+ )
89
+ action_item = get_action_item(action)
90
+ else:
91
+ action = action_type()
92
+ action_item = get_action_item(action)
93
+
94
+ # action.id = action_data['id']
95
+ action.name = action_data['name']
96
+
97
+ return action, action_item
@@ -0,0 +1,16 @@
1
+ # get pycromanager version
2
+ from mmpycorex import __version__ as mmpycorex_version
3
+ from pycromanager import __version__ as pycromanager_version
4
+
5
+ required_pycromanager_version = '1.0.2'
6
+ required_mmpycorex_version = '0.3.16'
7
+
8
+ if pycromanager_version != required_pycromanager_version:
9
+ raise ImportError(
10
+ f'This module requires pycromanager version {required_pycromanager_version}'
11
+ )
12
+
13
+ if mmpycorex_version < required_mmpycorex_version:
14
+ raise ImportError(
15
+ f'This module requires mmpycorex version {required_mmpycorex_version}'
16
+ )