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,258 @@
1
+ import os
2
+ import sys
3
+ from typing import Any, Optional
4
+
5
+ from microEye.hardware.pycromanager.headless import HeadlessInstance, HeadlessManager
6
+ from microEye.hardware.pycromanager.widgets.headless_options import (
7
+ HeadlessOptions,
8
+ headlessParams,
9
+ )
10
+ from microEye.qt import QAction, QApplication, Qt, QtCore, QtWidgets, Signal
11
+
12
+
13
+ class HeadlessManagerWidget(QtWidgets.QDialog):
14
+ '''Widget to display and manage running headless instances.'''
15
+
16
+ instance_started = Signal(int) # Signal emitted when new instance is started
17
+ instance_stopped = Signal(int) # Signal emitted when instance is stopped
18
+
19
+ def __init__(
20
+ self,
21
+ parent: Optional[QtWidgets.QWidget] = None,
22
+ ):
23
+ super().__init__(parent=parent)
24
+ self.options_dialog = None
25
+
26
+ self._setup_ui()
27
+ self._connect_signals()
28
+ self._refresh_instances()
29
+
30
+ self.setWindowTitle('Micro-Manager Headless Manager')
31
+ self.setMinimumWidth(700)
32
+
33
+ def _setup_ui(self):
34
+ '''Set up the UI components.'''
35
+ layout = QtWidgets.QVBoxLayout()
36
+
37
+ # Instance list
38
+ self.instance_list = QtWidgets.QTableWidget()
39
+ self.instance_list.setColumnCount(4)
40
+ self.instance_list.setHorizontalHeaderLabels(
41
+ ['Name', 'Port', 'Config', 'Actions']
42
+ )
43
+ self.instance_list.horizontalHeader().setStretchLastSection(True)
44
+ self.instance_list.horizontalHeader().setSectionResizeMode(
45
+ QtWidgets.QHeaderView.ResizeMode.ResizeToContents
46
+ )
47
+ self.instance_list.setSelectionBehavior(
48
+ QtWidgets.QTableWidget.SelectionBehavior.SelectRows
49
+ )
50
+ layout.addWidget(self.instance_list)
51
+
52
+ # Buttons
53
+ button_layout = QtWidgets.QHBoxLayout()
54
+
55
+ self.add_button = QtWidgets.QPushButton('Add Instance')
56
+ self.refresh_button = QtWidgets.QPushButton('Refresh')
57
+ self.stop_all_button = QtWidgets.QPushButton('Stop All')
58
+
59
+ button_layout.addWidget(self.add_button)
60
+ button_layout.addWidget(self.refresh_button)
61
+ button_layout.addWidget(self.stop_all_button)
62
+
63
+ layout.addLayout(button_layout)
64
+ self.setLayout(layout)
65
+
66
+ def _connect_signals(self):
67
+ '''Connect signals to slots.'''
68
+ self.add_button.clicked.connect(self._show_options_dialog)
69
+ self.refresh_button.clicked.connect(self._refresh_instances)
70
+ self.stop_all_button.clicked.connect(self._stop_all_instances)
71
+
72
+ def _refresh_instances(self):
73
+ '''Refresh the list of running instances.'''
74
+ self.instance_list.setRowCount(0)
75
+
76
+ instances = HeadlessManager().instances
77
+ self.instance_list.setRowCount(len(instances))
78
+
79
+ for idx, (port, instance) in enumerate(instances.items()):
80
+ # Name
81
+ name_item = QtWidgets.QTableWidgetItem(instance.name)
82
+ self.instance_list.setItem(idx, 0, name_item)
83
+
84
+ # Port
85
+ port_item = QtWidgets.QTableWidgetItem(str(instance.port))
86
+ self.instance_list.setItem(idx, 1, port_item)
87
+
88
+ # Config file
89
+ config_item = QtWidgets.QTableWidgetItem(
90
+ os.path.basename(instance.config_file)
91
+ if instance.config_file
92
+ else 'None'
93
+ )
94
+ self.instance_list.setItem(idx, 2, config_item)
95
+
96
+ # Action buttons
97
+ action_widget = QtWidgets.QWidget()
98
+ action_layout = QtWidgets.QHBoxLayout(action_widget)
99
+ action_layout.setContentsMargins(0, 0, 0, 0)
100
+
101
+ edit_button = QtWidgets.QPushButton('Details')
102
+ stop_button = QtWidgets.QPushButton('Stop')
103
+
104
+ edit_button.clicked.connect(
105
+ lambda checked, p=port: self._show_instance_details(p)
106
+ )
107
+ stop_button.clicked.connect(lambda checked, p=port: self._stop_instance(p))
108
+
109
+ action_layout.addWidget(edit_button)
110
+ action_layout.addWidget(stop_button)
111
+
112
+ self.instance_list.setCellWidget(idx, 3, action_widget)
113
+
114
+ self.instance_list.resizeRowsToContents()
115
+
116
+ def _show_options_dialog(self):
117
+ '''Show dialog to add a new instance.'''
118
+ if not self.options_dialog:
119
+ self.options_dialog = QtWidgets.QDialog(self)
120
+ self.options_dialog.setWindowFlags(
121
+ self.options_dialog.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint
122
+ )
123
+ self.options_dialog.setWindowTitle('Add Headless Instance')
124
+
125
+ self.options_dialog.setLayout(QtWidgets.QVBoxLayout())
126
+
127
+ self.options = HeadlessOptions()
128
+ self.options_dialog.layout().addWidget(self.options)
129
+
130
+ # Add buttons to accept/reject
131
+ button_box = QtWidgets.QDialogButtonBox(
132
+ QtWidgets.QDialogButtonBox.StandardButton.Ok
133
+ | QtWidgets.QDialogButtonBox.StandardButton.Cancel
134
+ )
135
+
136
+ button_box.accepted.connect(self._add_instance)
137
+ button_box.rejected.connect(self.options_dialog.close)
138
+
139
+ self.options_dialog.layout().addWidget(button_box)
140
+
141
+ self.options_dialog.exec()
142
+
143
+ def _add_instance(self):
144
+ '''Add a new instance from the options dialog.'''
145
+ getValues = self.options.get_param_value
146
+
147
+ # Create instance config from params
148
+ instance_config = HeadlessInstance(
149
+ port=getValues(headlessParams.PORT.label()),
150
+ mm_app_path=getValues(headlessParams.MM_APP_PATH.label()),
151
+ config_file=getValues(headlessParams.CONFIG_FILE.label()),
152
+ java_loc=getValues(headlessParams.JAVA_LOC.label()),
153
+ python_backend=getValues(headlessParams.PYTHON_BACKEND.label()),
154
+ core_log_path=getValues(headlessParams.CORE_LOG_PATH.label()),
155
+ buffer_size_mb=getValues(headlessParams.BUFFER_SIZE_MB.label()),
156
+ max_memory_mb=getValues(headlessParams.MAX_MEMORY_MB.label()),
157
+ debug=getValues(headlessParams.DEBUG.label()),
158
+ )
159
+
160
+ # Try to start the instance
161
+ if HeadlessManager().start_instance(instance_config):
162
+ self._refresh_instances()
163
+ self.instance_started.emit(instance_config.port)
164
+ self.options_dialog.close()
165
+ else:
166
+ QtWidgets.QMessageBox.warning(
167
+ self,
168
+ 'Failed to Start',
169
+ f'Failed to start instance on port {instance_config.port}.'
170
+ + ' Port may be in use.',
171
+ )
172
+
173
+ def _stop_instance(self, port: int):
174
+ '''Stop a specific instance.'''
175
+ if HeadlessManager().stop_instance(port):
176
+ self._refresh_instances()
177
+ self.instance_stopped.emit(port)
178
+ else:
179
+ QtWidgets.QMessageBox.warning(
180
+ self, 'Failed to Stop', f'Failed to stop instance on port {port}.'
181
+ )
182
+
183
+ def _stop_all_instances(self):
184
+ '''Stop all running instances.'''
185
+ confirm = QtWidgets.QMessageBox.question(
186
+ self,
187
+ 'Confirm Stop All',
188
+ 'Are you sure you want to stop all running headless instances?',
189
+ QtWidgets.QMessageBox.StandardButton.Yes
190
+ | QtWidgets.QMessageBox.StandardButton.No,
191
+ )
192
+
193
+ if confirm == QtWidgets.QMessageBox.StandardButton.Yes:
194
+ HeadlessManager().stop_all_instances()
195
+ self._refresh_instances()
196
+
197
+ def _show_instance_details(self, port: int):
198
+ '''Show details for a specific instance.'''
199
+ instance = HeadlessManager().get_instance(port)
200
+ if not instance:
201
+ return
202
+
203
+ details = QtWidgets.QDialog(self)
204
+ details.setWindowTitle(f'Instance Details: {instance.name}')
205
+
206
+ layout = QtWidgets.QVBoxLayout()
207
+
208
+ # Create a formatted text display
209
+ text = QtWidgets.QTextEdit()
210
+ text.setReadOnly(True)
211
+
212
+ detail_text = f'''<h3>Headless Instance Details</h3>
213
+ <p><b>Name:</b> {instance.name}</p>
214
+ <p><b>Port:</b> {instance.port}</p>
215
+ <p><b>Micro-Manager Path:</b> {instance.mm_app_path}</p>
216
+ <p><b>Config File:</b> {instance.config_file or "None"}</p>
217
+ <p><b>Java Location:</b> {instance.java_loc or "Default"}</p>
218
+ <p><b>Python Backend:</b> {"Yes" if instance.python_backend else "No"}</p>
219
+ <p><b>Buffer Size:</b> {instance.buffer_size_mb} MB</p>
220
+ <p><b>Max Memory:</b> {instance.max_memory_mb} MB</p>
221
+ <p><b>Debug Mode:</b> {"Enabled" if instance.debug else "Disabled"}</p>
222
+ '''
223
+
224
+ text.setHtml(detail_text)
225
+ layout.addWidget(text)
226
+
227
+ # Add a close button
228
+ close_button = QtWidgets.QPushButton('Close')
229
+ close_button.clicked.connect(details.close)
230
+ layout.addWidget(close_button)
231
+
232
+ details.setLayout(layout)
233
+ details.resize(500, 400)
234
+ details.exec()
235
+
236
+ @classmethod
237
+ def show_dialog(cls, parent: Optional[QtWidgets.QWidget] = None) -> None:
238
+ '''Show the headless manager widget.'''
239
+ if not hasattr(cls, 'singleton'):
240
+ cls.singleton = HeadlessManagerWidget(parent=parent)
241
+ cls.singleton.exec()
242
+
243
+ @classmethod
244
+ def get_menu_action(
245
+ cls, parent: Optional[QtWidgets.QWidget] = None
246
+ ) -> QAction:
247
+ '''Get the action to show this widget in a menu.'''
248
+ action = QAction('Micro-Manager Headless Manager', parent=parent)
249
+ action.triggered.connect(lambda: cls.show_dialog(parent=parent))
250
+ action.setStatusTip('Show the Micro-Manager Headless Manager')
251
+ return action
252
+
253
+
254
+ if __name__ == '__main__':
255
+ app = QApplication(sys.argv)
256
+ window = HeadlessManagerWidget()
257
+ window.show()
258
+ sys.exit(app.exec())
@@ -0,0 +1,224 @@
1
+ import os
2
+ import sys
3
+ from enum import Enum
4
+ from typing import Any, Optional
5
+
6
+ from mmpycorex.launcher import find_existing_mm_install
7
+ from pyqtgraph.parametertree import Parameter
8
+ from pyqtgraph.parametertree.parameterTypes import GroupParameter
9
+
10
+ from microEye.qt import QApplication, QtWidgets, Signal
11
+ from microEye.utils.parameter_tree import Tree
12
+
13
+
14
+ class headlessParams(Enum):
15
+ '''
16
+ Enum class defining headless MM parameters.
17
+ '''
18
+
19
+ MM_APP_PATH = 'Micro-Manager Path'
20
+ CONFIG_FILE = 'Config File'
21
+ JAVA_LOC = 'Java Location'
22
+ '''
23
+ On windows plaforms, the Java Runtime Environment will be grabbed automatically
24
+ as it is installed along with the Micro-Manager application.
25
+
26
+ On non-windows platforms, it may need to be installed/specified manually in order
27
+ to ensure compatibility.
28
+ Installing Java 11 is the most likely version to work without issue
29
+ '''
30
+ PYTHON_BACKEND = 'Python Backend'
31
+ CORE_LOG_PATH = 'Core Log Path'
32
+ BUFFER_SIZE_MB = 'Buffer Size MB'
33
+ MAX_MEMORY_MB = 'Max Memory MB'
34
+ PORT = 'Port'
35
+ DEBUG = 'Debug'
36
+
37
+ def __str__(self):
38
+ '''
39
+ Return the last part of the enum value (Param name).
40
+ '''
41
+ return self.value.split('.')[-1]
42
+
43
+ def get_path(self):
44
+ '''
45
+ Return the full parameter path.
46
+ '''
47
+ return self.value.split('.')
48
+
49
+ def label(self):
50
+ '''
51
+ Return the label of the parameter.
52
+ '''
53
+ return self.value.split('.')[-1]
54
+
55
+
56
+ def get_mm_path() -> str:
57
+ '''
58
+ Get the path to the Micro-Manager application.
59
+
60
+ Returns
61
+ -------
62
+ str
63
+ Path to the Micro-Manager application.
64
+ '''
65
+ return find_existing_mm_install()
66
+
67
+
68
+ class HeadlessOptions(Tree):
69
+ def __init__(self, parent: Optional['QtWidgets.QWidget'] = None):
70
+ '''
71
+ Initialize the CameraOptions.
72
+
73
+ Parameters
74
+ ----------
75
+ parent : QWidget, optional
76
+ The parent widget, by default None.
77
+ '''
78
+ super().__init__(parent=parent)
79
+
80
+ self.setMinimumWidth(700)
81
+
82
+ def create_parameters(self):
83
+ '''
84
+ Create the parameter tree structure.
85
+ '''
86
+ params = [
87
+ {
88
+ 'name': headlessParams.PORT.label(),
89
+ 'type': 'int',
90
+ 'value': 4827,
91
+ 'limits': [0, 9999],
92
+ 'tip': 'Default port to use for ZMQServer.',
93
+ },
94
+ {
95
+ 'name': headlessParams.DEBUG.label(),
96
+ 'type': 'bool',
97
+ 'value': False,
98
+ 'tip': 'Print debug messages.',
99
+ },
100
+ {
101
+ 'name': headlessParams.MM_APP_PATH.label(),
102
+ 'type': 'file',
103
+ 'directory': get_mm_path(),
104
+ 'fileMode': 'Directory',
105
+ 'value': get_mm_path(),
106
+ 'tip': 'Path to top level folder of Micro-Manager installation.',
107
+ },
108
+ {
109
+ 'name': headlessParams.CONFIG_FILE.label(),
110
+ 'type': 'list',
111
+ 'limits': [None],
112
+ 'value': None,
113
+ 'tip': 'Micro-manager config file with which core will be initialized.',
114
+ },
115
+ {
116
+ 'name': headlessParams.CORE_LOG_PATH.label(),
117
+ 'type': 'file',
118
+ 'directory': get_mm_path(),
119
+ 'fileMode': 'Directory',
120
+ 'value': '',
121
+ 'tip': 'Path to where core log files should be created.',
122
+ },
123
+ {
124
+ 'name': headlessParams.JAVA_LOC.label(),
125
+ 'type': 'file',
126
+ 'directory': get_mm_path(),
127
+ 'fileMode': 'Directory',
128
+ 'value': None,
129
+ 'tip': 'Path to the java version that it should be run with.',
130
+ },
131
+ {
132
+ 'name': headlessParams.PYTHON_BACKEND.label(),
133
+ 'type': 'bool',
134
+ 'readonly': True,
135
+ 'enabled': False,
136
+ 'value': False,
137
+ 'visible': False,
138
+ 'tip': 'Whether to use the python backend or the Java backend.',
139
+ },
140
+ {
141
+ 'name': headlessParams.BUFFER_SIZE_MB.label(),
142
+ 'type': 'list',
143
+ 'value': 1024,
144
+ 'limits': [
145
+ 512,
146
+ 768,
147
+ 1024,
148
+ 1280,
149
+ 1536,
150
+ 1792,
151
+ 2048,
152
+ 2304,
153
+ 2560,
154
+ 2816,
155
+ 3072,
156
+ 3328,
157
+ 3584,
158
+ 3840,
159
+ 4096,
160
+ ],
161
+ 'tip': 'Size of circular buffer in MB in MMCore.',
162
+ },
163
+ {
164
+ 'name': headlessParams.MAX_MEMORY_MB.label(),
165
+ 'type': 'list',
166
+ 'value': 2048,
167
+ 'limits': [
168
+ 2048,
169
+ 2304,
170
+ 2560,
171
+ 2816,
172
+ 3072,
173
+ 3328,
174
+ 3584,
175
+ 3840,
176
+ 4096,
177
+ 5120,
178
+ 6144,
179
+ 7168,
180
+ 8192,
181
+ 9216,
182
+ ],
183
+ 'tip': 'Maximum amount of memory to be allocated to JVM.',
184
+ },
185
+ ]
186
+
187
+ self.param_tree = Parameter.create(name='', type='group', children=params)
188
+ self.param_tree.sigTreeStateChanged.connect(self.change)
189
+ self.header().setSectionResizeMode(
190
+ QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
191
+ # self.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)
192
+
193
+ self.get_param(headlessParams.MM_APP_PATH).sigValueChanged.connect(
194
+ self.update_config_file
195
+ )
196
+ self.update_config_file(None, None)
197
+
198
+ def update_config_file(self, param: Parameter, value: Any):
199
+ '''
200
+ Update the config file list based on the selected MM path.
201
+
202
+ Parameters
203
+ ----------
204
+ param : Parameter
205
+ The parameter that triggered the change.
206
+ value : Any
207
+ The new value of the parameter.
208
+ '''
209
+ mm_path = self.get_param(headlessParams.MM_APP_PATH).value()
210
+ config_file_param = self.get_param(headlessParams.CONFIG_FILE)
211
+ if os.path.exists(mm_path):
212
+ config_files = [
213
+ os.path.basename(f)
214
+ for f in os.listdir(mm_path)
215
+ if f.endswith('.cfg') and f.startswith('MMConfig')
216
+ ]
217
+ config_file_param.setLimits([None, *config_files])
218
+
219
+
220
+ if __name__ == '__main__':
221
+ app = QApplication(sys.argv)
222
+ window = HeadlessOptions()
223
+ window.show()
224
+ sys.exit(app.exec())