pyobs-gui 2.1.0__tar.gz → 2.3.0__tar.gz
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.
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/PKG-INFO +1 -1
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/__init__.py +1 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/acquisitionwidget.py +9 -3
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/autofocuswidget.py +5 -3
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/autoguidingwidget.py +6 -3
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/camerawidget.py +22 -6
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/datadisplaywidget.py +5 -3
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/mainwindow.py +16 -3
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/modewidget.py +9 -2
- pyobs_gui-2.3.0/pyobs_gui/qt/videograbwidget.ui +257 -0
- pyobs_gui-2.3.0/pyobs_gui/qt/videograbwidget_ui.py +146 -0
- pyobs_gui-2.3.0/pyobs_gui/qt/videowidget.ui +102 -0
- pyobs_gui-2.3.0/pyobs_gui/qt/videowidget_ui.py +101 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/roofwidget.py +2 -1
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/temperaturesplotwidget.py +8 -3
- pyobs_gui-2.3.0/pyobs_gui/videograbwidget.py +143 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/videowidget.py +7 -95
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/weatherwidget.py +4 -3
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyproject.toml +1 -1
- pyobs_gui-2.3.0/specs/2026-09-01-gui-video-widget-split.md +90 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/specs/index.md +4 -3
- pyobs_gui-2.3.0/tests/test_modewidget.py +72 -0
- pyobs_gui-2.3.0/tests/test_videograbwidget.py +38 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_videowidget.py +1 -34
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/uv.lock +1 -1
- pyobs_gui-2.1.0/pyobs_gui/qt/videowidget.ui +0 -1115
- pyobs_gui-2.1.0/pyobs_gui/qt/videowidget_ui.py +0 -354
- pyobs_gui-2.1.0/specs/2026-09-01-gui-video-widget-split.md +0 -107
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.claude/skills/verify/SKILL.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.github/dependabot.yml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.github/workflows/dependabot-automerge.yml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.github/workflows/pypi.yml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.github/workflows/pyrefly.yml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.github/workflows/ruff.yml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.gitignore +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.pre-commit-config.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/.readthedocs.yml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/CHANGELOG.rst +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/LICENSE +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/README.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/bin/mypyuic5 +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/docs/Makefile +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/docs/make.bat +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/docs/requirements.txt +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/docs/source/_static/pyobs.gif +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/docs/source/conf.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/docs/source/index.rst +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/mypy.ini +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/__main__.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/_nuitka_astropy_patch.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/accounts.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/base.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/commandinputwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/compassmovewidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/coolingwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/eventswidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/filterwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/fitsheaderswidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/focuswidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/gui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/login.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/loginwindow.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/logmodel.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/modifiedmixin.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/modulegui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/__init__.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/acquisitionwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/acquisitionwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/autofocuswidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/autofocuswidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/autoguidingwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/autoguidingwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/camerawidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/camerawidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/compassmoveplugin.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/compassmovewidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/compassmovewidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/compassmovewidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/compile.sh +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/coolingwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/coolingwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/datadisplayplugin.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/datadisplaywidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/datadisplaywidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/designer.sh +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/eventswidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/eventswidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/filterwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/filterwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/fitsheaderswidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/fitsheaderswidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/focuswidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/focuswidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/mainwindow.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/mainwindow_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/modewidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/modewidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/arrow-alt-circle-down-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/arrow-alt-circle-left-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/arrow-alt-circle-right-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/arrow-alt-circle-up-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/arrows-to-eye-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/chart-line-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/circle-question-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/cloud-sun-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/edit-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/search-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources/undo-solid.svg +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources.qrc +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/resources_rc.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/roboticwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/roboticwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/roofwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/roofwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/schedulewidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/schedulewidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/shellwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/shellwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/spectrographwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/spectrographwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/telescopewidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/telescopewidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/temperaturesplotwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/temperaturesplotwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/temperatureswidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/temperatureswidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/weatherwidget.ui +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/qt/weatherwidget_ui.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/roboticwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/schedulewidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/shellwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/spectrographwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/statuswidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/structuredconfigwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/telescopewidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/temperatureswidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/utils.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pyobs_gui/watchedlabel.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/pysidedeploy.spec +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/specs/2026-08-20-gui-remote-call-error-handling.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/specs/2026-08-21-gui-widget-startup-responsiveness.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/specs/2026-08-28-gui-main-vs-sidebar-widgets.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/specs/2026-08-28-structuredconfig-widget.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/specs/2026-08-31-irobotic-widgets.md +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/spike_standalone/main.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/spike_standalone/pysidedeploy.spec +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/acquisition.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/autofocus.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/camera.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/full.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/guiding.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/mode.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/robotic.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/roof.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/spectrograph.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/structuredconfig.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/telescope.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/telescope_acl.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/telescope_acl_denied.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/test/video.yaml +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/__init__.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/conftest.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_accounts.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_camerawidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_loginwindow.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_mainwindow_startup.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_multiwidget_pages.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_roboticwidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_schedulewidget.py +0 -0
- {pyobs_gui-2.1.0 → pyobs_gui-2.3.0}/tests/test_structuredconfigwidget.py +0 -0
|
@@ -17,6 +17,7 @@ from .datadisplaywidget import DataDisplayWidget
|
|
|
17
17
|
from .coolingwidget import CoolingWidget
|
|
18
18
|
from .shellwidget import ShellWidget
|
|
19
19
|
from .videowidget import VideoWidget
|
|
20
|
+
from .videograbwidget import VideoGrabWidget
|
|
20
21
|
from .focuswidget import FocusWidget
|
|
21
22
|
from .telescopewidget import TelescopeWidget
|
|
22
23
|
from .weatherwidget import WeatherWidget
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
from PySide6 import QtCore, QtWidgets # type: ignore
|
|
6
6
|
|
|
7
7
|
os.environ["QT_API"] = "PySide6"
|
|
8
|
-
from matplotlib import
|
|
8
|
+
from matplotlib.figure import Figure
|
|
9
9
|
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
|
10
10
|
from matplotlib.ticker import MaxNLocator
|
|
11
11
|
|
|
@@ -36,8 +36,14 @@ class AcquisitionWidget(BaseWidget, Ui_AcquisitionWidget):
|
|
|
36
36
|
self._running = False
|
|
37
37
|
self._result: AcquisitionResult | None = None
|
|
38
38
|
|
|
39
|
-
# add plots: distance vs. attempt, and the 2D offset trajectory
|
|
40
|
-
|
|
39
|
+
# add plots: distance vs. attempt, and the 2D offset trajectory -- built via the plain OO
|
|
40
|
+
# Figure API, not pyplot: pyplot.subplots() does one-time GUI-backend registration (and
|
|
41
|
+
# can pop up a throwaway Qt window) on its first call anywhere in the process,
|
|
42
|
+
# synchronously on whatever thread calls it. In this app that's the shared qasync loop,
|
|
43
|
+
# so the first widget of this kind to open in a session stalls the event loop for
|
|
44
|
+
# several seconds. Figure() sidesteps pyplot entirely.
|
|
45
|
+
self.figure = Figure()
|
|
46
|
+
self.ax, self.ax2 = self.figure.subplots(1, 2)
|
|
41
47
|
layout = QtWidgets.QVBoxLayout(self.framePlot)
|
|
42
48
|
self.canvas = FigureCanvas(self.figure)
|
|
43
49
|
layout.addWidget(self.canvas)
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
from PySide6 import QtCore, QtWidgets # type: ignore
|
|
6
6
|
|
|
7
7
|
os.environ["QT_API"] = "PySide6"
|
|
8
|
-
from matplotlib import
|
|
8
|
+
from matplotlib.figure import Figure
|
|
9
9
|
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
|
10
10
|
|
|
11
11
|
from pyobs.events import Event, FocusFoundEvent
|
|
@@ -28,8 +28,10 @@ class AutoFocusWidget(BaseWidget, Ui_AutoFocusWidget):
|
|
|
28
28
|
self._running = False
|
|
29
29
|
self._last_result: tuple[float, float] | None = None
|
|
30
30
|
|
|
31
|
-
# add plot
|
|
32
|
-
|
|
31
|
+
# add plot -- built via the plain OO Figure API, not pyplot: see comment in
|
|
32
|
+
# acquisitionwidget.py for why (avoids a several-second first-use event-loop stall)
|
|
33
|
+
self.figure = Figure()
|
|
34
|
+
self.ax = self.figure.add_subplot()
|
|
33
35
|
layout = QtWidgets.QVBoxLayout(self.framePlot)
|
|
34
36
|
self.canvas = FigureCanvas(self.figure)
|
|
35
37
|
layout.addWidget(self.canvas)
|
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
from PySide6 import QtCore, QtWidgets # type: ignore
|
|
8
8
|
|
|
9
9
|
os.environ["QT_API"] = "PySide6"
|
|
10
|
-
from matplotlib import
|
|
10
|
+
from matplotlib.figure import Figure
|
|
11
11
|
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
|
12
12
|
from matplotlib.ticker import MaxNLocator
|
|
13
13
|
|
|
@@ -37,8 +37,11 @@ class AutoGuidingWidget(BaseWidget, Ui_AutoGuidingWidget):
|
|
|
37
37
|
self._offset: tuple[float, float] | None = None # last (lon, lat), arcsec
|
|
38
38
|
self._offset_history: deque[tuple[float, float]] = deque(maxlen=_HISTORY_LENGTH)
|
|
39
39
|
|
|
40
|
-
# add plots: offset magnitude vs. sample, and the lon/lat scatter
|
|
41
|
-
|
|
40
|
+
# add plots: offset magnitude vs. sample, and the lon/lat scatter -- built via the plain
|
|
41
|
+
# OO Figure API, not pyplot: see comment in acquisitionwidget.py for why (avoids a
|
|
42
|
+
# several-second first-use event-loop stall)
|
|
43
|
+
self.figure = Figure()
|
|
44
|
+
self.ax, self.ax2 = self.figure.subplots(1, 2)
|
|
42
45
|
layout = QtWidgets.QVBoxLayout(self.framePlot)
|
|
43
46
|
self.canvas = FigureCanvas(self.figure)
|
|
44
47
|
layout.addWidget(self.canvas)
|
|
@@ -57,6 +57,15 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
57
57
|
BaseWidget.__init__(self, **kwargs)
|
|
58
58
|
self.setupUi(self) # type: ignore
|
|
59
59
|
|
|
60
|
+
# scrollArea's horizontal scrollbar is off (this panel should never need to scroll
|
|
61
|
+
# sideways), but with widgetResizable=True that leaves nothing to widen the scroll area
|
|
62
|
+
# itself to fit its content -- it was clipping the right edge of every group box
|
|
63
|
+
# (values/buttons ran past the visible border) because the .ui's Designer-time width hint
|
|
64
|
+
# (239px) is narrower than the controls' actual required width. Size it from the real
|
|
65
|
+
# content instead of a hardcoded guess; recomputed again at the end of _init() once
|
|
66
|
+
# capability-dependent combo boxes (binning, image format) have their real items.
|
|
67
|
+
self._size_scroll_area_to_content()
|
|
68
|
+
|
|
60
69
|
# variables
|
|
61
70
|
self.new_image = False
|
|
62
71
|
self.image_filename = None
|
|
@@ -130,9 +139,16 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
130
139
|
self._init_once("data_sequence", self._init_data_sequence),
|
|
131
140
|
)
|
|
132
141
|
|
|
142
|
+
# capability-dependent combo boxes (binning, image format) may have widened the panel
|
|
143
|
+
# further, now that they're populated
|
|
144
|
+
self._size_scroll_area_to_content()
|
|
145
|
+
|
|
133
146
|
# update GUI
|
|
134
147
|
self.signal_update_gui.emit()
|
|
135
148
|
|
|
149
|
+
def _size_scroll_area_to_content(self) -> None:
|
|
150
|
+
self.scrollArea.setMinimumWidth(self.scrollAreaWidgetContents.sizeHint().width())
|
|
151
|
+
|
|
136
152
|
async def _init_window(self) -> None:
|
|
137
153
|
# window
|
|
138
154
|
window_caps = await self.comm.get_capabilities(self.module, IWindow)
|
|
@@ -167,7 +183,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
167
183
|
state = await proxy.wait_for_state(IBinning, timeout=_WAIT_FOR_STATE_TIMEOUT)
|
|
168
184
|
if state is not None:
|
|
169
185
|
self.comboBinning.setCurrentText(f"{state.x}x{state.y}")
|
|
170
|
-
|
|
186
|
+
await self.comm.subscribe_state(self.module, IBinning, self._update_binning)
|
|
171
187
|
|
|
172
188
|
async def _init_gain(self) -> None:
|
|
173
189
|
# gain
|
|
@@ -177,7 +193,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
177
193
|
if state is not None:
|
|
178
194
|
self.spinGain.setValue(state.gain)
|
|
179
195
|
self.spinGainOffset.setValue(state.offset)
|
|
180
|
-
|
|
196
|
+
await self.comm.subscribe_state(self.module, IGain, self._update_gain)
|
|
181
197
|
|
|
182
198
|
async def _init_image_format(self) -> None:
|
|
183
199
|
# image format
|
|
@@ -191,7 +207,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
191
207
|
state = await proxy.wait_for_state(IImageFormat, timeout=_WAIT_FOR_STATE_TIMEOUT)
|
|
192
208
|
if state is not None:
|
|
193
209
|
self.comboImageFormat.setCurrentText(state.image_format.name)
|
|
194
|
-
|
|
210
|
+
await self.comm.subscribe_state(self.module, IImageFormat, self._update_image_format)
|
|
195
211
|
|
|
196
212
|
async def _init_image_type(self) -> None:
|
|
197
213
|
# image type
|
|
@@ -200,7 +216,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
200
216
|
state = await proxy.wait_for_state(IImageType, timeout=_WAIT_FOR_STATE_TIMEOUT)
|
|
201
217
|
if state is not None:
|
|
202
218
|
self.comboImageType.setCurrentText(state.image_type.name)
|
|
203
|
-
|
|
219
|
+
await self.comm.subscribe_state(self.module, IImageType, self._update_image_type)
|
|
204
220
|
|
|
205
221
|
async def _init_exposure(self) -> None:
|
|
206
222
|
# exposure (status, progress, time left)
|
|
@@ -211,7 +227,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
211
227
|
self.exposure_status = state.status
|
|
212
228
|
self.exposure_progress = state.progress
|
|
213
229
|
self.exposure_time_left = state.exposure_time_left
|
|
214
|
-
|
|
230
|
+
await self.comm.subscribe_state(self.module, IExposure, self._update_exposure)
|
|
215
231
|
|
|
216
232
|
async def _init_exposure_time(self) -> None:
|
|
217
233
|
# exposure time
|
|
@@ -220,7 +236,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
|
220
236
|
state = await proxy.wait_for_state(IExposureTime, timeout=_WAIT_FOR_STATE_TIMEOUT)
|
|
221
237
|
if state is not None:
|
|
222
238
|
self.spinExpTime.setValue(state.exposure_time)
|
|
223
|
-
|
|
239
|
+
await self.comm.subscribe_state(self.module, IExposureTime, self._update_exposure_time)
|
|
224
240
|
|
|
225
241
|
async def _init_data_sequence(self) -> None:
|
|
226
242
|
# data sequence
|
|
@@ -5,9 +5,9 @@ import os
|
|
|
5
5
|
from typing import Any, TYPE_CHECKING
|
|
6
6
|
import numpy as np
|
|
7
7
|
from PySide6 import QtWidgets, QtCore # type: ignore
|
|
8
|
-
from matplotlib import pyplot as plt
|
|
9
8
|
|
|
10
9
|
os.environ["QT_API"] = "PySide6"
|
|
10
|
+
from matplotlib.figure import Figure
|
|
11
11
|
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT # pyrefly: ignore [missing-module-attribute]
|
|
12
12
|
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
|
13
13
|
from qfitswidget import QFitsWidget # type: ignore
|
|
@@ -19,7 +19,6 @@ from .base import BaseWidget
|
|
|
19
19
|
from .qt.datadisplaywidget_ui import Ui_DataDisplayWidget
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
-
from matplotlib.figure import Figure
|
|
23
22
|
from matplotlib.axes import Axes
|
|
24
23
|
from astropy.io import fits
|
|
25
24
|
|
|
@@ -66,7 +65,10 @@ class DataDisplayWidget(BaseWidget, Ui_DataDisplayWidget):
|
|
|
66
65
|
self.imageLayout = QtWidgets.QVBoxLayout(self.tabImage)
|
|
67
66
|
if await self.comm.has_proxy(self.module, ISpectrograph):
|
|
68
67
|
self.is_spectrograph = True
|
|
69
|
-
|
|
68
|
+
# built via the plain OO Figure API, not pyplot: see comment in acquisitionwidget.py
|
|
69
|
+
# for why (avoids a several-second first-use event-loop stall)
|
|
70
|
+
self.figure = Figure()
|
|
71
|
+
self.ax = self.figure.add_subplot()
|
|
70
72
|
self.canvas = FigureCanvas(self.figure)
|
|
71
73
|
self.plotTools = NavigationToolbar2QT(self.canvas, self.tabImage)
|
|
72
74
|
self.imageLayout.addWidget(self.plotTools)
|
|
@@ -53,6 +53,7 @@ from .telescopewidget import TelescopeWidget
|
|
|
53
53
|
from .focuswidget import FocusWidget
|
|
54
54
|
from .temperatureswidget import TemperaturesWidget
|
|
55
55
|
from .weatherwidget import WeatherWidget
|
|
56
|
+
from .videograbwidget import VideoGrabWidget
|
|
56
57
|
from .videowidget import VideoWidget
|
|
57
58
|
from .qt.mainwindow_ui import Ui_MainWindow
|
|
58
59
|
from .logmodel import LogModel, LogModelProxy
|
|
@@ -83,8 +84,11 @@ class MainWidgetEntry:
|
|
|
83
84
|
# cross-interface demotion: only promoted into `main` when nothing else matched (D1)
|
|
84
85
|
sidebar_preferred: bool = False
|
|
85
86
|
# same-interface pairing: always rendered together whenever this entry survives into `main`
|
|
86
|
-
# (D6); no MAIN_WIDGETS entry sets this yet
|
|
87
|
-
#
|
|
87
|
+
# (D6); no MAIN_WIDGETS entry sets this yet. specs/2026-09-01-gui-video-widget-split.md's
|
|
88
|
+
# original plan was going to be the first consumer, but VideoWidget ended up split into two
|
|
89
|
+
# independent same-interface entries (see the two IVideo rows below) instead -- each tab is
|
|
90
|
+
# fully self-contained, with no state to share across a widget boundary, so this mechanism
|
|
91
|
+
# wasn't needed after all.
|
|
88
92
|
paired_sidebar_widget: Optional[Type[BaseWidget]] = None
|
|
89
93
|
|
|
90
94
|
|
|
@@ -105,7 +109,16 @@ MAIN_WIDGETS: List[MainWidgetEntry] = [
|
|
|
105
109
|
MainWidgetEntry(IAcquisition, AcquisitionWidget, "Acquisition", "mdi.target"),
|
|
106
110
|
MainWidgetEntry(IAutoGuiding, AutoGuidingWidget, "Auto guiding", "mdi.crosshairs-gps"),
|
|
107
111
|
MainWidgetEntry(IWeather, WeatherWidget, "Weather", "fa5s.cloud-sun"),
|
|
108
|
-
|
|
112
|
+
# two entries sharing one interface: collect_main_widgets() matches every entry whose
|
|
113
|
+
# interface the module implements (D2), so both render as separate tabs for any IVideo
|
|
114
|
+
# module -- live view and the FITS-grab controls, fully independent widgets/state
|
|
115
|
+
# (previously one VideoWidget combining both; see
|
|
116
|
+
# specs/2026-09-01-gui-video-widget-split.md). Note: a custom `widgets:` config entry with
|
|
117
|
+
# `interface: IVideo` can only ever replace the first of these two matches (mainwindow.py's
|
|
118
|
+
# replace-in-place lookup keys on interface name, not registry position) -- a known, unused
|
|
119
|
+
# edge case, not a blocker for this pair.
|
|
120
|
+
MainWidgetEntry(IVideo, VideoWidget, "Live View", "fa5s.video"),
|
|
121
|
+
MainWidgetEntry(IVideo, VideoGrabWidget, "FITS Image", "fa5s.image", sidebar=((None, FitsHeadersWidget),)),
|
|
109
122
|
MainWidgetEntry(ISpectrograph, SpectrographWidget, "Spectrograph", "ei.graph"),
|
|
110
123
|
MainWidgetEntry(IFilters, FilterWidget, "Filter wheel", "mdi.air-filter", sidebar_preferred=True),
|
|
111
124
|
MainWidgetEntry(ITemperatures, TemperaturesWidget, "Temperatures", "mdi.thermometer", sidebar_preferred=True),
|
|
@@ -24,6 +24,11 @@ class ModeWidget(BaseWidget, Ui_ModeWidget):
|
|
|
24
24
|
self._mode_options: dict[str, list[str]] = {}
|
|
25
25
|
self._modes: dict[str, str] = {}
|
|
26
26
|
self._motion_status = MotionStatus.UNKNOWN
|
|
27
|
+
# whether self.module implements IMotion at all -- an IMode module doesn't have to be a
|
|
28
|
+
# motion device (e.g. a plain camera's fiber-hole/video-path selector), and gating the
|
|
29
|
+
# edit button on motion status for one that isn't left it permanently disabled: the
|
|
30
|
+
# IMotion state subscription below never fires, so _motion_status never leaves UNKNOWN
|
|
31
|
+
self._has_motion = False
|
|
27
32
|
self._mode_widgets: dict[str, tuple[QtWidgets.QLineEdit, QtWidgets.QToolButton]] = {}
|
|
28
33
|
|
|
29
34
|
# connect signals
|
|
@@ -42,7 +47,9 @@ class ModeWidget(BaseWidget, Ui_ModeWidget):
|
|
|
42
47
|
await self.register_event(ModeChangedEvent, self._on_mode_changed)
|
|
43
48
|
|
|
44
49
|
async def _init(self) -> None:
|
|
45
|
-
await self.comm.
|
|
50
|
+
self._has_motion = await self.comm.has_proxy(self.module, IMotion)
|
|
51
|
+
if self._has_motion:
|
|
52
|
+
await self.comm.subscribe_state(self.module, IMotion, self._on_motion_state)
|
|
46
53
|
|
|
47
54
|
# permitted methods (ACLs)
|
|
48
55
|
await self._fetch_permitted_methods()
|
|
@@ -80,7 +87,7 @@ class ModeWidget(BaseWidget, Ui_ModeWidget):
|
|
|
80
87
|
def update_gui(self) -> None:
|
|
81
88
|
self.setEnabled(True)
|
|
82
89
|
self.textStatus.setText(self._motion_status.name)
|
|
83
|
-
initialized = self._motion_status in [
|
|
90
|
+
initialized = not self._has_motion or self._motion_status in [
|
|
84
91
|
MotionStatus.SLEWING,
|
|
85
92
|
MotionStatus.TRACKING,
|
|
86
93
|
MotionStatus.IDLE,
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<ui version="4.0">
|
|
3
|
+
<class>VideoGrabWidget</class>
|
|
4
|
+
<widget class="QWidget" name="VideoGrabWidget">
|
|
5
|
+
<property name="geometry">
|
|
6
|
+
<rect>
|
|
7
|
+
<x>0</x>
|
|
8
|
+
<y>0</y>
|
|
9
|
+
<width>618</width>
|
|
10
|
+
<height>530</height>
|
|
11
|
+
</rect>
|
|
12
|
+
</property>
|
|
13
|
+
<property name="windowTitle">
|
|
14
|
+
<string>Form</string>
|
|
15
|
+
</property>
|
|
16
|
+
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0">
|
|
17
|
+
<item>
|
|
18
|
+
<widget class="QFrame" name="frame_2">
|
|
19
|
+
<property name="frameShape">
|
|
20
|
+
<enum>QFrame::Shape::StyledPanel</enum>
|
|
21
|
+
</property>
|
|
22
|
+
<property name="frameShadow">
|
|
23
|
+
<enum>QFrame::Shadow::Raised</enum>
|
|
24
|
+
</property>
|
|
25
|
+
<layout class="QVBoxLayout" name="verticalLayout">
|
|
26
|
+
<item>
|
|
27
|
+
<widget class="QGroupBox" name="groupExposure_2">
|
|
28
|
+
<property name="title">
|
|
29
|
+
<string>Exposure</string>
|
|
30
|
+
</property>
|
|
31
|
+
<layout class="QGridLayout" name="gridLayout_5">
|
|
32
|
+
<property name="spacing">
|
|
33
|
+
<number>0</number>
|
|
34
|
+
</property>
|
|
35
|
+
<item row="0" column="0">
|
|
36
|
+
<widget class="QLabel" name="labelImageType">
|
|
37
|
+
<property name="text">
|
|
38
|
+
<string>Type:</string>
|
|
39
|
+
</property>
|
|
40
|
+
</widget>
|
|
41
|
+
</item>
|
|
42
|
+
<item row="1" column="0">
|
|
43
|
+
<widget class="QLabel" name="label_9">
|
|
44
|
+
<property name="text">
|
|
45
|
+
<string>Count:</string>
|
|
46
|
+
</property>
|
|
47
|
+
</widget>
|
|
48
|
+
</item>
|
|
49
|
+
<item row="2" column="1">
|
|
50
|
+
<widget class="QCheckBox" name="checkBroadcast">
|
|
51
|
+
<property name="text">
|
|
52
|
+
<string>Broadcast</string>
|
|
53
|
+
</property>
|
|
54
|
+
<property name="checked">
|
|
55
|
+
<bool>true</bool>
|
|
56
|
+
</property>
|
|
57
|
+
</widget>
|
|
58
|
+
</item>
|
|
59
|
+
<item row="3" column="0" colspan="2">
|
|
60
|
+
<widget class="QPushButton" name="buttonGrabImage">
|
|
61
|
+
<property name="palette">
|
|
62
|
+
<palette>
|
|
63
|
+
<active>
|
|
64
|
+
<colorrole role="Button">
|
|
65
|
+
<brush brushstyle="SolidPattern">
|
|
66
|
+
<color alpha="255">
|
|
67
|
+
<red>0</red>
|
|
68
|
+
<green>85</green>
|
|
69
|
+
<blue>0</blue>
|
|
70
|
+
</color>
|
|
71
|
+
</brush>
|
|
72
|
+
</colorrole>
|
|
73
|
+
<colorrole role="ButtonText">
|
|
74
|
+
<brush brushstyle="SolidPattern">
|
|
75
|
+
<color alpha="255">
|
|
76
|
+
<red>255</red>
|
|
77
|
+
<green>255</green>
|
|
78
|
+
<blue>255</blue>
|
|
79
|
+
</color>
|
|
80
|
+
</brush>
|
|
81
|
+
</colorrole>
|
|
82
|
+
</active>
|
|
83
|
+
<inactive>
|
|
84
|
+
<colorrole role="Button">
|
|
85
|
+
<brush brushstyle="SolidPattern">
|
|
86
|
+
<color alpha="255">
|
|
87
|
+
<red>0</red>
|
|
88
|
+
<green>85</green>
|
|
89
|
+
<blue>0</blue>
|
|
90
|
+
</color>
|
|
91
|
+
</brush>
|
|
92
|
+
</colorrole>
|
|
93
|
+
<colorrole role="ButtonText">
|
|
94
|
+
<brush brushstyle="SolidPattern">
|
|
95
|
+
<color alpha="255">
|
|
96
|
+
<red>255</red>
|
|
97
|
+
<green>255</green>
|
|
98
|
+
<blue>255</blue>
|
|
99
|
+
</color>
|
|
100
|
+
</brush>
|
|
101
|
+
</colorrole>
|
|
102
|
+
</inactive>
|
|
103
|
+
<disabled>
|
|
104
|
+
<colorrole role="Button">
|
|
105
|
+
<brush brushstyle="SolidPattern">
|
|
106
|
+
<color alpha="255">
|
|
107
|
+
<red>0</red>
|
|
108
|
+
<green>85</green>
|
|
109
|
+
<blue>0</blue>
|
|
110
|
+
</color>
|
|
111
|
+
</brush>
|
|
112
|
+
</colorrole>
|
|
113
|
+
<colorrole role="ButtonText">
|
|
114
|
+
<brush brushstyle="SolidPattern">
|
|
115
|
+
<color alpha="255">
|
|
116
|
+
<red>0</red>
|
|
117
|
+
<green>42</green>
|
|
118
|
+
<blue>0</blue>
|
|
119
|
+
</color>
|
|
120
|
+
</brush>
|
|
121
|
+
</colorrole>
|
|
122
|
+
</disabled>
|
|
123
|
+
</palette>
|
|
124
|
+
</property>
|
|
125
|
+
<property name="text">
|
|
126
|
+
<string>Grab image</string>
|
|
127
|
+
</property>
|
|
128
|
+
</widget>
|
|
129
|
+
</item>
|
|
130
|
+
<item row="1" column="1">
|
|
131
|
+
<widget class="QSpinBox" name="spinCount">
|
|
132
|
+
<property name="minimum">
|
|
133
|
+
<number>1</number>
|
|
134
|
+
</property>
|
|
135
|
+
<property name="maximum">
|
|
136
|
+
<number>9999</number>
|
|
137
|
+
</property>
|
|
138
|
+
</widget>
|
|
139
|
+
</item>
|
|
140
|
+
<item row="0" column="1">
|
|
141
|
+
<widget class="QComboBox" name="comboImageType"/>
|
|
142
|
+
</item>
|
|
143
|
+
<item row="4" column="0" colspan="2">
|
|
144
|
+
<widget class="QPushButton" name="buttonAbort">
|
|
145
|
+
<property name="palette">
|
|
146
|
+
<palette>
|
|
147
|
+
<active>
|
|
148
|
+
<colorrole role="Button">
|
|
149
|
+
<brush brushstyle="SolidPattern">
|
|
150
|
+
<color alpha="255">
|
|
151
|
+
<red>170</red>
|
|
152
|
+
<green>0</green>
|
|
153
|
+
<blue>0</blue>
|
|
154
|
+
</color>
|
|
155
|
+
</brush>
|
|
156
|
+
</colorrole>
|
|
157
|
+
<colorrole role="ButtonText">
|
|
158
|
+
<brush brushstyle="SolidPattern">
|
|
159
|
+
<color alpha="255">
|
|
160
|
+
<red>0</red>
|
|
161
|
+
<green>0</green>
|
|
162
|
+
<blue>0</blue>
|
|
163
|
+
</color>
|
|
164
|
+
</brush>
|
|
165
|
+
</colorrole>
|
|
166
|
+
</active>
|
|
167
|
+
<inactive>
|
|
168
|
+
<colorrole role="Button">
|
|
169
|
+
<brush brushstyle="SolidPattern">
|
|
170
|
+
<color alpha="255">
|
|
171
|
+
<red>170</red>
|
|
172
|
+
<green>0</green>
|
|
173
|
+
<blue>0</blue>
|
|
174
|
+
</color>
|
|
175
|
+
</brush>
|
|
176
|
+
</colorrole>
|
|
177
|
+
<colorrole role="ButtonText">
|
|
178
|
+
<brush brushstyle="SolidPattern">
|
|
179
|
+
<color alpha="255">
|
|
180
|
+
<red>0</red>
|
|
181
|
+
<green>0</green>
|
|
182
|
+
<blue>0</blue>
|
|
183
|
+
</color>
|
|
184
|
+
</brush>
|
|
185
|
+
</colorrole>
|
|
186
|
+
</inactive>
|
|
187
|
+
<disabled>
|
|
188
|
+
<colorrole role="Button">
|
|
189
|
+
<brush brushstyle="SolidPattern">
|
|
190
|
+
<color alpha="255">
|
|
191
|
+
<red>170</red>
|
|
192
|
+
<green>0</green>
|
|
193
|
+
<blue>0</blue>
|
|
194
|
+
</color>
|
|
195
|
+
</brush>
|
|
196
|
+
</colorrole>
|
|
197
|
+
<colorrole role="ButtonText">
|
|
198
|
+
<brush brushstyle="SolidPattern">
|
|
199
|
+
<color alpha="255">
|
|
200
|
+
<red>85</red>
|
|
201
|
+
<green>0</green>
|
|
202
|
+
<blue>0</blue>
|
|
203
|
+
</color>
|
|
204
|
+
</brush>
|
|
205
|
+
</colorrole>
|
|
206
|
+
</disabled>
|
|
207
|
+
</palette>
|
|
208
|
+
</property>
|
|
209
|
+
<property name="text">
|
|
210
|
+
<string>Abort sequence</string>
|
|
211
|
+
</property>
|
|
212
|
+
</widget>
|
|
213
|
+
</item>
|
|
214
|
+
</layout>
|
|
215
|
+
</widget>
|
|
216
|
+
</item>
|
|
217
|
+
<item>
|
|
218
|
+
<spacer name="verticalSpacer_2">
|
|
219
|
+
<property name="orientation">
|
|
220
|
+
<enum>Qt::Orientation::Vertical</enum>
|
|
221
|
+
</property>
|
|
222
|
+
<property name="sizeHint" stdset="0">
|
|
223
|
+
<size>
|
|
224
|
+
<width>20</width>
|
|
225
|
+
<height>40</height>
|
|
226
|
+
</size>
|
|
227
|
+
</property>
|
|
228
|
+
</spacer>
|
|
229
|
+
</item>
|
|
230
|
+
<item>
|
|
231
|
+
<widget class="QLabel" name="labelExposuresLeft">
|
|
232
|
+
<property name="text">
|
|
233
|
+
<string>IDLE</string>
|
|
234
|
+
</property>
|
|
235
|
+
<property name="alignment">
|
|
236
|
+
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
237
|
+
</property>
|
|
238
|
+
</widget>
|
|
239
|
+
</item>
|
|
240
|
+
</layout>
|
|
241
|
+
</widget>
|
|
242
|
+
</item>
|
|
243
|
+
<item>
|
|
244
|
+
<widget class="DataDisplayWidget" name="datadisplay" native="true"/>
|
|
245
|
+
</item>
|
|
246
|
+
</layout>
|
|
247
|
+
</widget>
|
|
248
|
+
<customwidgets>
|
|
249
|
+
<customwidget>
|
|
250
|
+
<class>DataDisplayWidget</class>
|
|
251
|
+
<extends>QWidget</extends>
|
|
252
|
+
<header>..datadisplaywidget</header>
|
|
253
|
+
</customwidget>
|
|
254
|
+
</customwidgets>
|
|
255
|
+
<resources/>
|
|
256
|
+
<connections/>
|
|
257
|
+
</ui>
|