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,245 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
|
|
6
|
+
import pkg_resources
|
|
7
|
+
|
|
8
|
+
from microEye.qt import QApplication, Qt, QtCore, QtGui, QtSvg, QtWidgets
|
|
9
|
+
from microEye.utils.start_gui import StartGUI
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def check_modules(modules):
|
|
13
|
+
availability = {}
|
|
14
|
+
for module in modules:
|
|
15
|
+
try:
|
|
16
|
+
imported_module = importlib.import_module(module)
|
|
17
|
+
try:
|
|
18
|
+
version = pkg_resources.get_distribution(module).version
|
|
19
|
+
except pkg_resources.DistributionNotFound:
|
|
20
|
+
version = 'Version not found'
|
|
21
|
+
availability[module] = version
|
|
22
|
+
except ImportError:
|
|
23
|
+
availability[module] = None
|
|
24
|
+
return availability
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class microLauncher(QtWidgets.QMainWindow):
|
|
28
|
+
def __init__(self):
|
|
29
|
+
super().__init__()
|
|
30
|
+
|
|
31
|
+
# Remove the title bar
|
|
32
|
+
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
|
|
33
|
+
|
|
34
|
+
# Set round corners, white border, and dark gray background
|
|
35
|
+
self.setStyleSheet('''
|
|
36
|
+
microLauncher {
|
|
37
|
+
border: 1px solid white !important;
|
|
38
|
+
padding: 0;
|
|
39
|
+
}
|
|
40
|
+
QPushButton#closeButton {
|
|
41
|
+
border: none;
|
|
42
|
+
}
|
|
43
|
+
QPushButton#minButton {
|
|
44
|
+
border: none;
|
|
45
|
+
}
|
|
46
|
+
''')
|
|
47
|
+
|
|
48
|
+
self.dirname = os.path.dirname(os.path.abspath(__file__))
|
|
49
|
+
|
|
50
|
+
width = 160
|
|
51
|
+
height = 130
|
|
52
|
+
|
|
53
|
+
# Create a close button with SVG icon
|
|
54
|
+
self.close_button = QtWidgets.QPushButton()
|
|
55
|
+
self.close_button.setObjectName('closeButton')
|
|
56
|
+
self.close_button.setFixedSize(30, 30)
|
|
57
|
+
self.close_button.clicked.connect(self.close)
|
|
58
|
+
|
|
59
|
+
self.minimize_button = QtWidgets.QPushButton()
|
|
60
|
+
self.minimize_button.setObjectName('minButton')
|
|
61
|
+
self.minimize_button.setFixedSize(30, 30)
|
|
62
|
+
self.minimize_button.clicked.connect(lambda: self.showMinimized())
|
|
63
|
+
|
|
64
|
+
# Load SVG and set as icon
|
|
65
|
+
svg_renderer = QtSvg.QSvgRenderer(
|
|
66
|
+
os.path.join(self.dirname, '../icons/close.svg')
|
|
67
|
+
)
|
|
68
|
+
svg_renderer.setAspectRatioMode(Qt.AspectRatioMode.KeepAspectRatio)
|
|
69
|
+
svg_pixmap = QtGui.QPixmap(24, 24)
|
|
70
|
+
svg_pixmap.fill(QtCore.Qt.GlobalColor.transparent)
|
|
71
|
+
painter = QtGui.QPainter(svg_pixmap)
|
|
72
|
+
svg_renderer.render(painter)
|
|
73
|
+
painter.end()
|
|
74
|
+
self.close_button.setIcon(QtGui.QIcon(svg_pixmap))
|
|
75
|
+
self.close_button.setIconSize(QtCore.QSize(24, 24))
|
|
76
|
+
|
|
77
|
+
svg_renderer = QtSvg.QSvgRenderer(
|
|
78
|
+
os.path.join(self.dirname, '../icons/min.svg')
|
|
79
|
+
)
|
|
80
|
+
svg_renderer.setAspectRatioMode(Qt.AspectRatioMode.KeepAspectRatio)
|
|
81
|
+
svg_pixmap = QtGui.QPixmap(24, 24)
|
|
82
|
+
svg_pixmap.fill(QtCore.Qt.GlobalColor.transparent)
|
|
83
|
+
painter = QtGui.QPainter(svg_pixmap)
|
|
84
|
+
svg_renderer.render(painter)
|
|
85
|
+
painter.end()
|
|
86
|
+
self.minimize_button.setIcon(QtGui.QIcon(svg_pixmap))
|
|
87
|
+
self.minimize_button.setIconSize(QtCore.QSize(24, 24))
|
|
88
|
+
|
|
89
|
+
# Layout for close button
|
|
90
|
+
self.close_button_layout = QtWidgets.QHBoxLayout()
|
|
91
|
+
self.close_button_layout.addStretch()
|
|
92
|
+
self.close_button_layout.addWidget(self.minimize_button)
|
|
93
|
+
self.close_button_layout.addWidget(self.close_button)
|
|
94
|
+
|
|
95
|
+
# Set the main layout
|
|
96
|
+
self.main_layout = QtWidgets.QVBoxLayout()
|
|
97
|
+
self.main_layout.addLayout(self.close_button_layout)
|
|
98
|
+
|
|
99
|
+
# Create buttons with images
|
|
100
|
+
self.button1 = QtWidgets.QPushButton()
|
|
101
|
+
pixmap1 = QtGui.QPixmap(
|
|
102
|
+
os.path.join(self.dirname, '../icons/mieye.png')
|
|
103
|
+
).scaled(
|
|
104
|
+
width,
|
|
105
|
+
height,
|
|
106
|
+
Qt.AspectRatioMode.KeepAspectRatio,
|
|
107
|
+
Qt.TransformationMode.SmoothTransformation,
|
|
108
|
+
)
|
|
109
|
+
self.button1.setIcon(QtGui.QIcon(pixmap1))
|
|
110
|
+
self.button1.setIconSize(QtCore.QSize(width, height))
|
|
111
|
+
self.button1.clicked.connect(lambda: self.launch_module())
|
|
112
|
+
|
|
113
|
+
self.button2 = QtWidgets.QPushButton()
|
|
114
|
+
pixmap2 = QtGui.QPixmap(
|
|
115
|
+
os.path.join(self.dirname, '../icons/viewer.png')
|
|
116
|
+
).scaled(
|
|
117
|
+
width,
|
|
118
|
+
height,
|
|
119
|
+
Qt.AspectRatioMode.KeepAspectRatio,
|
|
120
|
+
Qt.TransformationMode.SmoothTransformation,
|
|
121
|
+
)
|
|
122
|
+
self.button2.setIcon(QtGui.QIcon(pixmap2))
|
|
123
|
+
self.button2.setIconSize(QtCore.QSize(width, height))
|
|
124
|
+
self.button2.clicked.connect(lambda: self.launch_module('viewer'))
|
|
125
|
+
|
|
126
|
+
# Create comboboxes
|
|
127
|
+
self.qt_api_combobox = QtWidgets.QComboBox()
|
|
128
|
+
self.populate_combobox(self.qt_api_combobox, ['PySide6', 'PyQt5', 'PyQt6'])
|
|
129
|
+
|
|
130
|
+
self.theme_combobox = QtWidgets.QComboBox()
|
|
131
|
+
self.populate_combobox(
|
|
132
|
+
self.theme_combobox, ['qdarktheme', 'qdarkstyle', 'None'], True
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Layout for buttons
|
|
136
|
+
self.buttons_layout = QtWidgets.QHBoxLayout()
|
|
137
|
+
self.buttons_layout.addWidget(self.button1)
|
|
138
|
+
self.buttons_layout.addWidget(self.button2)
|
|
139
|
+
|
|
140
|
+
# Add widgets to the main layout
|
|
141
|
+
self.main_layout.addLayout(self.buttons_layout)
|
|
142
|
+
self.main_layout.addWidget(self.qt_api_combobox)
|
|
143
|
+
self.main_layout.addWidget(self.theme_combobox)
|
|
144
|
+
|
|
145
|
+
# Set central widget
|
|
146
|
+
central_widget = QtWidgets.QWidget()
|
|
147
|
+
central_widget.setLayout(self.main_layout)
|
|
148
|
+
self.setCentralWidget(central_widget)
|
|
149
|
+
|
|
150
|
+
# Window size and title
|
|
151
|
+
self.setWindowTitle('microEye Launcher')
|
|
152
|
+
# self.resize(400, 300)
|
|
153
|
+
self.setContentsMargins(0, 0, 0, 0)
|
|
154
|
+
|
|
155
|
+
self.show()
|
|
156
|
+
self.center()
|
|
157
|
+
|
|
158
|
+
def launch_module(self, module='mieye'):
|
|
159
|
+
'''Launch the selected module'''
|
|
160
|
+
qt_api = self.qt_api_combobox.currentText()
|
|
161
|
+
miTheme = self.theme_combobox.currentText()
|
|
162
|
+
|
|
163
|
+
env = dict(os.environ, QT_API=qt_api, PYQTGRAPH_QT_LIB=qt_api, MITHEME=miTheme)
|
|
164
|
+
|
|
165
|
+
# Define the command and arguments
|
|
166
|
+
cmd = ['python', '-m', 'microEye.launcher', '--module', module]
|
|
167
|
+
|
|
168
|
+
# Get the current working directory
|
|
169
|
+
cwd = os.getcwd()
|
|
170
|
+
|
|
171
|
+
# Open a new terminal and run the command
|
|
172
|
+
if os.name == 'nt': # Windows
|
|
173
|
+
flags = 0
|
|
174
|
+
flags |= subprocess.DETACHED_PROCESS
|
|
175
|
+
flags |= subprocess.CREATE_NEW_PROCESS_GROUP
|
|
176
|
+
flags |= subprocess.CREATE_BREAKAWAY_FROM_JOB
|
|
177
|
+
flags |= subprocess.CREATE_NO_WINDOW
|
|
178
|
+
|
|
179
|
+
pkwargs = {
|
|
180
|
+
'close_fds': True, # close stdin/stdout/stderr on child
|
|
181
|
+
'creationflags': flags,
|
|
182
|
+
}
|
|
183
|
+
subprocess.Popen(
|
|
184
|
+
['start', 'cmd', '/k'] + cmd, cwd=cwd, shell=True, env=env, **pkwargs
|
|
185
|
+
)
|
|
186
|
+
# self.close()
|
|
187
|
+
else: # Unix-based systems (Linux, macOS, etc.)
|
|
188
|
+
subprocess.Popen(
|
|
189
|
+
['setsid', 'gnome-terminal', '--working-directory=' + cwd, '--'] + cmd,
|
|
190
|
+
env=env,
|
|
191
|
+
)
|
|
192
|
+
self.close()
|
|
193
|
+
|
|
194
|
+
def center(self):
|
|
195
|
+
'''Centers the window within the screen.'''
|
|
196
|
+
qtRectangle = self.frameGeometry()
|
|
197
|
+
centerPoint = QApplication.primaryScreen().availableGeometry().center()
|
|
198
|
+
qtRectangle.moveCenter(centerPoint)
|
|
199
|
+
self.move(qtRectangle.topLeft())
|
|
200
|
+
|
|
201
|
+
def populate_combobox(self, combobox, items: Iterable[str], theme=False):
|
|
202
|
+
model = QtGui.QStandardItemModel()
|
|
203
|
+
availability = check_modules(items)
|
|
204
|
+
for item in items:
|
|
205
|
+
standard_item = QtGui.QStandardItem(item)
|
|
206
|
+
if availability[item] is None and item.lower() != 'none':
|
|
207
|
+
disabledColor = QtGui.QBrush(QtGui.QColor(Qt.GlobalColor.red))
|
|
208
|
+
standard_item.setData(
|
|
209
|
+
disabledColor, QtCore.Qt.ItemDataRole.ForegroundRole
|
|
210
|
+
)
|
|
211
|
+
standard_item.setEnabled(False)
|
|
212
|
+
standard_item.setToolTip(f'{standard_item.text()} is not installed')
|
|
213
|
+
elif item.lower() != 'none':
|
|
214
|
+
prefix = (
|
|
215
|
+
'' if availability[item] == 'Version not found' else 'Version: '
|
|
216
|
+
)
|
|
217
|
+
standard_item.setToolTip(f'{prefix}{availability[item]}')
|
|
218
|
+
model.appendRow(standard_item)
|
|
219
|
+
|
|
220
|
+
if theme:
|
|
221
|
+
for item in QtWidgets.QStyleFactory.keys(): # noqa: SIM118
|
|
222
|
+
standard_item = QtGui.QStandardItem(item)
|
|
223
|
+
model.appendRow(standard_item)
|
|
224
|
+
combobox.setModel(model)
|
|
225
|
+
|
|
226
|
+
def StartGUI():
|
|
227
|
+
'''
|
|
228
|
+
Initializes a new QApplication and microLauncher.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
path : str, optional
|
|
233
|
+
The path to a file to be loaded initially.
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
tuple of QApplication and microLauncher
|
|
238
|
+
Returns a tuple with QApplication and microLauncher window.
|
|
239
|
+
'''
|
|
240
|
+
return StartGUI(microLauncher, theme=None)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
if __name__ == '__main__':
|
|
244
|
+
app, window = microLauncher.StartGUI()
|
|
245
|
+
app.exec()
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
|
|
5
|
+
from pyqtgraph.parametertree import Parameter, ParameterTree
|
|
6
|
+
from pyqtgraph.parametertree.parameterTypes import ActionParameter, GroupParameter
|
|
7
|
+
|
|
8
|
+
from microEye.qt import QtWidgets, Signal, Slot, getOpenFileName, getSaveFileName
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Tree(ParameterTree):
|
|
12
|
+
'''
|
|
13
|
+
Tree widget for editing parameters.
|
|
14
|
+
|
|
15
|
+
Attributes
|
|
16
|
+
----------
|
|
17
|
+
paramsChanged : Signal
|
|
18
|
+
Signal for parameter changed event.
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
PARAMS: type[Enum] = None
|
|
22
|
+
|
|
23
|
+
paramsChanged = Signal(Parameter, object)
|
|
24
|
+
'''Signal emitted when parameters are changed.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
GroupParameter
|
|
29
|
+
The group parameter that was changed.
|
|
30
|
+
list
|
|
31
|
+
A list of changes made to the parameter.
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
def __init__(self, parent: Optional['QtWidgets.QWidget'] = None):
|
|
35
|
+
'''
|
|
36
|
+
Initialize the Tree.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
parent : QWidget, optional
|
|
41
|
+
The parent widget, by default None.
|
|
42
|
+
'''
|
|
43
|
+
super().__init__(parent=parent)
|
|
44
|
+
|
|
45
|
+
self.setMinimumWidth(50)
|
|
46
|
+
self.create_parameters()
|
|
47
|
+
self.setParameters(self.param_tree, showTop=False)
|
|
48
|
+
|
|
49
|
+
def create_parameters(self):
|
|
50
|
+
'''
|
|
51
|
+
Create the parameter tree structure.
|
|
52
|
+
|
|
53
|
+
Reimplement in child class.
|
|
54
|
+
'''
|
|
55
|
+
params = []
|
|
56
|
+
|
|
57
|
+
self.param_tree = Parameter.create(name='', type='group', children=params)
|
|
58
|
+
self.param_tree.sigTreeStateChanged.connect(self.change)
|
|
59
|
+
self.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)
|
|
60
|
+
|
|
61
|
+
def get_param(self, param: Union[Enum, str]) -> Union[Parameter, ActionParameter]:
|
|
62
|
+
'''Get a parameter by name.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
param : Enum
|
|
67
|
+
parameter.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
Union[Parameter, ActionParameter]
|
|
72
|
+
Retrieved parameter.
|
|
73
|
+
'''
|
|
74
|
+
try:
|
|
75
|
+
if isinstance(param, Enum):
|
|
76
|
+
return self.param_tree.param(*param.value.split('.'))
|
|
77
|
+
else:
|
|
78
|
+
return self.param_tree.param(*param.split('.'))
|
|
79
|
+
except KeyError:
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def get_param_value(self, param: Union[Enum, str]):
|
|
83
|
+
'''Get a parameter value by name.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
param : Enum
|
|
88
|
+
parameter.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
Any
|
|
93
|
+
The value of the parameter.
|
|
94
|
+
'''
|
|
95
|
+
try:
|
|
96
|
+
if isinstance(param, Enum):
|
|
97
|
+
return self.param_tree.param(*param.value.split('.')).value()
|
|
98
|
+
else:
|
|
99
|
+
return self.param_tree.param(*param.split('.')).value()
|
|
100
|
+
except KeyError:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def set_param_value(
|
|
104
|
+
self, param: Enum, value, blockSignals: Union[Any, None] = None
|
|
105
|
+
):
|
|
106
|
+
'''
|
|
107
|
+
Set a parameter value by name.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
param : Enum
|
|
112
|
+
parameter.
|
|
113
|
+
value : Any
|
|
114
|
+
The value to set.
|
|
115
|
+
blockSignals : Union[Any, None], optional
|
|
116
|
+
If provided, signals for parameter changes are blocked during the update.
|
|
117
|
+
The interpretation of the value is left to the implementation.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
bool
|
|
122
|
+
True if the value is set successfully, False otherwise.
|
|
123
|
+
'''
|
|
124
|
+
try:
|
|
125
|
+
parameter: Parameter = self.param_tree.param(*param.value.split('.'))
|
|
126
|
+
parameter.setValue(value, blockSignals)
|
|
127
|
+
except Exception:
|
|
128
|
+
import traceback
|
|
129
|
+
|
|
130
|
+
traceback.print_exc()
|
|
131
|
+
return False
|
|
132
|
+
else:
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
def get_param_path(self, param: Parameter):
|
|
136
|
+
'''
|
|
137
|
+
Get the child path of a parameter in the parameter tree.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
param : Parameter
|
|
142
|
+
The parameter for which to retrieve the child path.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
list
|
|
147
|
+
The child path of the parameter.
|
|
148
|
+
'''
|
|
149
|
+
return self.param_tree.childPath(param)
|
|
150
|
+
|
|
151
|
+
def add_param_child(self, parent: Enum, value: dict):
|
|
152
|
+
"""
|
|
153
|
+
Add a child parameter to the specified parent parameter.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
parent : Enum
|
|
158
|
+
The parent parameter to which the child will be added.
|
|
159
|
+
value : dict
|
|
160
|
+
A dictionary representing the child parameter. It should contain at
|
|
161
|
+
least the following key-value pairs:
|
|
162
|
+
- 'name': str, the name of the parameter.
|
|
163
|
+
- 'type': str, the type of the parameter (e.g., 'int', 'float', 'str').
|
|
164
|
+
- 'value': Any, the initial value of the parameter.
|
|
165
|
+
Additional optional keys can be included based on the desired configuration.
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
None
|
|
170
|
+
"""
|
|
171
|
+
parent = self.get_param(parent)
|
|
172
|
+
parent.addChild(value, autoIncrementName=True)
|
|
173
|
+
|
|
174
|
+
def get_children(self, param: Enum):
|
|
175
|
+
"""
|
|
176
|
+
Get the values of all children of a specified parameter.
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
param : Enum
|
|
181
|
+
The parameter whose children's values will be retrieved.
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
list
|
|
186
|
+
List of values of all children of the specified parameter.
|
|
187
|
+
"""
|
|
188
|
+
res = []
|
|
189
|
+
param = self.get_param(param)
|
|
190
|
+
if isinstance(param, GroupParameter):
|
|
191
|
+
for child in param.children():
|
|
192
|
+
res.append(child.value())
|
|
193
|
+
return res
|
|
194
|
+
|
|
195
|
+
def change(self, param: Parameter, changes: list):
|
|
196
|
+
'''
|
|
197
|
+
Handle parameter changes as needed.
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
----------
|
|
201
|
+
param : Parameter
|
|
202
|
+
The parameter that triggered the change.
|
|
203
|
+
changes : list
|
|
204
|
+
List of changes.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
None
|
|
209
|
+
'''
|
|
210
|
+
# Handle parameter changes as needed
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
@Slot(object)
|
|
214
|
+
def export_json(self, action=None):
|
|
215
|
+
'''
|
|
216
|
+
Export parameters to a JSON file.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
None
|
|
221
|
+
'''
|
|
222
|
+
filename, _ = getSaveFileName(
|
|
223
|
+
None, 'Save Parameters', '', 'JSON Files (*.json);;All Files (*)'
|
|
224
|
+
)
|
|
225
|
+
if not filename:
|
|
226
|
+
return # User canceled the operation
|
|
227
|
+
|
|
228
|
+
state = self.param_tree.saveState()
|
|
229
|
+
with open(filename, 'w', encoding='utf8') as file:
|
|
230
|
+
json.dump(state, file, indent=2)
|
|
231
|
+
|
|
232
|
+
def get_json(self):
|
|
233
|
+
'''
|
|
234
|
+
Export parameters to a JSON text.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
str
|
|
239
|
+
A string containing all parameters in JSON format.
|
|
240
|
+
'''
|
|
241
|
+
return self.param_tree.saveState()
|
|
242
|
+
|
|
243
|
+
# Load parameters from JSON
|
|
244
|
+
@Slot(object)
|
|
245
|
+
def load_json(self, action=None):
|
|
246
|
+
'''
|
|
247
|
+
Load parameters from a JSON file.
|
|
248
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
None
|
|
252
|
+
'''
|
|
253
|
+
filename, _ = getOpenFileName(
|
|
254
|
+
None, 'Load Parameters', '', 'JSON Files (*.json);;All Files (*)'
|
|
255
|
+
)
|
|
256
|
+
if not filename:
|
|
257
|
+
return # User canceled the operation
|
|
258
|
+
|
|
259
|
+
with open(filename, encoding='utf8') as file:
|
|
260
|
+
state = json.load(file)
|
|
261
|
+
self.param_tree.restoreState(
|
|
262
|
+
state, addChildren=False, removeChildren=False, blockSignals=False)
|
|
263
|
+
|
|
264
|
+
def get_param_paths(self) -> list[str]:
|
|
265
|
+
'''
|
|
266
|
+
Get a list of all parameter paths in the tree, excluding group parameters.
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
list
|
|
271
|
+
A list of all parameter paths, excluding groups.
|
|
272
|
+
'''
|
|
273
|
+
|
|
274
|
+
def collect_paths(param, current_path=None):
|
|
275
|
+
paths = []
|
|
276
|
+
if current_path is None:
|
|
277
|
+
current_path = []
|
|
278
|
+
for child in param.children():
|
|
279
|
+
child_path = current_path + [child.name()]
|
|
280
|
+
if not child.hasChildren(): # This checks if parameter is not a group
|
|
281
|
+
paths.append('.'.join(child_path))
|
|
282
|
+
else:
|
|
283
|
+
paths.extend(collect_paths(child, child_path))
|
|
284
|
+
return paths
|
|
285
|
+
|
|
286
|
+
return collect_paths(self.param_tree)
|
|
287
|
+
|
|
288
|
+
def search_param(self, contains: str) -> Union[None, Parameter, ActionParameter]:
|
|
289
|
+
'''
|
|
290
|
+
Find parameter that contains the supplied string
|
|
291
|
+
in the tree and return it if found.
|
|
292
|
+
|
|
293
|
+
Parameters
|
|
294
|
+
----------
|
|
295
|
+
contains : str
|
|
296
|
+
The string to search for in the parameter paths.
|
|
297
|
+
|
|
298
|
+
Returns
|
|
299
|
+
-------
|
|
300
|
+
object or None
|
|
301
|
+
The value of the first parameter that contains the supplied string,
|
|
302
|
+
or None if no match is found.
|
|
303
|
+
'''
|
|
304
|
+
path = [
|
|
305
|
+
path
|
|
306
|
+
for path in self.get_param_paths()
|
|
307
|
+
if contains.lower().strip() in path.lower().split('.')[-1]
|
|
308
|
+
]
|
|
309
|
+
if path:
|
|
310
|
+
self.get_param(path[0])
|
|
311
|
+
else:
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
def set_expanded(self, param: Union[Parameter, Enum, str], value: bool = False):
|
|
315
|
+
if isinstance(param, (Enum, str)):
|
|
316
|
+
param = self.get_param(param)
|
|
317
|
+
|
|
318
|
+
if not isinstance(param, Parameter):
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
if param:
|
|
322
|
+
for item in self.listAllItems():
|
|
323
|
+
if item.param == param:
|
|
324
|
+
item.setExpanded(value)
|
|
325
|
+
break
|