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,457 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from microEye.qt import QApplication, QtWidgets
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def create_line_edit(
|
|
9
|
+
label,
|
|
10
|
+
default_text,
|
|
11
|
+
layout: Union[QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout],
|
|
12
|
+
) -> QtWidgets.QLineEdit:
|
|
13
|
+
'''
|
|
14
|
+
Create a QLineEdit widget with label.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
label : str
|
|
19
|
+
The label for the line edit.
|
|
20
|
+
default_text : str
|
|
21
|
+
The default text to display in the line edit.
|
|
22
|
+
layout : Union[QVBoxLayout, QHBoxLayout, QFormLayout]
|
|
23
|
+
The layout to which the line edit will be added.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
QLineEdit
|
|
28
|
+
The created QLineEdit widget.
|
|
29
|
+
'''
|
|
30
|
+
line_edit = QtWidgets.QLineEdit(default_text)
|
|
31
|
+
|
|
32
|
+
if isinstance(layout, QtWidgets.QFormLayout):
|
|
33
|
+
layout.addRow(QtWidgets.QLabel(label), line_edit)
|
|
34
|
+
else:
|
|
35
|
+
layout.addWidget(QtWidgets.QLabel(label))
|
|
36
|
+
layout.addWidget(line_edit)
|
|
37
|
+
|
|
38
|
+
return line_edit
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_text_edit(
|
|
42
|
+
label,
|
|
43
|
+
default_text,
|
|
44
|
+
layout: Union[QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout],
|
|
45
|
+
) -> QtWidgets.QTextEdit:
|
|
46
|
+
'''
|
|
47
|
+
Create a QLineEdit widget with label.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
label : str
|
|
52
|
+
The label for the line edit.
|
|
53
|
+
default_text : str
|
|
54
|
+
The default text to display in the line edit.
|
|
55
|
+
layout : Union[QVBoxLayout, QHBoxLayout, QFormLayout]
|
|
56
|
+
The layout to which the line edit will be added.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
QLineEdit
|
|
61
|
+
The created QLineEdit widget.
|
|
62
|
+
'''
|
|
63
|
+
text_edit = QtWidgets.QTextEdit(default_text)
|
|
64
|
+
if isinstance(layout, QtWidgets.QFormLayout):
|
|
65
|
+
layout.addRow(QtWidgets.QLabel(label))
|
|
66
|
+
layout.addRow(text_edit)
|
|
67
|
+
else:
|
|
68
|
+
layout.addWidget(QtWidgets.QLabel(label))
|
|
69
|
+
layout.addWidget(text_edit)
|
|
70
|
+
return text_edit
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def create_combo_box(
|
|
74
|
+
label,
|
|
75
|
+
items,
|
|
76
|
+
default_item,
|
|
77
|
+
layout: Union[QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout],
|
|
78
|
+
) -> QtWidgets.QComboBox:
|
|
79
|
+
'''
|
|
80
|
+
Create a QComboBox widget with label.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
label : str
|
|
85
|
+
The label for the combo box.
|
|
86
|
+
items : list
|
|
87
|
+
The list of items to populate the combo box.
|
|
88
|
+
default_item : str
|
|
89
|
+
The default item to select in the combo box.
|
|
90
|
+
layout : Union[QVBoxLayout, QHBoxLayout, QFormLayout]
|
|
91
|
+
The layout to which the combo box will be added.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
QComboBox
|
|
96
|
+
The created QComboBox widget.
|
|
97
|
+
'''
|
|
98
|
+
combo_box = QtWidgets.QComboBox()
|
|
99
|
+
combo_box.addItems(items)
|
|
100
|
+
combo_box.setCurrentText(default_item)
|
|
101
|
+
if isinstance(layout, QtWidgets.QFormLayout):
|
|
102
|
+
layout.addRow(QtWidgets.QLabel(label), combo_box)
|
|
103
|
+
else:
|
|
104
|
+
layout.addWidget(QtWidgets.QLabel(label))
|
|
105
|
+
layout.addWidget(combo_box)
|
|
106
|
+
return combo_box
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def create_double_spin_box(
|
|
110
|
+
min_value=0, max_value=1, single_step=0.01, decimals=3, initial_value=0, slot=None
|
|
111
|
+
):
|
|
112
|
+
'''
|
|
113
|
+
Create a QDoubleSpinBox widget.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
min_value : float, optional
|
|
118
|
+
Minimum allowed value.
|
|
119
|
+
max_value : float, optional
|
|
120
|
+
Maximum allowed value.
|
|
121
|
+
single_step : float, optional
|
|
122
|
+
Step size for increment/decrement.
|
|
123
|
+
decimals : int, optional
|
|
124
|
+
Number of decimal places.
|
|
125
|
+
initial_value : float, optional
|
|
126
|
+
Initial value of the spin box.
|
|
127
|
+
slot : Callable, optional
|
|
128
|
+
Function to connect to the valueChanged signal.
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
QDoubleSpinBox
|
|
133
|
+
The created QDoubleSpinBox widget.
|
|
134
|
+
'''
|
|
135
|
+
double_spin_box = QtWidgets.QDoubleSpinBox()
|
|
136
|
+
double_spin_box.setMinimum(min_value)
|
|
137
|
+
double_spin_box.setMaximum(max_value)
|
|
138
|
+
double_spin_box.setSingleStep(single_step)
|
|
139
|
+
double_spin_box.setDecimals(decimals)
|
|
140
|
+
double_spin_box.setValue(initial_value)
|
|
141
|
+
if slot:
|
|
142
|
+
double_spin_box.valueChanged.connect(slot)
|
|
143
|
+
return double_spin_box
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def create_labelled_double_spin_box(
|
|
147
|
+
label,
|
|
148
|
+
min_val,
|
|
149
|
+
max_val,
|
|
150
|
+
default_val,
|
|
151
|
+
layout: Union[QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout],
|
|
152
|
+
) -> QtWidgets.QDoubleSpinBox:
|
|
153
|
+
'''
|
|
154
|
+
Create a QDoubleSpinBox widget with a label.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
label : str
|
|
159
|
+
The label for the spin box.
|
|
160
|
+
min_val : float
|
|
161
|
+
Minimum allowed value.
|
|
162
|
+
max_val : float
|
|
163
|
+
Maximum allowed value.
|
|
164
|
+
default_val : float
|
|
165
|
+
Initial value of the spin box.
|
|
166
|
+
layout : Union[QVBoxLayout, QHBoxLayout, QFormLayout]
|
|
167
|
+
The layout to which the spin box will be added.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
QDoubleSpinBox
|
|
172
|
+
The created QDoubleSpinBox widget.
|
|
173
|
+
'''
|
|
174
|
+
spin_box = QtWidgets.QDoubleSpinBox()
|
|
175
|
+
spin_box.setMinimum(min_val)
|
|
176
|
+
spin_box.setMaximum(max_val)
|
|
177
|
+
spin_box.setValue(default_val)
|
|
178
|
+
|
|
179
|
+
if isinstance(layout, QtWidgets.QFormLayout):
|
|
180
|
+
layout.addRow(QtWidgets.QLabel(label), spin_box)
|
|
181
|
+
else:
|
|
182
|
+
layout.addWidget(QtWidgets.QLabel(label))
|
|
183
|
+
layout.addWidget(spin_box)
|
|
184
|
+
return spin_box
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def create_spin_box(
|
|
188
|
+
min_value=0, max_value=100, single_step=1, initial_value=0, slot=None
|
|
189
|
+
):
|
|
190
|
+
'''
|
|
191
|
+
Create a QSpinBox widget.
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
min_value : int, optional
|
|
196
|
+
Minimum allowed value.
|
|
197
|
+
max_value : int, optional
|
|
198
|
+
Maximum allowed value.
|
|
199
|
+
single_step : int, optional
|
|
200
|
+
Step size for increment/decrement.
|
|
201
|
+
initial_value : int, optional
|
|
202
|
+
Initial value of the spin box.
|
|
203
|
+
slot : Callable, optional
|
|
204
|
+
Function to connect to the valueChanged signal.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
QSpinBox
|
|
209
|
+
The created QSpinBox widget.
|
|
210
|
+
'''
|
|
211
|
+
|
|
212
|
+
spin_box = QtWidgets.QSpinBox()
|
|
213
|
+
spin_box.setMinimum(min_value)
|
|
214
|
+
spin_box.setMaximum(max_value)
|
|
215
|
+
spin_box.setSingleStep(single_step)
|
|
216
|
+
spin_box.setValue(initial_value)
|
|
217
|
+
if slot:
|
|
218
|
+
spin_box.valueChanged.connect(slot)
|
|
219
|
+
return spin_box
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def create_check_box(text, initial_state=False, state_changed_slot=None):
|
|
223
|
+
'''
|
|
224
|
+
Create a QCheckBox widget.
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
text : str
|
|
229
|
+
Text to display next to the checkbox.
|
|
230
|
+
initial_state : bool, optional
|
|
231
|
+
Initial state of the checkbox.
|
|
232
|
+
state_changed_slot : Callable, optional
|
|
233
|
+
Function to connect to the stateChanged signal.
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
QCheckBox
|
|
238
|
+
The created QCheckBox widget.
|
|
239
|
+
'''
|
|
240
|
+
check_box = QtWidgets.QCheckBox(text)
|
|
241
|
+
check_box.setChecked(initial_state)
|
|
242
|
+
|
|
243
|
+
if state_changed_slot:
|
|
244
|
+
check_box.stateChanged.connect(state_changed_slot)
|
|
245
|
+
|
|
246
|
+
return check_box
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def create_group_box(
|
|
250
|
+
title,
|
|
251
|
+
layout_type: Union[
|
|
252
|
+
QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout
|
|
253
|
+
] = QtWidgets.QVBoxLayout,
|
|
254
|
+
):
|
|
255
|
+
'''
|
|
256
|
+
Create a QGroupBox with a specified layout type.
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
title : str
|
|
261
|
+
Title of the group box.
|
|
262
|
+
layout_type : QVBoxLayout or QHBoxLayout or QFormLayout, optional
|
|
263
|
+
Type of layout to use inside the group box.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
QGroupBox, QLayout
|
|
268
|
+
The created QGroupBox and its associated layout.
|
|
269
|
+
'''
|
|
270
|
+
group_box = QtWidgets.QGroupBox(title)
|
|
271
|
+
layout = layout_type()
|
|
272
|
+
group_box.setLayout(layout)
|
|
273
|
+
return group_box, layout
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def create_widget(
|
|
277
|
+
layout_type: Union[
|
|
278
|
+
QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout
|
|
279
|
+
] = QtWidgets.QVBoxLayout,
|
|
280
|
+
) -> tuple[
|
|
281
|
+
QtWidgets.QWidget,
|
|
282
|
+
Union[QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout, QtWidgets.QFormLayout],
|
|
283
|
+
]:
|
|
284
|
+
'''
|
|
285
|
+
Create a QWidget with a specified layout type.
|
|
286
|
+
|
|
287
|
+
Parameters
|
|
288
|
+
----------
|
|
289
|
+
layout_type : QVBoxLayout or QHBoxLayout or QFormLayout, optional
|
|
290
|
+
Type of layout to use inside the widget.
|
|
291
|
+
|
|
292
|
+
Returns
|
|
293
|
+
-------
|
|
294
|
+
QWidget, QLayout
|
|
295
|
+
The created QWidget and its associated layout.
|
|
296
|
+
'''
|
|
297
|
+
widget = QtWidgets.QWidget()
|
|
298
|
+
layout = layout_type()
|
|
299
|
+
widget.setLayout(layout)
|
|
300
|
+
return widget, layout
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def create_hbox_layout(*args):
|
|
304
|
+
'''
|
|
305
|
+
Create a QHBoxLayout and add the specified widgets to it.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
*args : QWidget
|
|
310
|
+
Widgets to add to the layout.
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
-------
|
|
314
|
+
QHBoxLayout
|
|
315
|
+
The created QHBoxLayout.
|
|
316
|
+
'''
|
|
317
|
+
hbox_layout = QtWidgets.QHBoxLayout()
|
|
318
|
+
|
|
319
|
+
for widget in args:
|
|
320
|
+
hbox_layout.addWidget(widget)
|
|
321
|
+
|
|
322
|
+
return hbox_layout
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def create_vbox_layout(*args):
|
|
326
|
+
'''
|
|
327
|
+
Create a QVBoxLayout and add the specified widgets to it.
|
|
328
|
+
|
|
329
|
+
Parameters
|
|
330
|
+
----------
|
|
331
|
+
*args : QWidget
|
|
332
|
+
Widgets to add to the layout.
|
|
333
|
+
|
|
334
|
+
Returns
|
|
335
|
+
-------
|
|
336
|
+
QVBoxLayout
|
|
337
|
+
The created QVBoxLayout.
|
|
338
|
+
'''
|
|
339
|
+
vbox_layout = QtWidgets.QVBoxLayout()
|
|
340
|
+
|
|
341
|
+
for widget in args:
|
|
342
|
+
vbox_layout.addWidget(widget)
|
|
343
|
+
|
|
344
|
+
return vbox_layout
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def layout_add_elements(
|
|
348
|
+
layout: Union[QtWidgets.QVBoxLayout, QtWidgets.QHBoxLayout], *args
|
|
349
|
+
):
|
|
350
|
+
'''
|
|
351
|
+
Add elements to a QVBoxLayout or QHBoxLayout.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
layout : QVBoxLayout or QHBoxLayout
|
|
356
|
+
The layout to which elements will be added.
|
|
357
|
+
*args : QWidget or QLayout
|
|
358
|
+
Elements to add to the layout.
|
|
359
|
+
'''
|
|
360
|
+
for widget in args:
|
|
361
|
+
if isinstance(widget, QtWidgets.QWidget):
|
|
362
|
+
layout.addWidget(widget)
|
|
363
|
+
elif isinstance(widget, QtWidgets.QLayout):
|
|
364
|
+
layout.addLayout(widget)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def get_scaling_factor(height, width, target_percentage=0.8):
|
|
368
|
+
'''
|
|
369
|
+
Calculate the scaling factor to fill the specified percentage of the screen.
|
|
370
|
+
|
|
371
|
+
Parameters
|
|
372
|
+
----------
|
|
373
|
+
height : int
|
|
374
|
+
Height of the content.
|
|
375
|
+
width : int
|
|
376
|
+
Width of the content.
|
|
377
|
+
target_percentage : float, optional
|
|
378
|
+
Target percentage of the screen to fill.
|
|
379
|
+
|
|
380
|
+
Returns
|
|
381
|
+
-------
|
|
382
|
+
float
|
|
383
|
+
The calculated scaling factor.
|
|
384
|
+
'''
|
|
385
|
+
main_screen = QApplication.primaryScreen().availableGeometry()
|
|
386
|
+
screen_info = (main_screen.width(), main_screen.height())
|
|
387
|
+
|
|
388
|
+
# Calculate scaling factor to fill the specified percentage of the screen
|
|
389
|
+
target_height = screen_info[1] * target_percentage
|
|
390
|
+
target_width = screen_info[0] * target_percentage
|
|
391
|
+
|
|
392
|
+
scaling_factor_height = target_height / height
|
|
393
|
+
scaling_factor_width = target_width / width
|
|
394
|
+
|
|
395
|
+
return min(scaling_factor_height, scaling_factor_width)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def time_string_ms(value: float):
|
|
399
|
+
'''
|
|
400
|
+
Convert time in milliseconds to a formatted string.
|
|
401
|
+
|
|
402
|
+
Parameters
|
|
403
|
+
----------
|
|
404
|
+
value : float
|
|
405
|
+
Time value in milliseconds.
|
|
406
|
+
|
|
407
|
+
Returns
|
|
408
|
+
-------
|
|
409
|
+
str
|
|
410
|
+
Formatted time string in minutes and seconds.
|
|
411
|
+
'''
|
|
412
|
+
seconds = value / 1000
|
|
413
|
+
return f'{seconds // 60:.0f} min {seconds % 60} seconds'
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def time_string_s(value: float):
|
|
417
|
+
'''
|
|
418
|
+
Convert time in seconds to a formatted string.
|
|
419
|
+
|
|
420
|
+
Parameters
|
|
421
|
+
----------
|
|
422
|
+
value : float
|
|
423
|
+
Time value in seconds.
|
|
424
|
+
|
|
425
|
+
Returns
|
|
426
|
+
-------
|
|
427
|
+
str
|
|
428
|
+
Formatted time string in minutes and seconds.
|
|
429
|
+
'''
|
|
430
|
+
return f'{value // 60:.0f} min {value % 60} seconds'
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def GaussianOffSet(x, a, x0, sigma, offset):
|
|
434
|
+
'''
|
|
435
|
+
Returns a Gaussian function with an offset.
|
|
436
|
+
|
|
437
|
+
f(x) = a * exp(-(x - x0)**2 / (2 * sigma**2)) + offset
|
|
438
|
+
|
|
439
|
+
Parameters
|
|
440
|
+
----------
|
|
441
|
+
x : np.ndarray
|
|
442
|
+
Input values.
|
|
443
|
+
a : float
|
|
444
|
+
Amplitude.
|
|
445
|
+
x0 : float
|
|
446
|
+
Center or mean.
|
|
447
|
+
sigma : float
|
|
448
|
+
Standard deviation.
|
|
449
|
+
offset : float
|
|
450
|
+
Y offset.
|
|
451
|
+
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
np.ndarray
|
|
455
|
+
Gaussian function f(x).
|
|
456
|
+
'''
|
|
457
|
+
return a * np.exp(-((x - x0) ** 2) / (2 * sigma**2)) + offset
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import hid
|
|
5
|
+
import numpy as np
|
|
6
|
+
from pyqtgraph.parametertree import Parameter
|
|
7
|
+
|
|
8
|
+
from microEye.qt import QApplication, QtCore, QtWidgets, Signal
|
|
9
|
+
from microEye.utils.hid.device import hidDevice
|
|
10
|
+
from microEye.utils.hid.enums import Buttons, hidParams
|
|
11
|
+
from microEye.utils.hid.utils import dz_hybrid, dz_scaled_radial, dz_sloped_scaled_axial
|
|
12
|
+
from microEye.utils.parameter_tree import Tree
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class hidController(Tree):
|
|
16
|
+
'''QWidget for handling HID controller input.'''
|
|
17
|
+
# Define signals with docstrings
|
|
18
|
+
reportEvent = Signal(Buttons)
|
|
19
|
+
'''Signal emitted when a controller button event occurs.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
Buttons: :class:`Buttons`
|
|
24
|
+
The enum member representing the button.
|
|
25
|
+
'''
|
|
26
|
+
|
|
27
|
+
reportLStickPosition = Signal(int, int)
|
|
28
|
+
'''Signal emitted when the left stick position changes.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
int: int
|
|
33
|
+
X-axis position of the left stick.
|
|
34
|
+
int: int
|
|
35
|
+
Y-axis position of the left stick.
|
|
36
|
+
'''
|
|
37
|
+
|
|
38
|
+
reportRStickPosition = Signal(int, int)
|
|
39
|
+
'''Signal emitted when the right stick position changes.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
int: int
|
|
44
|
+
X-axis position of the right stick.
|
|
45
|
+
int: int
|
|
46
|
+
Y-axis position of the right stick.
|
|
47
|
+
'''
|
|
48
|
+
PARAMS = hidParams
|
|
49
|
+
|
|
50
|
+
def __init__(self, parent: Optional['QtWidgets.QWidget'] = None):
|
|
51
|
+
'''Initialize the HID controller widget.'''
|
|
52
|
+
super().__init__(parent=parent)
|
|
53
|
+
|
|
54
|
+
self.refresh_list()
|
|
55
|
+
|
|
56
|
+
self.hid_device = None
|
|
57
|
+
|
|
58
|
+
self.last_btn = None
|
|
59
|
+
self.last_aux = None
|
|
60
|
+
self.left_analog = (128, 127)
|
|
61
|
+
self.right_analog = (128, 127)
|
|
62
|
+
|
|
63
|
+
self.timer = QtCore.QTimer()
|
|
64
|
+
self.timer.setInterval(1)
|
|
65
|
+
self.timer.timeout.connect(self.recurring_timer)
|
|
66
|
+
self.timer.start()
|
|
67
|
+
|
|
68
|
+
def create_parameters(self):
|
|
69
|
+
'''
|
|
70
|
+
Create the parameter tree structure.
|
|
71
|
+
'''
|
|
72
|
+
params = [
|
|
73
|
+
{'name': str(hidParams.TITLE), 'type': 'group',
|
|
74
|
+
'children': []},
|
|
75
|
+
{'name': str(hidParams.DEVICE), 'type': 'list',
|
|
76
|
+
'limits': []},
|
|
77
|
+
{'name': str(hidParams.REFRESH), 'type': 'action'},
|
|
78
|
+
{'name': str(hidParams.OPEN), 'type': 'action'},
|
|
79
|
+
{'name': str(hidParams.CLOSE), 'type': 'action'},
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
self.param_tree = Parameter.create(name='', type='group', children=params)
|
|
83
|
+
self.param_tree.sigTreeStateChanged.connect(self.change)
|
|
84
|
+
self.header().setSectionResizeMode(
|
|
85
|
+
QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
|
|
86
|
+
|
|
87
|
+
self.get_param(
|
|
88
|
+
hidParams.REFRESH).sigActivated.connect(self.refresh_list)
|
|
89
|
+
self.get_param(
|
|
90
|
+
hidParams.OPEN).sigActivated.connect(self.open_HID)
|
|
91
|
+
self.get_param(
|
|
92
|
+
hidParams.CLOSE).sigActivated.connect(self.close_HID)
|
|
93
|
+
|
|
94
|
+
def recurring_timer(self):
|
|
95
|
+
'''Read and process controller input in a recurring timer.'''
|
|
96
|
+
if self.hid_device is not None:
|
|
97
|
+
report = self.hid_device.read(64)
|
|
98
|
+
if report:
|
|
99
|
+
# print(report)
|
|
100
|
+
if len(report) == 14:
|
|
101
|
+
res = Buttons.from_value(0, report[-4])
|
|
102
|
+
if res:
|
|
103
|
+
if res != self.last_btn:
|
|
104
|
+
self.reportEvent.emit(res)
|
|
105
|
+
self.last_btn = res
|
|
106
|
+
else:
|
|
107
|
+
self.last_btn = None
|
|
108
|
+
res = Buttons.from_value(1, report[-3])
|
|
109
|
+
if res:
|
|
110
|
+
if res != self.last_aux:
|
|
111
|
+
self.reportEvent.emit(res)
|
|
112
|
+
self.last_aux = res
|
|
113
|
+
else:
|
|
114
|
+
self.last_aux = None
|
|
115
|
+
|
|
116
|
+
self.left_analog = report[1], report[3]
|
|
117
|
+
self.right_analog = report[5], report[7]
|
|
118
|
+
self.reportLStickPosition.emit(
|
|
119
|
+
*self.left_analog)
|
|
120
|
+
self.reportRStickPosition.emit(
|
|
121
|
+
*self.right_analog)
|
|
122
|
+
|
|
123
|
+
def close_HID(self):
|
|
124
|
+
'''Close the HID device.'''
|
|
125
|
+
if self.hid_device is not None:
|
|
126
|
+
self.hid_device.close()
|
|
127
|
+
self.hid_device = None
|
|
128
|
+
|
|
129
|
+
def open_HID(self):
|
|
130
|
+
'''Open the selected HID device.'''
|
|
131
|
+
device: hidDevice = self.get_param_value(hidParams.DEVICE)
|
|
132
|
+
if device is not None:
|
|
133
|
+
if self.hid_device is not None:
|
|
134
|
+
self.hid_device.close()
|
|
135
|
+
self.hid_device = None
|
|
136
|
+
|
|
137
|
+
self.hid_device = device.getHID()
|
|
138
|
+
|
|
139
|
+
def refresh_list(self):
|
|
140
|
+
'''Refresh the list of available HID devices.'''
|
|
141
|
+
devicesParam = self.get_param(hidParams.DEVICE)
|
|
142
|
+
|
|
143
|
+
devices = [hidDevice(device) for device in hid.enumerate()]
|
|
144
|
+
|
|
145
|
+
devicesParam.setLimits(devices)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == '__main__':
|
|
149
|
+
app = QApplication(sys.argv)
|
|
150
|
+
win = hidController()
|
|
151
|
+
win.show()
|
|
152
|
+
|
|
153
|
+
app.exec()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import hid
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class hidDevice:
|
|
5
|
+
def __init__(self, device: dict) -> None:
|
|
6
|
+
'''
|
|
7
|
+
Initialize a new hidDevice object.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
device : dict
|
|
12
|
+
A dictionary containing information about the HID device.
|
|
13
|
+
'''
|
|
14
|
+
self.data = device
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def vendorID(self):
|
|
18
|
+
'''
|
|
19
|
+
Get the vendor ID of the HID device.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
int or None
|
|
24
|
+
The vendor ID of the HID device, or None if it is not available.
|
|
25
|
+
'''
|
|
26
|
+
return self.data.get('vendor_id', None)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def productID(self):
|
|
30
|
+
'''
|
|
31
|
+
Get the product ID of the HID device.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
int or None
|
|
36
|
+
The product ID of the HID device, or None if it is not available.
|
|
37
|
+
'''
|
|
38
|
+
return self.data.get('product_id', None)
|
|
39
|
+
|
|
40
|
+
def getHID(self):
|
|
41
|
+
'''
|
|
42
|
+
Get a hid.device object for the HID device.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
hid.device
|
|
47
|
+
A hid.device object for the HID device.
|
|
48
|
+
'''
|
|
49
|
+
hid_device = hid.device()
|
|
50
|
+
hid_device.open(self.vendorID, self.productID)
|
|
51
|
+
hid_device.set_nonblocking(True)
|
|
52
|
+
return hid_device
|
|
53
|
+
|
|
54
|
+
def __str__(self) -> str:
|
|
55
|
+
'''
|
|
56
|
+
Get a string representation of the HID device.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
str
|
|
61
|
+
A string representation of the HID device.
|
|
62
|
+
'''
|
|
63
|
+
return self.data.get('product_string', 'N/A')
|