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,706 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, Callable, Optional
|
|
3
|
+
|
|
4
|
+
from pyqtgraph.parametertree import Parameter
|
|
5
|
+
|
|
6
|
+
from microEye import __version__
|
|
7
|
+
from microEye.hardware.stages.elliptec.baseDevice import DeviceDirection
|
|
8
|
+
from microEye.hardware.stages.elliptec.deviceStatus import DeviceStatusValues
|
|
9
|
+
from microEye.hardware.stages.elliptec.ellDevices import *
|
|
10
|
+
from microEye.hardware.stages.elliptec.motorInfo import MotorInfo
|
|
11
|
+
from microEye.qt import QtCore, QtSerialPort, QtWidgets, Signal
|
|
12
|
+
from microEye.utils import Tree
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ElliptecStageParams(Enum):
|
|
16
|
+
MODEL = 'Controller Settings.Model'
|
|
17
|
+
|
|
18
|
+
# Serial Port Settings
|
|
19
|
+
SERIAL_PORT = 'Controller Settings.Serial Port'
|
|
20
|
+
PORT = 'Controller Settings.Serial Port.Port'
|
|
21
|
+
REFRESH = 'Controller Settings.Serial Port.Refresh Ports'
|
|
22
|
+
BAUDRATE = 'Controller Settings.Serial Port.Baudrate'
|
|
23
|
+
SET_PORT = 'Controller Settings.Serial Port.Set Config'
|
|
24
|
+
OPEN = 'Controller Settings.Serial Port.Connect'
|
|
25
|
+
CLOSE = 'Controller Settings.Serial Port.Disconnect'
|
|
26
|
+
PORT_STATE = 'Controller Settings.Serial Port.State'
|
|
27
|
+
|
|
28
|
+
# Stage Management
|
|
29
|
+
MIN_ADDRESS = 'Stage Management.Range (min)'
|
|
30
|
+
MAX_ADDRESS = 'Stage Management.Range (max)'
|
|
31
|
+
SCAN = 'Stage Management.Scan Devices'
|
|
32
|
+
|
|
33
|
+
# Devices
|
|
34
|
+
DEVICES = 'Connected Devices'
|
|
35
|
+
|
|
36
|
+
# Device Specific Params
|
|
37
|
+
HOME = 'Home'
|
|
38
|
+
HOME_DIRECTION = 'Homing Direction'
|
|
39
|
+
FORWARD = 'Forward'
|
|
40
|
+
BACKWARD = 'Backward'
|
|
41
|
+
STOP_JOG = 'Stop Jog'
|
|
42
|
+
SLOT = 'Slot'
|
|
43
|
+
SET_SLOT = 'Set Slot'
|
|
44
|
+
POSITION = 'Position'
|
|
45
|
+
SET_POSITION = 'Set Position'
|
|
46
|
+
STATUS = 'Status'
|
|
47
|
+
JOG_STEP_SIZE = 'Jog Step Size'
|
|
48
|
+
SET_JOG = 'Set Jog Step Size'
|
|
49
|
+
GET_JOG = 'Get Jog Step Size'
|
|
50
|
+
VELOCITY = 'Velocity'
|
|
51
|
+
REMOVE = 'Remove Device'
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
return self.value.split('.')[-1]
|
|
55
|
+
|
|
56
|
+
def get_path(self):
|
|
57
|
+
return self.value.split('.')
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ElliptecStageView(Tree):
|
|
61
|
+
PARAMS = ElliptecStageParams
|
|
62
|
+
removed = Signal(object)
|
|
63
|
+
|
|
64
|
+
# STAGE_TYPES = ['ELL6', 'ELL9', 'ELL14']
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self, controller: ELLDevices, parent: Optional['QtWidgets.QWidget'] = None
|
|
68
|
+
):
|
|
69
|
+
super().__init__(parent=parent)
|
|
70
|
+
|
|
71
|
+
if not isinstance(controller, ELLDevices):
|
|
72
|
+
raise ValueError('Invalid controller type!')
|
|
73
|
+
|
|
74
|
+
self.controller = controller
|
|
75
|
+
|
|
76
|
+
def create_parameters(self):
|
|
77
|
+
params = [
|
|
78
|
+
{
|
|
79
|
+
'name': 'Controller Settings',
|
|
80
|
+
'type': 'group',
|
|
81
|
+
'children': [
|
|
82
|
+
{
|
|
83
|
+
'name': str(ElliptecStageParams.MODEL),
|
|
84
|
+
'type': 'str',
|
|
85
|
+
'value': 'Elliptec Devices Controller',
|
|
86
|
+
'readonly': True,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
'name': str(ElliptecStageParams.SERIAL_PORT),
|
|
90
|
+
'type': 'group',
|
|
91
|
+
'expanded': False,
|
|
92
|
+
'children': [
|
|
93
|
+
{
|
|
94
|
+
'name': str(ElliptecStageParams.PORT),
|
|
95
|
+
'type': 'list',
|
|
96
|
+
'limits': self._get_available_ports(),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
'name': str(ElliptecStageParams.BAUDRATE),
|
|
100
|
+
'type': 'list',
|
|
101
|
+
'default': 9600,
|
|
102
|
+
'limits': [9600, 19200, 38400, 57600, 115200],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
'name': str(ElliptecStageParams.SET_PORT),
|
|
106
|
+
'type': 'action',
|
|
107
|
+
},
|
|
108
|
+
{'name': str(ElliptecStageParams.OPEN), 'type': 'action'},
|
|
109
|
+
{'name': str(ElliptecStageParams.CLOSE), 'type': 'action'},
|
|
110
|
+
{
|
|
111
|
+
'name': str(ElliptecStageParams.REFRESH),
|
|
112
|
+
'type': 'action',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
'name': str(ElliptecStageParams.PORT_STATE),
|
|
116
|
+
'type': 'str',
|
|
117
|
+
'value': 'closed',
|
|
118
|
+
'readonly': True,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
'name': 'Stage Management',
|
|
126
|
+
'type': 'group',
|
|
127
|
+
'children': [
|
|
128
|
+
{
|
|
129
|
+
'name': str(ElliptecStageParams.MIN_ADDRESS),
|
|
130
|
+
'type': 'list',
|
|
131
|
+
'default': '0',
|
|
132
|
+
'limits': EllDevice.VALID_ADDRESSES,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
'name': str(ElliptecStageParams.MAX_ADDRESS),
|
|
136
|
+
'type': 'list',
|
|
137
|
+
'default': 'F',
|
|
138
|
+
'limits': EllDevice.VALID_ADDRESSES,
|
|
139
|
+
},
|
|
140
|
+
{'name': str(ElliptecStageParams.SCAN), 'type': 'action'},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{'name': str(ElliptecStageParams.DEVICES), 'type': 'group', 'children': []},
|
|
144
|
+
# {'name': str(ElliptecStageParams.REMOVE), 'type': 'action'},
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
self.param_tree = Parameter.create(name='', type='group', children=params)
|
|
148
|
+
# self.param_tree.sigTreeStateChanged.connect(self.change)
|
|
149
|
+
self.header().setSectionResizeMode(
|
|
150
|
+
QtWidgets.QHeaderView.ResizeMode.ResizeToContents
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self.initializeSignals()
|
|
154
|
+
|
|
155
|
+
def initializeSignals(self):
|
|
156
|
+
self.get_param(ElliptecStageParams.SET_PORT).sigActivated.connect(
|
|
157
|
+
lambda: self.setConfig()
|
|
158
|
+
)
|
|
159
|
+
self.get_param(ElliptecStageParams.OPEN).sigActivated.connect(
|
|
160
|
+
lambda: self.open()
|
|
161
|
+
)
|
|
162
|
+
self.get_param(ElliptecStageParams.CLOSE).sigActivated.connect(
|
|
163
|
+
lambda: DevicePort.close()
|
|
164
|
+
)
|
|
165
|
+
self.get_param(ElliptecStageParams.REFRESH).sigActivated.connect(
|
|
166
|
+
lambda: self._refresh_ports()
|
|
167
|
+
)
|
|
168
|
+
self.get_param(ElliptecStageParams.SCAN).sigActivated.connect(
|
|
169
|
+
lambda: self.findDevices()
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def isOpen(self):
|
|
173
|
+
return DevicePort.isOpen()
|
|
174
|
+
|
|
175
|
+
def setConfig(self):
|
|
176
|
+
if not DevicePort.isOpen():
|
|
177
|
+
DevicePort.setPortName(self.get_param_value(ElliptecStageParams.PORT))
|
|
178
|
+
DevicePort.setBaudRate(self.get_param_value(ElliptecStageParams.BAUDRATE))
|
|
179
|
+
|
|
180
|
+
def setPortName(self, name: str):
|
|
181
|
+
DevicePort.setPortName(name)
|
|
182
|
+
|
|
183
|
+
def setBaudRate(self, baudRate: int):
|
|
184
|
+
DevicePort.setBaudRate(baudRate)
|
|
185
|
+
|
|
186
|
+
def open(self):
|
|
187
|
+
res = DevicePort.open()
|
|
188
|
+
|
|
189
|
+
if res:
|
|
190
|
+
self.set_expanded(ElliptecStageParams.SERIAL_PORT)
|
|
191
|
+
|
|
192
|
+
def findDevices(self):
|
|
193
|
+
if not DevicePort.isOpen():
|
|
194
|
+
print('Open the serial port first!')
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
self.get_param(ElliptecStageParams.DEVICES).clearChildren()
|
|
198
|
+
|
|
199
|
+
min_address = self._get_valid_address(ElliptecStageParams.MIN_ADDRESS, '0')
|
|
200
|
+
max_address = self._get_valid_address(ElliptecStageParams.MAX_ADDRESS, 'F')
|
|
201
|
+
|
|
202
|
+
devices = self.controller.scanAddresses(min_address, max_address)
|
|
203
|
+
for device in devices:
|
|
204
|
+
# Configure each device found
|
|
205
|
+
if self.controller.configure(device):
|
|
206
|
+
self.configureDeviceParams(self.controller.addressedDevice(device[0]))
|
|
207
|
+
|
|
208
|
+
def configureDeviceParams(self, device: EllDevice):
|
|
209
|
+
if device.deviceInfo.DeviceType in [
|
|
210
|
+
DeviceID.DeviceTypes.Shutter2,
|
|
211
|
+
DeviceID.DeviceTypes.Shutter4,
|
|
212
|
+
DeviceID.DeviceTypes.Shutter6,
|
|
213
|
+
]:
|
|
214
|
+
self.buildShutterParams(device)
|
|
215
|
+
elif device.deviceInfo.DeviceType in [
|
|
216
|
+
DeviceID.DeviceTypes.OpticsRotator,
|
|
217
|
+
DeviceID.DeviceTypes.LinearStage25mm,
|
|
218
|
+
DeviceID.DeviceTypes.LinearStage28mm,
|
|
219
|
+
DeviceID.DeviceTypes.LinearStage60mm,
|
|
220
|
+
DeviceID.DeviceTypes.LinearStage60mm_10,
|
|
221
|
+
]:
|
|
222
|
+
self.buildStageParams(device)
|
|
223
|
+
|
|
224
|
+
def buildShutterParams(self, device: EllDevice):
|
|
225
|
+
# Implement logic to add device params
|
|
226
|
+
params = {
|
|
227
|
+
'name': f'ELL{device.deviceInfo.DeviceType.value} - {device.address}',
|
|
228
|
+
'type': 'group',
|
|
229
|
+
'children': [
|
|
230
|
+
{'name': str(ElliptecStageParams.HOME), 'type': 'action'},
|
|
231
|
+
{'name': str(ElliptecStageParams.FORWARD), 'type': 'action'},
|
|
232
|
+
{'name': str(ElliptecStageParams.BACKWARD), 'type': 'action'},
|
|
233
|
+
{
|
|
234
|
+
'name': str(ElliptecStageParams.POSITION),
|
|
235
|
+
'type': 'float',
|
|
236
|
+
'value': 0.0,
|
|
237
|
+
'readonly': True,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
'name': str(ElliptecStageParams.STATUS),
|
|
241
|
+
'type': 'str',
|
|
242
|
+
'value': device.deviceStatus.name,
|
|
243
|
+
'readonly': True,
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if device.deviceInfo.DeviceType in [
|
|
249
|
+
DeviceID.DeviceTypes.Shutter4,
|
|
250
|
+
DeviceID.DeviceTypes.Shutter6,
|
|
251
|
+
] and isinstance(params['children'], list):
|
|
252
|
+
params['children'].extend(
|
|
253
|
+
[
|
|
254
|
+
{
|
|
255
|
+
'name': str(ElliptecStageParams.SLOT),
|
|
256
|
+
'type': 'int',
|
|
257
|
+
'default': 0,
|
|
258
|
+
'limits': [
|
|
259
|
+
0,
|
|
260
|
+
3
|
|
261
|
+
if device.deviceInfo.DeviceType
|
|
262
|
+
== DeviceID.DeviceTypes.Shutter4
|
|
263
|
+
else 5,
|
|
264
|
+
],
|
|
265
|
+
},
|
|
266
|
+
{'name': str(ElliptecStageParams.SET_SLOT), 'type': 'action'},
|
|
267
|
+
]
|
|
268
|
+
)
|
|
269
|
+
self.add_param_child(ElliptecStageParams.DEVICES, params)
|
|
270
|
+
|
|
271
|
+
self.connectDeviceSignals(device)
|
|
272
|
+
|
|
273
|
+
def buildStageParams(self, device: EllDevice):
|
|
274
|
+
# Implement logic to add device params
|
|
275
|
+
params = {
|
|
276
|
+
'name': f'ELL{device.deviceInfo.DeviceType.value} - {device.address}',
|
|
277
|
+
'type': 'group',
|
|
278
|
+
'children': [],
|
|
279
|
+
}
|
|
280
|
+
if device.deviceInfo.DeviceType not in [
|
|
281
|
+
DeviceID.DeviceTypes.OpticsRotator,
|
|
282
|
+
DeviceID.DeviceTypes.LinearStage25mm,
|
|
283
|
+
DeviceID.DeviceTypes.LinearStage28mm,
|
|
284
|
+
DeviceID.DeviceTypes.LinearStage60mm,
|
|
285
|
+
DeviceID.DeviceTypes.LinearStage60mm_10,
|
|
286
|
+
]:
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
device.get_position()
|
|
290
|
+
device.get_jogstep_size()
|
|
291
|
+
|
|
292
|
+
params['children'] = [
|
|
293
|
+
{'name': str(ElliptecStageParams.HOME), 'type': 'action'},
|
|
294
|
+
{'name': str(ElliptecStageParams.FORWARD), 'type': 'action'},
|
|
295
|
+
{'name': str(ElliptecStageParams.BACKWARD), 'type': 'action'},
|
|
296
|
+
{
|
|
297
|
+
'name': str(ElliptecStageParams.POSITION),
|
|
298
|
+
'type': 'float',
|
|
299
|
+
'value': device.position,
|
|
300
|
+
'suffix': device.deviceInfo.Units,
|
|
301
|
+
},
|
|
302
|
+
{'name': str(ElliptecStageParams.SET_POSITION), 'type': 'action'},
|
|
303
|
+
{
|
|
304
|
+
'name': str(ElliptecStageParams.STATUS),
|
|
305
|
+
'type': 'str',
|
|
306
|
+
'value': device.deviceStatus.name,
|
|
307
|
+
'readonly': True,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
'name': str(ElliptecStageParams.JOG_STEP_SIZE),
|
|
311
|
+
'type': 'float',
|
|
312
|
+
'value': device.jogstep_size,
|
|
313
|
+
'limits': [0.0, device.deviceInfo.Travel],
|
|
314
|
+
'suffix': device.deviceInfo.Units,
|
|
315
|
+
},
|
|
316
|
+
{'name': str(ElliptecStageParams.GET_JOG), 'type': 'action'},
|
|
317
|
+
{'name': str(ElliptecStageParams.SET_JOG), 'type': 'action'},
|
|
318
|
+
{'name': str(ElliptecStageParams.STOP_JOG), 'type': 'action'},
|
|
319
|
+
{
|
|
320
|
+
'name': str(ElliptecStageParams.HOME_DIRECTION),
|
|
321
|
+
'type': 'list',
|
|
322
|
+
'default': 'Clockwise',
|
|
323
|
+
'limits': [
|
|
324
|
+
'Clockwise',
|
|
325
|
+
'AntiClockwise',
|
|
326
|
+
],
|
|
327
|
+
'visible': device.deviceInfo.DeviceType
|
|
328
|
+
in [
|
|
329
|
+
DeviceID.DeviceTypes.OpticsRotator,
|
|
330
|
+
DeviceID.DeviceTypes.RotaryStage8,
|
|
331
|
+
DeviceID.DeviceTypes.RotaryStage18,
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
]
|
|
335
|
+
self.add_param_child(ElliptecStageParams.DEVICES, params)
|
|
336
|
+
|
|
337
|
+
self.connectDeviceSignals(device)
|
|
338
|
+
|
|
339
|
+
def connectParamSignal(self, paramPath: str, callback: Callable):
|
|
340
|
+
param_obj = self.get_param(paramPath)
|
|
341
|
+
if param_obj is not None:
|
|
342
|
+
param_obj.sigActivated.connect(callback)
|
|
343
|
+
return True
|
|
344
|
+
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
def connectDeviceSignals(self, device: EllDevice):
|
|
348
|
+
address = device.address
|
|
349
|
+
dev_type = device.deviceInfo.DeviceType
|
|
350
|
+
stage_path = (
|
|
351
|
+
f'{str(ElliptecStageParams.DEVICES)}.ELL{dev_type.value} - {address}'
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
self.connectParamSignal(
|
|
355
|
+
f'{stage_path}.{str(ElliptecStageParams.FORWARD)}',
|
|
356
|
+
lambda: device.jog_forward(),
|
|
357
|
+
)
|
|
358
|
+
self.connectParamSignal(
|
|
359
|
+
f'{stage_path}.{str(ElliptecStageParams.BACKWARD)}',
|
|
360
|
+
lambda: device.jog_backward(),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
if dev_type in [
|
|
364
|
+
DeviceID.DeviceTypes.Shutter2,
|
|
365
|
+
DeviceID.DeviceTypes.Shutter4,
|
|
366
|
+
DeviceID.DeviceTypes.Shutter6,
|
|
367
|
+
]:
|
|
368
|
+
self.connectParamSignal(
|
|
369
|
+
f'{stage_path}.{str(ElliptecStageParams.HOME)}',
|
|
370
|
+
lambda: device.home(),
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
self.connectParamSignal(
|
|
374
|
+
f'{stage_path}.{str(ElliptecStageParams.SET_SLOT)}',
|
|
375
|
+
lambda: device.move_absolute(
|
|
376
|
+
self.get_param_value(
|
|
377
|
+
f'{stage_path}.{str(ElliptecStageParams.SLOT)}'
|
|
378
|
+
)
|
|
379
|
+
* SHUTTER_POSITION_FACTOR[dev_type],
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
elif dev_type in [
|
|
383
|
+
DeviceID.DeviceTypes.OpticsRotator,
|
|
384
|
+
DeviceID.DeviceTypes.LinearStage25mm,
|
|
385
|
+
DeviceID.DeviceTypes.LinearStage28mm,
|
|
386
|
+
DeviceID.DeviceTypes.LinearStage60mm,
|
|
387
|
+
DeviceID.DeviceTypes.LinearStage60mm_10,
|
|
388
|
+
]:
|
|
389
|
+
self.connectParamSignal(
|
|
390
|
+
f'{stage_path}.{str(ElliptecStageParams.STOP_JOG)}',
|
|
391
|
+
lambda: device.jog_stop(),
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
self.connectParamSignal(
|
|
395
|
+
f'{stage_path}.{str(ElliptecStageParams.SET_POSITION)}',
|
|
396
|
+
lambda: device.move_absolute(
|
|
397
|
+
self.get_param_value(
|
|
398
|
+
f'{stage_path}.{str(ElliptecStageParams.POSITION)}'
|
|
399
|
+
)
|
|
400
|
+
),
|
|
401
|
+
)
|
|
402
|
+
self.connectParamSignal(
|
|
403
|
+
f'{stage_path}.{str(ElliptecStageParams.HOME)}',
|
|
404
|
+
lambda: device.home(
|
|
405
|
+
DeviceDirection[
|
|
406
|
+
self.get_param_value(
|
|
407
|
+
f'{stage_path}.{str(ElliptecStageParams.HOME_DIRECTION)}'
|
|
408
|
+
)
|
|
409
|
+
]
|
|
410
|
+
),
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
self.connectParamSignal(
|
|
414
|
+
f'{stage_path}.{str(ElliptecStageParams.GET_JOG)}',
|
|
415
|
+
lambda: device.get_jogstep_size(),
|
|
416
|
+
)
|
|
417
|
+
self.connectParamSignal(
|
|
418
|
+
f'{stage_path}.{str(ElliptecStageParams.SET_JOG)}',
|
|
419
|
+
lambda: device.set_jogstep_size(
|
|
420
|
+
self.get_param_value(
|
|
421
|
+
f'{stage_path}.{str(ElliptecStageParams.JOG_STEP_SIZE)}'
|
|
422
|
+
)
|
|
423
|
+
),
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
def remove_widget(self):
|
|
427
|
+
if self.parent() and not DevicePort.isOpen():
|
|
428
|
+
self.parent().layout().removeWidget(self)
|
|
429
|
+
self.removed.emit(self)
|
|
430
|
+
self.deleteLater()
|
|
431
|
+
else:
|
|
432
|
+
print(f'Disconnect Elliptec stage before removing!')
|
|
433
|
+
|
|
434
|
+
def __str__(self):
|
|
435
|
+
return f'Elliptec Devices ({DevicePort.instance().serial.portName()})'
|
|
436
|
+
|
|
437
|
+
@staticmethod
|
|
438
|
+
def _get_available_ports():
|
|
439
|
+
return [
|
|
440
|
+
info.portName() for info in QtSerialPort.QSerialPortInfo.availablePorts()
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
def _refresh_ports(self):
|
|
444
|
+
self.get_param(ElliptecStageParams.PORT).setLimits(self._get_available_ports())
|
|
445
|
+
|
|
446
|
+
def _get_valid_address(self, param: ElliptecStageParams, default: Any):
|
|
447
|
+
address = self.get_param_value(param)
|
|
448
|
+
return address if EllDevice.is_valid_address(address) else default
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class ElliptecView(QtWidgets.QTabWidget):
|
|
452
|
+
HEADER = '> <span style="color:#0f0;">Elliptec Driver ('
|
|
453
|
+
HEADER += f'<span style="color:#aaf;">microEye v{__version__}</span>)</span>'
|
|
454
|
+
|
|
455
|
+
def __init__(
|
|
456
|
+
self,
|
|
457
|
+
elleptic: Optional[ELLDevices] = None,
|
|
458
|
+
parent: Optional[QtWidgets.QWidget] = None,
|
|
459
|
+
) -> None:
|
|
460
|
+
super().__init__(parent)
|
|
461
|
+
|
|
462
|
+
self.initUI(elleptic)
|
|
463
|
+
|
|
464
|
+
def initUI(self, elleptic: Optional[ELLDevices] = None):
|
|
465
|
+
'''
|
|
466
|
+
Initializes the ElliptecView UI.
|
|
467
|
+
'''
|
|
468
|
+
|
|
469
|
+
# Set window properties
|
|
470
|
+
self.setWindowTitle('Elliptec Driver')
|
|
471
|
+
|
|
472
|
+
# Create the output terminal
|
|
473
|
+
self._output = QtWidgets.QPlainTextEdit()
|
|
474
|
+
self._output.setReadOnly(True)
|
|
475
|
+
self._output.appendHtml(self.HEADER)
|
|
476
|
+
self._output.setStyleSheet('QPlainTextEdit { background-color: #111;}')
|
|
477
|
+
|
|
478
|
+
# Create the ELLDevices controller
|
|
479
|
+
if elleptic:
|
|
480
|
+
self._ellDevices = elleptic
|
|
481
|
+
else:
|
|
482
|
+
self._ellDevices = ELLDevices()
|
|
483
|
+
self._ellDevices.MessageUpdates.outputUpdated.connect(self.handleOutputUpdate)
|
|
484
|
+
self._ellDevices.MessageUpdates.parameterUpdated.connect(
|
|
485
|
+
self.handleParameterUpdate
|
|
486
|
+
)
|
|
487
|
+
DevicePort.instance().dataSent.connect(self.handleDataSent)
|
|
488
|
+
DevicePort.instance().dataReceived.connect(self.handleDataReceived)
|
|
489
|
+
|
|
490
|
+
# Create the devices tab
|
|
491
|
+
self._elliptecStageView = ElliptecStageView(controller=self._ellDevices)
|
|
492
|
+
|
|
493
|
+
# Set up the layout
|
|
494
|
+
self.addTab(self._elliptecStageView, 'Devices')
|
|
495
|
+
self.addTab(self._output, 'Output')
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def isOpen(self):
|
|
499
|
+
return DevicePort.isOpen()
|
|
500
|
+
|
|
501
|
+
def portName(self):
|
|
502
|
+
return DevicePort.instance().serial.portName()
|
|
503
|
+
|
|
504
|
+
def setPortName(self, name: str):
|
|
505
|
+
DevicePort.setPortName(name)
|
|
506
|
+
|
|
507
|
+
def baudRate(self):
|
|
508
|
+
return DevicePort.instance().serial.baudRate()
|
|
509
|
+
|
|
510
|
+
def setBaudRate(self, baudRate: int):
|
|
511
|
+
DevicePort.setBaudRate(baudRate)
|
|
512
|
+
|
|
513
|
+
def open(self):
|
|
514
|
+
DevicePort.open()
|
|
515
|
+
|
|
516
|
+
def closePort(self):
|
|
517
|
+
DevicePort.close()
|
|
518
|
+
|
|
519
|
+
def updateHighlight(self):
|
|
520
|
+
'''
|
|
521
|
+
Updates the highlight style of the "Connect" action.
|
|
522
|
+
'''
|
|
523
|
+
style = ''
|
|
524
|
+
if self.isOpen():
|
|
525
|
+
style = 'background-color: #004CB6'
|
|
526
|
+
else:
|
|
527
|
+
style = 'background-color: black'
|
|
528
|
+
|
|
529
|
+
next(
|
|
530
|
+
self._elliptecStageView.get_param(ElliptecStageParams.OPEN).items.keys()
|
|
531
|
+
).button.setStyleSheet(style)
|
|
532
|
+
|
|
533
|
+
def clearTerminal(self):
|
|
534
|
+
'''
|
|
535
|
+
Clears the terminal.
|
|
536
|
+
'''
|
|
537
|
+
self._output.clear()
|
|
538
|
+
self._output.appendHtml(self.HEADER)
|
|
539
|
+
|
|
540
|
+
def handleDataSent(self, message: str):
|
|
541
|
+
'''
|
|
542
|
+
Handles the data sent event.
|
|
543
|
+
'''
|
|
544
|
+
self._output.appendHtml(f'<span style="color: cyan;">Tx: {message}</span>')
|
|
545
|
+
|
|
546
|
+
def handleDataReceived(self, message: str):
|
|
547
|
+
'''
|
|
548
|
+
Handles the data received event.
|
|
549
|
+
'''
|
|
550
|
+
self._output.appendHtml(f'<span style="color: green;">Rx: {message}</span>')
|
|
551
|
+
|
|
552
|
+
def handleOutputUpdate(self, messages: list[str], error: bool):
|
|
553
|
+
'''
|
|
554
|
+
Handles the output update event.
|
|
555
|
+
'''
|
|
556
|
+
for message in messages:
|
|
557
|
+
if 'Scanning for devices' in message:
|
|
558
|
+
self.clearTerminal()
|
|
559
|
+
|
|
560
|
+
self._output.appendHtml(
|
|
561
|
+
f'<span style="color: yellow;">Output-> {message}</span>'
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
def handleParameterUpdate(
|
|
565
|
+
self, update_type: MessageUpdater.UpdateTypes, address: str, data: Any
|
|
566
|
+
):
|
|
567
|
+
if update_type == MessageUpdater.UpdateTypes.DeviceInfo and isinstance(
|
|
568
|
+
data, DeviceID
|
|
569
|
+
):
|
|
570
|
+
# if data and address in _device_view_models:
|
|
571
|
+
# _device_view_models[address].description = data.description()
|
|
572
|
+
pass
|
|
573
|
+
elif update_type == MessageUpdater.UpdateTypes.MotorInfo and isinstance(
|
|
574
|
+
data, MotorInfo
|
|
575
|
+
):
|
|
576
|
+
# if data and address in _device_view_models:
|
|
577
|
+
# motor_view_model = next(
|
|
578
|
+
# (
|
|
579
|
+
# motor
|
|
580
|
+
# for motor in _device_view_models[address].motors
|
|
581
|
+
# if motor.motor_id == data.motor_id
|
|
582
|
+
# ),
|
|
583
|
+
# None,
|
|
584
|
+
# )
|
|
585
|
+
# if motor_view_model:
|
|
586
|
+
# motor_view_model.update_info(data)
|
|
587
|
+
pass
|
|
588
|
+
elif update_type == MessageUpdater.UpdateTypes.Status and isinstance(
|
|
589
|
+
data, DeviceStatusValues
|
|
590
|
+
):
|
|
591
|
+
if data:
|
|
592
|
+
device = self._ellDevices.addressedDevice(address)
|
|
593
|
+
stage_path = (
|
|
594
|
+
f'{str(ElliptecStageParams.DEVICES)}.'
|
|
595
|
+
f'ELL{device.deviceInfo.DeviceType.value} - {address}'
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
param = self._elliptecStageView.get_param(
|
|
599
|
+
f'{stage_path}.{str(ElliptecStageParams.STATUS)}'
|
|
600
|
+
)
|
|
601
|
+
if param:
|
|
602
|
+
param.setValue(data.name)
|
|
603
|
+
|
|
604
|
+
if data not in [DeviceStatusValues.OK, DeviceStatusValues.Busy]:
|
|
605
|
+
self._output.appendHtml(
|
|
606
|
+
f'<span style="color: red;">'
|
|
607
|
+
f'Device #{address} error: {data.name}</span>'
|
|
608
|
+
)
|
|
609
|
+
elif update_type == MessageUpdater.UpdateTypes.Position and isinstance(
|
|
610
|
+
data, (float, int)
|
|
611
|
+
):
|
|
612
|
+
try:
|
|
613
|
+
device = self._ellDevices.addressedDevice(address)
|
|
614
|
+
stage_path = (
|
|
615
|
+
f'{str(ElliptecStageParams.DEVICES)}.'
|
|
616
|
+
f'ELL{device.deviceInfo.DeviceType.value} - {address}'
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
position = float(data)
|
|
620
|
+
param = self._elliptecStageView.get_param(
|
|
621
|
+
f'{stage_path}.{str(ElliptecStageParams.POSITION)}'
|
|
622
|
+
)
|
|
623
|
+
if param:
|
|
624
|
+
param.setValue(position)
|
|
625
|
+
if device.deviceInfo.DeviceType in [
|
|
626
|
+
DeviceID.DeviceTypes.Shutter4,
|
|
627
|
+
DeviceID.DeviceTypes.Shutter6,
|
|
628
|
+
]:
|
|
629
|
+
n = (
|
|
630
|
+
4
|
|
631
|
+
if device.deviceInfo.DeviceType == DeviceID.DeviceTypes.Shutter4
|
|
632
|
+
else 6
|
|
633
|
+
)
|
|
634
|
+
increment = device.deviceInfo.Travel / (n - 1)
|
|
635
|
+
slot = position // increment
|
|
636
|
+
param = self._elliptecStageView.get_param(
|
|
637
|
+
f'{stage_path}.{str(ElliptecStageParams.SLOT)}'
|
|
638
|
+
)
|
|
639
|
+
if param:
|
|
640
|
+
param.setValue(slot)
|
|
641
|
+
except Exception as e:
|
|
642
|
+
self._output.appendHtml(
|
|
643
|
+
f'<span style="color: red;">Device #{address} error: {e}</span>'
|
|
644
|
+
)
|
|
645
|
+
elif update_type in [
|
|
646
|
+
MessageUpdater.UpdateTypes.PolarizerPositions,
|
|
647
|
+
MessageUpdater.UpdateTypes.PaddlePosition,
|
|
648
|
+
]:
|
|
649
|
+
pass
|
|
650
|
+
elif update_type == MessageUpdater.UpdateTypes.HomeOffset and isinstance(
|
|
651
|
+
data, (float, int)
|
|
652
|
+
):
|
|
653
|
+
try:
|
|
654
|
+
home_offset = float(data)
|
|
655
|
+
# if address in _device_view_models:
|
|
656
|
+
# _device_view_models[address].update_home_offset(home_offset)
|
|
657
|
+
except Exception as e:
|
|
658
|
+
self._output.appendHtml(
|
|
659
|
+
f'<span style="color: red;">Device #{address} error: {e}</span>'
|
|
660
|
+
)
|
|
661
|
+
elif update_type == MessageUpdater.UpdateTypes.JogstepSize and isinstance(
|
|
662
|
+
data, (float, int)
|
|
663
|
+
):
|
|
664
|
+
try:
|
|
665
|
+
device = self._ellDevices.addressedDevice(address)
|
|
666
|
+
stage_path = (
|
|
667
|
+
f'{str(ElliptecStageParams.DEVICES)}.'
|
|
668
|
+
f'ELL{device.deviceInfo.DeviceType.value} - {address}'
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
jog_step = float(data)
|
|
672
|
+
param = self._elliptecStageView.get_param(
|
|
673
|
+
f'{stage_path}.{str(ElliptecStageParams.JOG_STEP_SIZE)}'
|
|
674
|
+
)
|
|
675
|
+
if param:
|
|
676
|
+
param.setValue(jog_step)
|
|
677
|
+
# if address in _device_view_models:
|
|
678
|
+
# _device_view_models[address].update_jogstep_size(jog_step)
|
|
679
|
+
except Exception as e:
|
|
680
|
+
self._output.appendHtml(
|
|
681
|
+
f'<span style="color: red;">Device #{address} error: {e}</span>'
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
def __str__(self):
|
|
685
|
+
return 'Elliptec Devices'
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
if __name__ == '__main__':
|
|
689
|
+
import sys
|
|
690
|
+
|
|
691
|
+
from microEye.qt import QApplication
|
|
692
|
+
|
|
693
|
+
try:
|
|
694
|
+
print('Starting application...')
|
|
695
|
+
app = QApplication(sys.argv)
|
|
696
|
+
|
|
697
|
+
print('Creating ElliptecView...')
|
|
698
|
+
view = ElliptecView()
|
|
699
|
+
|
|
700
|
+
print('Showing window...')
|
|
701
|
+
view.show()
|
|
702
|
+
|
|
703
|
+
print('Entering event loop...')
|
|
704
|
+
sys.exit(app.exec())
|
|
705
|
+
except Exception as e:
|
|
706
|
+
print(f'An error occurred: {e}')
|