pyobs-gui 1.8.7__tar.gz → 2.0.0.dev1__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.0.0.dev1/.github/workflows/pyrefly.yml +23 -0
- pyobs_gui-2.0.0.dev1/.github/workflows/ruff.yml +23 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/.pre-commit-config.yaml +10 -3
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/PKG-INFO +4 -2
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/base.py +24 -34
- pyobs_gui-2.0.0.dev1/pyobs_gui/camerawidget.py +307 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/commandinputwidget.py +1 -1
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/compassmovewidget.py +24 -13
- pyobs_gui-2.0.0.dev1/pyobs_gui/coolingwidget.py +55 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/datadisplaywidget.py +21 -23
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/eventswidget.py +9 -7
- pyobs_gui-2.0.0.dev1/pyobs_gui/filterwidget.py +69 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/fitsheaderswidget.py +1 -1
- pyobs_gui-2.0.0.dev1/pyobs_gui/focuswidget.py +91 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/gui.py +1 -1
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/logmodel.py +1 -1
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/mainwindow.py +30 -32
- pyobs_gui-2.0.0.dev1/pyobs_gui/modewidget.py +119 -0
- pyobs_gui-2.0.0.dev1/pyobs_gui/modifiedmixin.py +86 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/modulegui.py +10 -8
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/camerawidget.ui +165 -98
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/camerawidget_ui.py +125 -80
- pyobs_gui-2.0.0.dev1/pyobs_gui/roofwidget.py +69 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/shellwidget.py +6 -10
- pyobs_gui-2.0.0.dev1/pyobs_gui/spectrographwidget.py +88 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/statuswidget.py +45 -110
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/telescopewidget.py +170 -269
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/temperaturesplotwidget.py +5 -7
- pyobs_gui-2.0.0.dev1/pyobs_gui/temperatureswidget.py +67 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/videowidget.py +77 -43
- pyobs_gui-2.0.0.dev1/pyobs_gui/watchedlabel.py +22 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/weatherwidget.py +1 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyproject.toml +34 -3
- pyobs_gui-2.0.0.dev1/test/camera.yaml +32 -0
- pyobs_gui-2.0.0.dev1/test/full.yaml +66 -0
- pyobs_gui-2.0.0.dev1/test/mode.yaml +18 -0
- pyobs_gui-2.0.0.dev1/test/roof.yaml +18 -0
- pyobs_gui-2.0.0.dev1/test/spectrograph.yaml +18 -0
- pyobs_gui-2.0.0.dev1/test/telescope.yaml +23 -0
- pyobs_gui-2.0.0.dev1/test/video.yaml +31 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/uv.lock +94 -51
- pyobs_gui-1.8.7/.flake8 +0 -12
- pyobs_gui-1.8.7/pyobs_gui/camerawidget.py +0 -378
- pyobs_gui-1.8.7/pyobs_gui/coolingwidget.py +0 -74
- pyobs_gui-1.8.7/pyobs_gui/filterwidget.py +0 -124
- pyobs_gui-1.8.7/pyobs_gui/focuswidget.py +0 -147
- pyobs_gui-1.8.7/pyobs_gui/modewidget.py +0 -160
- pyobs_gui-1.8.7/pyobs_gui/roofwidget.py +0 -80
- pyobs_gui-1.8.7/pyobs_gui/spectrographwidget.py +0 -163
- pyobs_gui-1.8.7/pyobs_gui/temperatureswidget.py +0 -80
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/.github/workflows/pypi.yml +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/.readthedocs.yml +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/CHANGELOG.rst +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/LICENSE +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/README.md +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/bin/mypyuic5 +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/docs/Makefile +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/docs/make.bat +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/docs/requirements.txt +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/docs/source/_static/pyobs.gif +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/docs/source/conf.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/docs/source/index.rst +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/mypy.ini +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/__init__.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/__init__.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/compassmoveplugin.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/compassmovewidget.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/compassmovewidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/compassmovewidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/compile.sh +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/coolingwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/coolingwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/datadisplayplugin.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/datadisplaywidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/datadisplaywidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/designer.sh +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/eventswidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/eventswidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/filterwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/filterwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/fitsheaderswidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/fitsheaderswidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/focuswidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/focuswidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/mainwindow.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/mainwindow_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/modewidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/modewidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/arrow-alt-circle-down-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/arrow-alt-circle-left-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/arrow-alt-circle-right-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/arrow-alt-circle-up-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/arrows-to-eye-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/chart-line-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/circle-question-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/cloud-sun-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/edit-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/search-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources/undo-solid.svg +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources.qrc +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/resources_rc.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/roofwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/roofwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/shellwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/shellwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/spectrographwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/spectrographwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/telescopewidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/telescopewidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/temperaturesplotwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/temperaturesplotwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/temperatureswidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/temperatureswidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/videowidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/videowidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/weatherwidget.ui +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/qt/weatherwidget_ui.py +0 -0
- {pyobs_gui-1.8.7 → pyobs_gui-2.0.0.dev1}/pyobs_gui/utils.py +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: pyrefly
|
|
2
|
+
on: push
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
pyrefly:
|
|
6
|
+
runs-on: ubuntu-latest
|
|
7
|
+
timeout-minutes: 10
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- name: Check out repository code
|
|
11
|
+
uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- name: Set up Python
|
|
14
|
+
uses: astral-sh/setup-uv@v5
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
python-version: "3.13"
|
|
18
|
+
|
|
19
|
+
- name: Install packages
|
|
20
|
+
run: uv sync --all-extras --dev
|
|
21
|
+
|
|
22
|
+
- name: Run pyrefly
|
|
23
|
+
run: uv run pyrefly check
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: ruff
|
|
2
|
+
on: push
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
ruff:
|
|
6
|
+
runs-on: ubuntu-latest
|
|
7
|
+
timeout-minutes: 10
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- name: Check out repository code
|
|
11
|
+
uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- name: Set up Python
|
|
14
|
+
uses: astral-sh/setup-uv@v5
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
python-version: "3.13"
|
|
18
|
+
|
|
19
|
+
- name: Install packages
|
|
20
|
+
run: uv sync --all-extras --dev
|
|
21
|
+
|
|
22
|
+
- name: Run ruff
|
|
23
|
+
run: uv run ruff check pyobs_gui/
|
|
@@ -9,7 +9,14 @@ repos:
|
|
|
9
9
|
# pre-commit's default_language_version, see
|
|
10
10
|
# https://pre-commit.com/#top_level-default_language_version
|
|
11
11
|
language_version: python3.11
|
|
12
|
-
- repo: https://github.com/
|
|
13
|
-
rev:
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.11.0
|
|
14
14
|
hooks:
|
|
15
|
-
|
|
15
|
+
- id: ruff
|
|
16
|
+
- repo: local
|
|
17
|
+
hooks:
|
|
18
|
+
- id: pyrefly
|
|
19
|
+
name: pyrefly
|
|
20
|
+
entry: uv run pyrefly check
|
|
21
|
+
language: system
|
|
22
|
+
pass_filenames: false
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyobs-gui
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0.dev1
|
|
4
4
|
Summary: A remote GUI for pyobs
|
|
5
5
|
Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Python: <3.14,>=3.11
|
|
9
|
+
Requires-Dist: astroplan>=0.10.1
|
|
10
|
+
Requires-Dist: astropy<8,>=7.0.1
|
|
9
11
|
Requires-Dist: colour<0.2,>=0.1.5
|
|
10
12
|
Requires-Dist: matplotlib<4,>=3.10.1
|
|
11
|
-
Requires-Dist: pyobs-core<2
|
|
13
|
+
Requires-Dist: pyobs-core<3,>=2.0.0.dev6
|
|
12
14
|
Requires-Dist: pyside6>=6.10.1
|
|
13
15
|
Requires-Dist: qasync<0.28,>=0.27.1
|
|
14
16
|
Requires-Dist: qfitswidget>=0.13.1
|
|
@@ -2,28 +2,31 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
-
from
|
|
6
|
-
from typing import Any, TypeVar, Callable, Type
|
|
7
|
-
from PySide6 import QtWidgets, QtGui, QtCore # type: ignore
|
|
8
|
-
from astroplan import Observer
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable, Type, TypeVar, overload
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
from pyobs.interfaces import IModule
|
|
7
|
+
import pyobs.utils.exceptions as exc
|
|
12
8
|
from pyobs.object import create_object
|
|
13
9
|
from pyobs.utils.enums import ModuleState
|
|
14
|
-
from
|
|
15
|
-
|
|
10
|
+
from PySide6 import QtCore, QtGui, QtWidgets # type: ignore
|
|
11
|
+
|
|
16
12
|
from .utils import QAsyncMessageBox
|
|
17
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Coroutine
|
|
16
|
+
|
|
17
|
+
from astroplan import Observer
|
|
18
|
+
from pyobs.comm import Comm
|
|
19
|
+
from pyobs.vfs import VirtualFileSystem
|
|
20
|
+
|
|
18
21
|
log = logging.getLogger(__name__)
|
|
19
22
|
|
|
20
|
-
WidgetClass = TypeVar("WidgetClass")
|
|
23
|
+
WidgetClass = TypeVar("WidgetClass", bound="BaseWidget")
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
class BaseWindow:
|
|
24
27
|
def __init__(self) -> None:
|
|
25
28
|
"""Base class for MainWindow and all widgets."""
|
|
26
|
-
self.modules: list[
|
|
29
|
+
self.modules: list[str] = []
|
|
27
30
|
self._comm: Comm | None = None
|
|
28
31
|
self.observer: Observer | None = None
|
|
29
32
|
self.vfs: VirtualFileSystem | dict[str, Any] | None = None
|
|
@@ -36,29 +39,11 @@ class BaseWindow:
|
|
|
36
39
|
return self._comm
|
|
37
40
|
|
|
38
41
|
@property
|
|
39
|
-
def module(self) ->
|
|
42
|
+
def module(self) -> str:
|
|
40
43
|
"""Returns the first module in the list or None, if list is empty"""
|
|
41
44
|
return self.modules[0]
|
|
42
45
|
|
|
43
|
-
def
|
|
44
|
-
"""Return the module with the given name or None, if not exists.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
name: Name of module to return.
|
|
48
|
-
|
|
49
|
-
Returns:
|
|
50
|
-
Module or None.
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
# loop all modules and check name
|
|
54
|
-
for module in self.modules:
|
|
55
|
-
if module.name == name:
|
|
56
|
-
return module
|
|
57
|
-
|
|
58
|
-
# nothing found
|
|
59
|
-
return None
|
|
60
|
-
|
|
61
|
-
def modules_by_interface(self, interface: Any) -> list[Proxy]:
|
|
46
|
+
def modules_by_interface(self, interface: Any) -> list[str]:
|
|
62
47
|
"""Returns all modules that implement the given interface.
|
|
63
48
|
|
|
64
49
|
Args:
|
|
@@ -69,7 +54,7 @@ class BaseWindow:
|
|
|
69
54
|
"""
|
|
70
55
|
return list(filter(lambda m: isinstance(m, interface), self.modules))
|
|
71
56
|
|
|
72
|
-
def module_by_interface(self, interface: Any) ->
|
|
57
|
+
def module_by_interface(self, interface: Any) -> str | None:
|
|
73
58
|
"""Returns first modules that implement the given interface, or None, if no exist.
|
|
74
59
|
|
|
75
60
|
Args:
|
|
@@ -81,6 +66,10 @@ class BaseWindow:
|
|
|
81
66
|
modules = self.modules_by_interface(interface)
|
|
82
67
|
return None if len(modules) == 0 else modules[0]
|
|
83
68
|
|
|
69
|
+
@overload
|
|
70
|
+
def create_widget(self, config: type[WidgetClass], **kwargs: Any) -> WidgetClass: ...
|
|
71
|
+
@overload
|
|
72
|
+
def create_widget(self, config: dict[str, Any], **kwargs: Any) -> "BaseWidget": ...
|
|
84
73
|
def create_widget(self, config: dict[str, Any] | type, **kwargs: Any) -> "BaseWidget":
|
|
85
74
|
"""Creates new widget.
|
|
86
75
|
|
|
@@ -108,7 +97,7 @@ class BaseWindow:
|
|
|
108
97
|
|
|
109
98
|
async def open(
|
|
110
99
|
self,
|
|
111
|
-
modules: list[
|
|
100
|
+
modules: list[str] | None = None,
|
|
112
101
|
comm: Comm | None = None,
|
|
113
102
|
observer: Observer | None = None,
|
|
114
103
|
vfs: VirtualFileSystem | dict[str, Any] | None = None,
|
|
@@ -247,8 +236,9 @@ class BaseWidget(BaseWindow, QtWidgets.QWidget): # type: ignore
|
|
|
247
236
|
try:
|
|
248
237
|
# get module state
|
|
249
238
|
module = self.module
|
|
250
|
-
|
|
251
|
-
|
|
239
|
+
client_state = self.comm.get_client_state(module)
|
|
240
|
+
if client_state is not None:
|
|
241
|
+
state, _ = client_state
|
|
252
242
|
self.setEnabled(state == ModuleState.READY)
|
|
253
243
|
if state != ModuleState.READY:
|
|
254
244
|
return
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
import qasync # type: ignore
|
|
4
|
+
from PySide6 import QtWidgets, QtCore # type: ignore
|
|
5
|
+
from astroplan import Observer
|
|
6
|
+
|
|
7
|
+
from pyobs.comm import Comm
|
|
8
|
+
from pyobs.events import NewImageEvent
|
|
9
|
+
from pyobs.interfaces import (
|
|
10
|
+
IAbortable,
|
|
11
|
+
IExposureTime,
|
|
12
|
+
ExposureTimeState,
|
|
13
|
+
IImageType,
|
|
14
|
+
ImageTypeState,
|
|
15
|
+
IImageFormat,
|
|
16
|
+
ImageFormatState,
|
|
17
|
+
IBinning,
|
|
18
|
+
BinningState,
|
|
19
|
+
IWindow,
|
|
20
|
+
WindowState,
|
|
21
|
+
IFilters,
|
|
22
|
+
ICooling,
|
|
23
|
+
ITemperatures,
|
|
24
|
+
IGain,
|
|
25
|
+
GainState,
|
|
26
|
+
IExposure,
|
|
27
|
+
ExposureState,
|
|
28
|
+
)
|
|
29
|
+
from pyobs.utils.enums import ImageType, ImageFormat, ExposureStatus
|
|
30
|
+
from pyobs.vfs import VirtualFileSystem
|
|
31
|
+
from .base import BaseWidget
|
|
32
|
+
from .coolingwidget import CoolingWidget
|
|
33
|
+
from .filterwidget import FilterWidget
|
|
34
|
+
from .temperatureswidget import TemperaturesWidget
|
|
35
|
+
from .fitsheaderswidget import FitsHeadersWidget
|
|
36
|
+
from .qt.camerawidget_ui import Ui_CameraWidget
|
|
37
|
+
|
|
38
|
+
log = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CameraWidget(BaseWidget, Ui_CameraWidget):
|
|
42
|
+
signal_update_gui = QtCore.Signal()
|
|
43
|
+
signal_new_image = QtCore.Signal(NewImageEvent, str)
|
|
44
|
+
|
|
45
|
+
def __init__(self, **kwargs: Any):
|
|
46
|
+
BaseWidget.__init__(self, **kwargs)
|
|
47
|
+
self.setupUi(self) # type: ignore
|
|
48
|
+
|
|
49
|
+
# variables
|
|
50
|
+
self.new_image = False
|
|
51
|
+
self.image_filename = None
|
|
52
|
+
self.image = None
|
|
53
|
+
self.status = None
|
|
54
|
+
self.exposure_status = ExposureStatus.IDLE
|
|
55
|
+
self.exposures_left = 0
|
|
56
|
+
self.exposure_time_left = 0.0
|
|
57
|
+
self.exposure_progress = 0.0
|
|
58
|
+
|
|
59
|
+
self.spinWindowLeft.init_modified(self.labelWindowLeft).committed.connect(self._window_changed)
|
|
60
|
+
self.spinWindowTop.init_modified(self.labelWindowTop).committed.connect(self._window_changed)
|
|
61
|
+
self.spinWindowWidth.init_modified(self.labelWindowWidth).committed.connect(self._window_changed)
|
|
62
|
+
self.spinWindowHeight.init_modified(self.labelWindowHeight).committed.connect(self._window_changed)
|
|
63
|
+
self.spinGain.init_modified(self.labelGain).committed.connect(self._gain_changed)
|
|
64
|
+
self.spinGainOffset.init_modified(self.labelGain).committed.connect(self._gain_changed)
|
|
65
|
+
|
|
66
|
+
async def open(
|
|
67
|
+
self,
|
|
68
|
+
modules: list[str] | None = None,
|
|
69
|
+
comm: Comm | None = None,
|
|
70
|
+
observer: Observer | None = None,
|
|
71
|
+
vfs: VirtualFileSystem | dict[str, Any] | None = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
"""Open module."""
|
|
74
|
+
await BaseWidget.open(self, modules=modules, comm=comm, observer=observer, vfs=vfs)
|
|
75
|
+
await self.datadisplay.open(modules=modules, comm=comm, observer=observer, vfs=vfs)
|
|
76
|
+
|
|
77
|
+
# set exposure types
|
|
78
|
+
image_types = sorted([it.name for it in ImageType])
|
|
79
|
+
self.comboImageType.addItems(image_types)
|
|
80
|
+
|
|
81
|
+
# before first update, disable mys
|
|
82
|
+
self.setEnabled(False)
|
|
83
|
+
|
|
84
|
+
# hide groups, if necessary
|
|
85
|
+
self.groupWindowing.setVisible(await self.comm.has_proxy(self.module, IWindow))
|
|
86
|
+
self.groupBinning.setVisible(await self.comm.has_proxy(self.module, IBinning))
|
|
87
|
+
self.groupImageFormat.setVisible(await self.comm.has_proxy(self.module, IImageFormat))
|
|
88
|
+
self.groupExpTime.setVisible(await self.comm.has_proxy(self.module, IExposureTime))
|
|
89
|
+
self.groupGain.setVisible(await self.comm.has_proxy(self.module, IGain))
|
|
90
|
+
|
|
91
|
+
# and single controls
|
|
92
|
+
self.labelImageType.setVisible(await self.comm.has_proxy(self.module, IImageType))
|
|
93
|
+
self.comboImageType.setVisible(await self.comm.has_proxy(self.module, IImageType))
|
|
94
|
+
self.butAbort.setVisible(await self.comm.has_proxy(self.module, IAbortable))
|
|
95
|
+
|
|
96
|
+
# initial values
|
|
97
|
+
self.comboImageType.setCurrentIndex(image_types.index("OBJECT"))
|
|
98
|
+
|
|
99
|
+
# connect signals
|
|
100
|
+
self.signal_update_gui.connect(self.update_gui)
|
|
101
|
+
self.butFullFrame.clicked.connect(self.set_full_frame)
|
|
102
|
+
self.comboBinning.currentTextChanged.connect(self.set_full_frame)
|
|
103
|
+
self.checkBroadcast.stateChanged.connect(self.broadcast_changed)
|
|
104
|
+
self.comboImageType.currentTextChanged.connect(self.image_type_changed)
|
|
105
|
+
self.butExpose.clicked.connect(self.expose)
|
|
106
|
+
self.butAbort.clicked.connect(self.abort)
|
|
107
|
+
|
|
108
|
+
# fill sidebar
|
|
109
|
+
await self.add_to_sidebar(self.create_widget(FitsHeadersWidget, module=self.module))
|
|
110
|
+
if await self.comm.has_proxy(self.module, IFilters):
|
|
111
|
+
await self.add_to_sidebar(self.create_widget(FilterWidget, module=self.module))
|
|
112
|
+
if await self.comm.has_proxy(self.module, ICooling):
|
|
113
|
+
await self.add_to_sidebar(self.create_widget(CoolingWidget, module=self.module))
|
|
114
|
+
if await self.comm.has_proxy(self.module, ITemperatures):
|
|
115
|
+
await self.add_to_sidebar(self.create_widget(TemperaturesWidget, module=self.module))
|
|
116
|
+
|
|
117
|
+
async def _init(self) -> None:
|
|
118
|
+
# window
|
|
119
|
+
async with self.comm.safe_proxy(self.module, IWindow) as proxy:
|
|
120
|
+
if proxy is not None:
|
|
121
|
+
await self.comm.subscribe_state(self.module, IWindow, self._update_window)
|
|
122
|
+
await self.set_full_frame()
|
|
123
|
+
|
|
124
|
+
# binning
|
|
125
|
+
binning_caps = await self.comm.get_capabilities(self.module, IBinning)
|
|
126
|
+
if binning_caps is not None:
|
|
127
|
+
binnings = [f"{b.x}x{b.y}" for b in binning_caps.binnings]
|
|
128
|
+
self.comboBinning.clear()
|
|
129
|
+
self.comboBinning.addItems(binnings)
|
|
130
|
+
await self.comm.subscribe_state(self.module, IBinning, self._update_binning)
|
|
131
|
+
|
|
132
|
+
# gain — initial values delivered by subscription callback
|
|
133
|
+
if await self.comm.has_proxy(self.module, IGain):
|
|
134
|
+
await self.comm.subscribe_state(self.module, IGain, self._update_gain)
|
|
135
|
+
|
|
136
|
+
# image format
|
|
137
|
+
image_format_caps = await self.comm.get_capabilities(self.module, IImageFormat)
|
|
138
|
+
if image_format_caps is not None:
|
|
139
|
+
image_formats = [ImageFormat(f) for f in image_format_caps.image_formats]
|
|
140
|
+
self.comboImageFormat.clear()
|
|
141
|
+
self.comboImageFormat.addItems([f.name for f in image_formats])
|
|
142
|
+
await self.comm.subscribe_state(self.module, IImageFormat, self._update_image_format)
|
|
143
|
+
|
|
144
|
+
async with self.comm.safe_proxy(self.module, IImageType) as proxy:
|
|
145
|
+
if proxy is not None:
|
|
146
|
+
await self.comm.subscribe_state(self.module, IImageType, self._update_image_type)
|
|
147
|
+
|
|
148
|
+
async with self.comm.safe_proxy(self.module, IExposure) as proxy:
|
|
149
|
+
if proxy is not None:
|
|
150
|
+
await self.comm.subscribe_state(self.module, IExposure, self._update_exposure)
|
|
151
|
+
|
|
152
|
+
async with self.comm.safe_proxy(self.module, IExposureTime) as proxy:
|
|
153
|
+
if proxy is not None:
|
|
154
|
+
await self.comm.subscribe_state(self.module, IExposureTime, self._update_exposure_time)
|
|
155
|
+
|
|
156
|
+
# update GUI
|
|
157
|
+
self.signal_update_gui.emit()
|
|
158
|
+
|
|
159
|
+
def _update_window(self, state: WindowState):
|
|
160
|
+
self.labelWindowLeft.setText(str(state.x))
|
|
161
|
+
self.labelWindowTop.setText(str(state.y))
|
|
162
|
+
self.labelWindowWidth.setText(str(state.width))
|
|
163
|
+
self.labelWindowHeight.setText(str(state.height))
|
|
164
|
+
|
|
165
|
+
def _update_binning(self, state: BinningState):
|
|
166
|
+
self.comboBinning.setCurrentText(f"{state.x}x{state.y}")
|
|
167
|
+
|
|
168
|
+
def _update_gain(self, state: GainState):
|
|
169
|
+
self.labelGain.setText(str(state.gain))
|
|
170
|
+
self.labelGainOffset.setText(str(state.offset))
|
|
171
|
+
|
|
172
|
+
def _update_image_format(self, state: ImageFormatState):
|
|
173
|
+
self.comboImageFormat.setCurrentText(state.image_format.name)
|
|
174
|
+
|
|
175
|
+
def _update_image_type(self, state: ImageTypeState):
|
|
176
|
+
self.comboImageType.setCurrentText(state.image_type.name)
|
|
177
|
+
|
|
178
|
+
def _update_exposure(self, state: ExposureState):
|
|
179
|
+
self.exposure_status = state.status
|
|
180
|
+
self.exposure_progress = state.progress
|
|
181
|
+
self.exposure_time_left = state.exposure_time_left
|
|
182
|
+
self.update_gui()
|
|
183
|
+
|
|
184
|
+
def _update_exposure_time(self, state: ExposureTimeState):
|
|
185
|
+
self.spinExpTime.setValue(state.exposure_time)
|
|
186
|
+
self.update_gui()
|
|
187
|
+
|
|
188
|
+
@qasync.asyncSlot()
|
|
189
|
+
async def _window_changed(self):
|
|
190
|
+
print("ok")
|
|
191
|
+
|
|
192
|
+
@qasync.asyncSlot()
|
|
193
|
+
async def _gain_changed(self):
|
|
194
|
+
print("ok")
|
|
195
|
+
|
|
196
|
+
@qasync.asyncSlot() # type: ignore
|
|
197
|
+
async def set_full_frame(self) -> None:
|
|
198
|
+
caps = await self.comm.get_capabilities(self.module, IWindow)
|
|
199
|
+
if caps is not None:
|
|
200
|
+
# get binning
|
|
201
|
+
binning = 1
|
|
202
|
+
if self.comboBinning.count() > 0 and await self.comm.has_proxy(self.module, IBinning):
|
|
203
|
+
binning = int(self.comboBinning.currentText()[0])
|
|
204
|
+
|
|
205
|
+
# max values
|
|
206
|
+
self.spinWindowLeft.setMaximum(int(caps.full_frame_width / binning))
|
|
207
|
+
self.spinWindowTop.setMaximum(int(caps.full_frame_height / binning))
|
|
208
|
+
self.spinWindowWidth.setMaximum(int(caps.full_frame_width / binning))
|
|
209
|
+
self.spinWindowHeight.setMaximum(int(caps.full_frame_height / binning))
|
|
210
|
+
|
|
211
|
+
# set it
|
|
212
|
+
self.spinWindowLeft.setValue(caps.full_frame_x)
|
|
213
|
+
self.spinWindowTop.setValue(caps.full_frame_y)
|
|
214
|
+
self.spinWindowWidth.setValue(int(caps.full_frame_width / binning))
|
|
215
|
+
self.spinWindowHeight.setValue(int(caps.full_frame_height / binning))
|
|
216
|
+
|
|
217
|
+
@QtCore.Slot(int) # type: ignore
|
|
218
|
+
def broadcast_changed(self, state: int) -> None:
|
|
219
|
+
if state == 0:
|
|
220
|
+
r = QtWidgets.QMessageBox.question(
|
|
221
|
+
self,
|
|
222
|
+
"pyobs",
|
|
223
|
+
"When disabling the broadcast, new images will not processed (and "
|
|
224
|
+
"saved) within the pyobs network. Continue?",
|
|
225
|
+
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
|
|
226
|
+
QtWidgets.QMessageBox.StandardButton.No,
|
|
227
|
+
)
|
|
228
|
+
if r == QtWidgets.QMessageBox.StandardButton.No:
|
|
229
|
+
self.checkBroadcast.setChecked(True)
|
|
230
|
+
|
|
231
|
+
@QtCore.Slot(str) # type: ignore
|
|
232
|
+
def image_type_changed(self, image_type: str) -> None:
|
|
233
|
+
if image_type == "BIAS":
|
|
234
|
+
self.spinExpTime.setValue(0)
|
|
235
|
+
self.spinExpTime.setEnabled(False)
|
|
236
|
+
else:
|
|
237
|
+
self.spinExpTime.setEnabled(True)
|
|
238
|
+
|
|
239
|
+
@qasync.asyncSlot() # type: ignore
|
|
240
|
+
async def expose(self) -> None:
|
|
241
|
+
# set initial image count
|
|
242
|
+
self.exposures_left = self.spinCount.value()
|
|
243
|
+
|
|
244
|
+
# do exposure(s)
|
|
245
|
+
while self.exposures_left > 0:
|
|
246
|
+
# expose
|
|
247
|
+
broadcast = self.checkBroadcast.isChecked()
|
|
248
|
+
await self.datadisplay.grab_data(broadcast)
|
|
249
|
+
|
|
250
|
+
# decrement number of exposures left
|
|
251
|
+
self.exposures_left -= 1
|
|
252
|
+
|
|
253
|
+
# signal GUI update
|
|
254
|
+
self.signal_update_gui.emit()
|
|
255
|
+
|
|
256
|
+
@qasync.asyncSlot() # type: ignore
|
|
257
|
+
async def abort(self) -> None:
|
|
258
|
+
"""Abort exposure."""
|
|
259
|
+
# do we have a running exposure?
|
|
260
|
+
if self.exposures_left == 0:
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
# got exposures left?
|
|
264
|
+
if self.exposures_left > 1:
|
|
265
|
+
# abort sequence
|
|
266
|
+
self.exposures_left = 0
|
|
267
|
+
else:
|
|
268
|
+
async with self.comm.safe_proxy(self.module, IAbortable) as proxy:
|
|
269
|
+
if proxy is not None:
|
|
270
|
+
await proxy.abort()
|
|
271
|
+
|
|
272
|
+
def update_gui(self) -> None:
|
|
273
|
+
"""Update the GUI."""
|
|
274
|
+
|
|
275
|
+
# enable myself
|
|
276
|
+
self.setEnabled(True)
|
|
277
|
+
|
|
278
|
+
# enable/disable buttons
|
|
279
|
+
self.butExpose.setEnabled(self.exposure_status == ExposureStatus.IDLE)
|
|
280
|
+
self.butAbort.setEnabled(self.exposure_status != ExposureStatus.IDLE)
|
|
281
|
+
|
|
282
|
+
# set abort text
|
|
283
|
+
if self.exposures_left > 1:
|
|
284
|
+
self.butAbort.setText("Abort sequence")
|
|
285
|
+
else:
|
|
286
|
+
self.butAbort.setText("Abort exposure")
|
|
287
|
+
|
|
288
|
+
# set progress
|
|
289
|
+
msg = ""
|
|
290
|
+
if self.exposure_status == ExposureStatus.IDLE:
|
|
291
|
+
self.progressExposure.setValue(0)
|
|
292
|
+
msg = "IDLE"
|
|
293
|
+
elif self.exposure_status == ExposureStatus.EXPOSING:
|
|
294
|
+
self.progressExposure.setValue(int(self.exposure_progress))
|
|
295
|
+
msg = "EXPOSING %.1fs" % self.exposure_time_left
|
|
296
|
+
elif self.exposure_status == ExposureStatus.READOUT:
|
|
297
|
+
self.progressExposure.setValue(100)
|
|
298
|
+
msg = "READOUT"
|
|
299
|
+
|
|
300
|
+
# set message
|
|
301
|
+
self.labelStatus.setText(msg)
|
|
302
|
+
|
|
303
|
+
# exposures left
|
|
304
|
+
if self.exposures_left > 0:
|
|
305
|
+
self.labelExposuresLeft.setText("%d exposure(s) left" % self.exposures_left)
|
|
306
|
+
else:
|
|
307
|
+
self.labelExposuresLeft.setText("")
|
|
@@ -13,7 +13,7 @@ class CommandInputWidget(QtWidgets.QLineEdit): # type: ignore
|
|
|
13
13
|
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
|
|
14
14
|
if event.key() == QtCore.Qt.Key.Key_Return:
|
|
15
15
|
# get command
|
|
16
|
-
cmd =
|
|
16
|
+
cmd = self.text()
|
|
17
17
|
# emit command
|
|
18
18
|
self.commandExecuted.emit(cmd)
|
|
19
19
|
# clear text field
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
from PySide6 import QtWidgets, QtCore # type: ignore
|
|
3
|
-
import qasync # type: ignore
|
|
4
3
|
import astropy.units as u
|
|
5
4
|
from astropy.coordinates import SkyCoord, ICRS
|
|
6
5
|
|
|
@@ -33,20 +32,30 @@ class CompassMoveWidget(BaseWidget, Ui_CompassMoveWidget):
|
|
|
33
32
|
self.run_background(self.__move_offset, self.sender())
|
|
34
33
|
|
|
35
34
|
async def __move_offset(self, sender: QtWidgets.QWidget) -> None:
|
|
35
|
+
if self.observer is None:
|
|
36
|
+
return
|
|
37
|
+
|
|
36
38
|
# get offsets
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
altaz = None
|
|
40
|
+
has_offsets_altaz = await self.comm.has_proxy(self.module, IOffsetsAltAz)
|
|
41
|
+
has_pointing_altaz = await self.comm.has_proxy(self.module, IPointingAltAz)
|
|
42
|
+
has_offsets_radec = await self.comm.has_proxy(self.module, IOffsetsRaDec)
|
|
43
|
+
if has_offsets_altaz and has_pointing_altaz:
|
|
44
|
+
async with self.comm.proxy(self.module, IPointingAltAz) as p:
|
|
45
|
+
alt, az = await p.get_altaz() # type: ignore[attr-defined]
|
|
39
46
|
altaz = SkyCoord(
|
|
40
|
-
alt=alt * u.degree,
|
|
41
|
-
az=
|
|
47
|
+
alt=alt * u.degree, # type: ignore[attr-defined]
|
|
48
|
+
az=az * u.degree, # type: ignore[attr-defined]
|
|
42
49
|
obstime=Time.now(),
|
|
43
50
|
location=self.observer.location,
|
|
44
51
|
frame="altaz",
|
|
45
52
|
)
|
|
46
|
-
|
|
53
|
+
async with self.comm.proxy(self.module, IOffsetsAltAz) as p:
|
|
54
|
+
off_alt, off_az = await p.get_offsets_altaz() # type: ignore[attr-defined]
|
|
47
55
|
off_ra, off_dec = offset_altaz_to_radec(altaz, off_alt, off_az)
|
|
48
|
-
elif
|
|
49
|
-
|
|
56
|
+
elif has_offsets_radec:
|
|
57
|
+
async with self.comm.proxy(self.module, IOffsetsRaDec) as p:
|
|
58
|
+
off_ra, off_dec = await p.get_offsets_radec() # type: ignore[attr-defined]
|
|
50
59
|
else:
|
|
51
60
|
return
|
|
52
61
|
|
|
@@ -64,11 +73,13 @@ class CompassMoveWidget(BaseWidget, Ui_CompassMoveWidget):
|
|
|
64
73
|
off_ra -= user_offset
|
|
65
74
|
|
|
66
75
|
# move
|
|
67
|
-
if
|
|
68
|
-
self.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
if has_offsets_radec:
|
|
77
|
+
async with self.comm.proxy(self.module, IOffsetsRaDec) as p:
|
|
78
|
+
await p.set_offsets_radec(off_ra, off_dec)
|
|
79
|
+
elif has_offsets_altaz and altaz is not None:
|
|
80
|
+
off_alt, off_az = offset_radec_to_altaz(altaz.transform_to(ICRS()), off_ra, off_dec, self.observer.location) # noqa: E501
|
|
81
|
+
async with self.comm.proxy(self.module, IOffsetsAltAz) as p:
|
|
82
|
+
await p.set_offsets_altaz(off_alt, off_az)
|
|
72
83
|
else:
|
|
73
84
|
raise ValueError
|
|
74
85
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import qasync
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any
|
|
4
|
+
from PySide6 import QtCore # type: ignore
|
|
5
|
+
|
|
6
|
+
from pyobs.interfaces import ICooling, CoolingState
|
|
7
|
+
from .base import BaseWidget
|
|
8
|
+
from .qt.coolingwidget_ui import Ui_CoolingWidget
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CoolingWidget(BaseWidget, Ui_CoolingWidget):
|
|
14
|
+
signal_update_gui = QtCore.Signal()
|
|
15
|
+
|
|
16
|
+
def __init__(self, **kwargs: Any):
|
|
17
|
+
BaseWidget.__init__(self, **kwargs)
|
|
18
|
+
self.setupUi(self) # type: ignore
|
|
19
|
+
|
|
20
|
+
# state
|
|
21
|
+
self.state: CoolingState | None = None
|
|
22
|
+
|
|
23
|
+
# connect signals
|
|
24
|
+
self.signal_update_gui.connect(self.update_gui)
|
|
25
|
+
self.checkEnabled.toggled.connect(self.checkEnabled_toggled)
|
|
26
|
+
self.buttonApply.clicked.connect(self.buttonApply_clicked)
|
|
27
|
+
|
|
28
|
+
async def _init(self) -> None:
|
|
29
|
+
await self.comm.subscribe_state(self.module, ICooling, self._update_state)
|
|
30
|
+
|
|
31
|
+
def _update_state(self, state: CoolingState) -> None:
|
|
32
|
+
self.state = state
|
|
33
|
+
self.signal_update_gui.emit()
|
|
34
|
+
|
|
35
|
+
def update_gui(self) -> None:
|
|
36
|
+
if self.state is not None:
|
|
37
|
+
self.setEnabled(True)
|
|
38
|
+
|
|
39
|
+
if self.state.enabled:
|
|
40
|
+
self.labelStatus.setText("N/A" if self.state.setpoint is None else f"{self.state.setpoint:.1f}°C")
|
|
41
|
+
self.labelPower.setText("N/A" if self.state.power is None else f"{self.state.power:d}%")
|
|
42
|
+
else:
|
|
43
|
+
self.labelStatus.setText("OFF")
|
|
44
|
+
self.labelPower.clear()
|
|
45
|
+
|
|
46
|
+
@QtCore.Slot(bool) # type: ignore
|
|
47
|
+
def checkEnabled_toggled(self, enabled: bool) -> None:
|
|
48
|
+
self.spinSetPoint.setEnabled(enabled)
|
|
49
|
+
|
|
50
|
+
@qasync.asyncSlot() # type: ignore
|
|
51
|
+
async def buttonApply_clicked(self) -> None:
|
|
52
|
+
enabled = self.checkEnabled.isChecked()
|
|
53
|
+
temp = self.spinSetPoint.value()
|
|
54
|
+
async with self.comm.proxy(self.module, ICooling) as proxy:
|
|
55
|
+
await proxy.set_cooling(enabled, temp)
|