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.
- microEye/__init__.py +47 -0
- microEye/_version.py +2 -0
- microEye/analysis/__init__.py +1 -0
- microEye/analysis/checklist_dialog.py +143 -0
- microEye/analysis/cmosMaps.py +228 -0
- microEye/analysis/filters/__init__.py +9 -0
- microEye/analysis/filters/base.py +21 -0
- microEye/analysis/filters/spatial.py +338 -0
- microEye/analysis/filters/temporal.py +76 -0
- microEye/analysis/fitting/__init__.py +0 -0
- microEye/analysis/fitting/fit.py +680 -0
- microEye/analysis/fitting/nena.py +375 -0
- microEye/analysis/fitting/phasor_fit.py +90 -0
- microEye/analysis/fitting/processing.py +317 -0
- microEye/analysis/fitting/psf/__init__.py +6 -0
- microEye/analysis/fitting/psf/extract.py +1129 -0
- microEye/analysis/fitting/psf/rubost_mean.py +150 -0
- microEye/analysis/fitting/psf/spline.py +167 -0
- microEye/analysis/fitting/psf/stats/__init__.py +12 -0
- microEye/analysis/fitting/psf/stats/core.py +295 -0
- microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
- microEye/analysis/fitting/psf/stats/io.py +104 -0
- microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
- microEye/analysis/fitting/psf/temp.py +147 -0
- microEye/analysis/fitting/psf/test.py +47 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
- microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
- microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
- microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
- microEye/analysis/fitting/results.py +917 -0
- microEye/analysis/fitting/results_stats.py +251 -0
- microEye/analysis/fitting/tardis.py +209 -0
- microEye/analysis/multi_viewer.py +568 -0
- microEye/analysis/processing/__init__.py +1 -0
- microEye/analysis/processing/frc.py +120 -0
- microEye/analysis/rendering/__init__.py +3 -0
- microEye/analysis/rendering/base.py +452 -0
- microEye/analysis/rendering/cloud.py +215 -0
- microEye/analysis/rendering/core.py +47 -0
- microEye/analysis/rendering/volumetric.py +198 -0
- microEye/analysis/tools/__init__.py +0 -0
- microEye/analysis/tools/kymograms.py +1079 -0
- microEye/analysis/tools/roi_selectors.py +556 -0
- microEye/analysis/utils/__init__.py +3 -0
- microEye/analysis/utils/coordinates.py +18 -0
- microEye/analysis/utils/images.py +68 -0
- microEye/analysis/utils/windows.py +19 -0
- microEye/analysis/viewer/__init__.py +3 -0
- microEye/analysis/viewer/image_options_widget.py +615 -0
- microEye/analysis/viewer/images.py +1225 -0
- microEye/analysis/viewer/layers_widget.py +415 -0
- microEye/analysis/viewer/localizations.py +1304 -0
- microEye/analysis/viewer/psf.py +1003 -0
- microEye/analysis/viewer/volume.py +456 -0
- microEye/hardware/__init__.py +3 -0
- microEye/hardware/cams/__init__.py +29 -0
- microEye/hardware/cams/camera_calibration.py +99 -0
- microEye/hardware/cams/camera_list.py +528 -0
- microEye/hardware/cams/camera_options.py +694 -0
- microEye/hardware/cams/camera_panel.py +941 -0
- microEye/hardware/cams/dummy/__init__.py +1 -0
- microEye/hardware/cams/dummy/dummy_panel.py +759 -0
- microEye/hardware/cams/jobs.py +497 -0
- microEye/hardware/cams/line_profiler.py +99 -0
- microEye/hardware/cams/linescan/IR_Cam.py +474 -0
- microEye/hardware/cams/linescan/__init__.py +1 -0
- microEye/hardware/cams/micam.py +524 -0
- microEye/hardware/cams/pco/__init__.py +58 -0
- microEye/hardware/cams/pco/enums.py +382 -0
- microEye/hardware/cams/pco/pco_cam.py +761 -0
- microEye/hardware/cams/pco/pco_panel.py +477 -0
- microEye/hardware/cams/shortcuts.py +312 -0
- microEye/hardware/cams/thorlabs/__init__.py +1 -0
- microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
- microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
- microEye/hardware/cams/ueye/__init__.py +1 -0
- microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
- microEye/hardware/cams/ueye/ueye_panel.py +861 -0
- microEye/hardware/cams/vimba/__init__.py +1 -0
- microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
- microEye/hardware/cams/vimba/vimba_panel.py +813 -0
- microEye/hardware/device.py +60 -0
- microEye/hardware/lasers/__init__.py +13 -0
- microEye/hardware/lasers/io_matchbox.py +791 -0
- microEye/hardware/lasers/io_params.py +85 -0
- microEye/hardware/lasers/io_single_laser.py +742 -0
- microEye/hardware/lasers/laser_relay.py +594 -0
- microEye/hardware/mieye/__init__.py +1 -0
- microEye/hardware/mieye/acquisition_manager.py +467 -0
- microEye/hardware/mieye/devices_manager.py +533 -0
- microEye/hardware/mieye/miEye.py +659 -0
- microEye/hardware/misc/__init__.py +0 -0
- microEye/hardware/misc/acquisition_view.py +71 -0
- microEye/hardware/misc/reglo.py +761 -0
- microEye/hardware/misc/temp.py +188 -0
- microEye/hardware/port_config.py +59 -0
- microEye/hardware/protocols/__init__.py +2 -0
- microEye/hardware/protocols/actions.py +402 -0
- microEye/hardware/protocols/actions_items.py +703 -0
- microEye/hardware/protocols/designer.py +244 -0
- microEye/hardware/protocols/scene_manager.py +191 -0
- microEye/hardware/protocols/serialization.py +97 -0
- microEye/hardware/pycromanager/__init__.py +16 -0
- microEye/hardware/pycromanager/core.py +1433 -0
- microEye/hardware/pycromanager/devices.py +461 -0
- microEye/hardware/pycromanager/enums.py +107 -0
- microEye/hardware/pycromanager/headless.py +153 -0
- microEye/hardware/pycromanager/utils.py +34 -0
- microEye/hardware/pycromanager/widgets/__init__.py +5 -0
- microEye/hardware/pycromanager/widgets/bridges.py +407 -0
- microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
- microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
- microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
- microEye/hardware/stages/__init__.py +18 -0
- microEye/hardware/stages/elliptec/__init__.py +5 -0
- microEye/hardware/stages/elliptec/baseDevice.py +314 -0
- microEye/hardware/stages/elliptec/device.py +384 -0
- microEye/hardware/stages/elliptec/deviceID.py +212 -0
- microEye/hardware/stages/elliptec/devicePort.py +379 -0
- microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
- microEye/hardware/stages/elliptec/devicesView.py +706 -0
- microEye/hardware/stages/elliptec/ellDevices.py +134 -0
- microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
- microEye/hardware/stages/elliptec/motorInfo.py +153 -0
- microEye/hardware/stages/elliptec/stage.py +62 -0
- microEye/hardware/stages/elliptec/test.py +139 -0
- microEye/hardware/stages/kinesis/__init__.py +1 -0
- microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
- microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
- microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
- microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
- microEye/hardware/stages/kinesis/kinesis.py +776 -0
- microEye/hardware/stages/piezo_concept.py +607 -0
- microEye/hardware/stages/stabilizer.py +785 -0
- microEye/hardware/stages/stage.py +89 -0
- microEye/hardware/widgets/__init__.py +10 -0
- microEye/hardware/widgets/controller.py +246 -0
- microEye/hardware/widgets/devices.py +133 -0
- microEye/hardware/widgets/focusWidget.py +264 -0
- microEye/hardware/widgets/qlist_slider.py +113 -0
- microEye/hardware/widgets/scan_acquisition.py +424 -0
- microEye/icons/1024.png +0 -0
- microEye/icons/128.png +0 -0
- microEye/icons/16.png +0 -0
- microEye/icons/24.png +0 -0
- microEye/icons/256.png +0 -0
- microEye/icons/32.png +0 -0
- microEye/icons/48.png +0 -0
- microEye/icons/512.png +0 -0
- microEye/icons/64.png +0 -0
- microEye/icons/__init__.py +0 -0
- microEye/icons/close.svg +88 -0
- microEye/icons/mieye.png +0 -0
- microEye/icons/min.svg +83 -0
- microEye/icons/viewer.png +0 -0
- microEye/launcher.py +42 -0
- microEye/qt.py +181 -0
- microEye/utils/__init__.py +2 -0
- microEye/utils/enum_encoder.py +10 -0
- microEye/utils/expandable_groupbox.py +93 -0
- microEye/utils/gui_helper.py +457 -0
- microEye/utils/hid/__init__.py +8 -0
- microEye/utils/hid/controller.py +153 -0
- microEye/utils/hid/device.py +63 -0
- microEye/utils/hid/enums.py +140 -0
- microEye/utils/hid/utils.py +58 -0
- microEye/utils/labelled_slider.py +134 -0
- microEye/utils/metadata.py +599 -0
- microEye/utils/metadata_tree.py +718 -0
- microEye/utils/micro_launcher.py +245 -0
- microEye/utils/parameter_tree.py +325 -0
- microEye/utils/pyscripting.py +444 -0
- microEye/utils/retry_exec.py +35 -0
- microEye/utils/start_gui.py +112 -0
- microEye/utils/thread_worker.py +115 -0
- microEye/utils/uImage.py +1512 -0
- microeye-2.3.2.dist-info/METADATA +401 -0
- microeye-2.3.2.dist-info/RECORD +190 -0
- microeye-2.3.2.dist-info/WHEEL +5 -0
- microeye-2.3.2.dist-info/entry_points.txt +2 -0
- microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
- 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())
|