pyobs-gui 2.4.2__tar.gz → 2.5.1__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.4.2 → pyobs_gui-2.5.1}/PKG-INFO +1 -1
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/spectrographwidget.py +41 -10
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyproject.toml +1 -1
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-09-14-fitswidget-toolbar-overflow.md +3 -2
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/index.md +10 -7
- pyobs_gui-2.5.1/tests/test_spectrographwidget.py +117 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/uv.lock +28 -28
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.claude/skills/verify/SKILL.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.github/dependabot.yml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.github/workflows/dependabot-automerge.yml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.github/workflows/pypi.yml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.github/workflows/pyrefly.yml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.github/workflows/ruff.yml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.gitignore +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.pre-commit-config.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/.readthedocs.yml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/CHANGELOG.rst +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/CLAUDE.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/LICENSE +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/README.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/bin/mypyuic5 +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/docs/Makefile +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/docs/make.bat +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/docs/requirements.txt +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/docs/source/_static/pyobs.gif +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/docs/source/conf.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/docs/source/index.rst +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/mypy.ini +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/__init__.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/__main__.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/_nuitka_astropy_patch.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/accounts.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/acquisitionwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/autofocuswidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/autoguidingwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/base.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/camerawidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/commandinputwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/compassmovewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/coolingwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/datadisplaywidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/eventswidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/filterwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/fitsheaderswidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/focuswidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/gui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/login.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/loginwindow.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/logmodel.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/mainwindow.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/modewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/modifiedmixin.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/modulegui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/movestack.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/nowheelfilter.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/__init__.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/acquisitionwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/acquisitionwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/autofocuswidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/autofocuswidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/autoguidingwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/autoguidingwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/camerawidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/camerawidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/compassmoveplugin.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/compassmovewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/compassmovewidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/compassmovewidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/compile.sh +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/coolingwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/coolingwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/datadisplayplugin.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/datadisplaywidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/datadisplaywidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/designer.sh +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/eventswidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/eventswidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/filterwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/filterwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/fitsheaderswidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/fitsheaderswidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/focuswidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/focuswidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/mainwindow.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/mainwindow_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/modewidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/modewidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrow-alt-circle-down-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrow-alt-circle-left-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrow-alt-circle-right-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrow-alt-circle-up-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrows-to-eye-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/chart-line-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/circle-question-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/cloud-sun-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/edit-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/search-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/undo-solid.svg +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources.qrc +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources_rc.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/roboticwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/roboticwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/roofwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/roofwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/schedulewidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/schedulewidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/shellwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/shellwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/spectrographwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/spectrographwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/telescopewidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/telescopewidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/temperaturesplotwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/temperaturesplotwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/temperatureswidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/temperatureswidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/videograbwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/videograbwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/videowidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/videowidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/weatherwidget.ui +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/weatherwidget_ui.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/roboticwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/roofwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/schedulewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/shellwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/statuswidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/structuredconfigwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/telescopewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/temperaturesplotwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/temperatureswidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/utils.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/videograbwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/videowidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/watchedlabel.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/weatherwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pysidedeploy.spec +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-08-20-gui-remote-call-error-handling.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-08-21-gui-widget-startup-responsiveness.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-08-28-gui-main-vs-sidebar-widgets.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-08-28-structuredconfig-widget.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-08-31-irobotic-widgets.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-09-01-gui-video-widget-split.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/specs/2026-09-14-stacked-widget-scroll-fallback.md +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/spike_standalone/main.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/spike_standalone/pysidedeploy.spec +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/acquisition.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/autofocus.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/camera.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/full.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/guiding.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/mode.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/robotic.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/roof.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/spectrograph.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/structuredconfig.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/telescope.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/telescope_acl.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/telescope_acl_denied.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/test/video.yaml +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/__init__.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/conftest.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_accounts.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_camerawidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_loginwindow.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_mainwindow_startup.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_modewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_multiwidget_pages.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_roboticwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_schedulewidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_structuredconfigwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_videograbwidget.py +0 -0
- {pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/tests/test_videowidget.py +0 -0
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
from typing import Any, TYPE_CHECKING
|
|
5
5
|
from PySide6 import QtCore # type: ignore
|
|
6
6
|
|
|
7
|
-
from pyobs.interfaces import IAbortable, IExposure, ExposureState
|
|
7
|
+
from pyobs.interfaces import IAbortable, IExposure, ExposureState, IDataSequence, DataSequenceState
|
|
8
8
|
from pyobs.utils.enums import ExposureStatus
|
|
9
9
|
from .base import BaseWidget
|
|
10
10
|
|
|
@@ -53,34 +53,65 @@ class SpectrographWidget(BaseWidget, Ui_SpectrographWidget):
|
|
|
53
53
|
|
|
54
54
|
async def _init(self) -> None:
|
|
55
55
|
await self.comm.subscribe_state(self.module, IExposure, self._on_exposure_state)
|
|
56
|
+
if await self.comm.has_proxy(self.module, IDataSequence):
|
|
57
|
+
await self.comm.subscribe_state(self.module, IDataSequence, self._on_sequence_state)
|
|
56
58
|
|
|
57
59
|
def _on_exposure_state(self, state: ExposureState) -> None:
|
|
58
60
|
self.exposure_status = state.status
|
|
59
61
|
self.signal_update_gui.emit()
|
|
60
62
|
|
|
63
|
+
def _on_sequence_state(self, state: DataSequenceState) -> None:
|
|
64
|
+
self.exposures_left = state.count_left
|
|
65
|
+
self.signal_update_gui.emit()
|
|
66
|
+
|
|
61
67
|
def grab_spectrum(self) -> None:
|
|
62
68
|
self.run_background(self._grab_spectra)
|
|
63
69
|
|
|
64
70
|
async def _grab_spectra(self) -> None:
|
|
65
71
|
broadcast = self.checkBroadcast.isChecked()
|
|
66
72
|
|
|
67
|
-
# take the requested number of spectra; an exception (e.g. from a failed exposure,
|
|
68
|
-
# or abort() zeroing exposures_left mid-loop) stops the sequence
|
|
69
73
|
self.exposures_left = self.spinCount.value()
|
|
70
74
|
self.signal_update_gui.emit()
|
|
75
|
+
|
|
76
|
+
# if the module can grab a counted sequence server-side, let it -- but only when
|
|
77
|
+
# broadcasting, since grab_sequence() doesn't hand filenames back to the caller and
|
|
78
|
+
# the client has no other way to learn a spectrum is ready to display
|
|
79
|
+
if broadcast:
|
|
80
|
+
async with self.comm.safe_proxy(self.module, IDataSequence) as proxy:
|
|
81
|
+
if proxy is not None:
|
|
82
|
+
await proxy.grab_sequence(self.exposures_left, broadcast)
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
# fall back to a client-side loop for modules that don't support IDataSequence, or
|
|
86
|
+
# when not broadcasting (grab_data() returns the filename directly for display); an
|
|
87
|
+
# exception (e.g. from a failed exposure, or abort() zeroing exposures_left mid-loop)
|
|
88
|
+
# stops the sequence
|
|
71
89
|
while self.exposures_left > 0:
|
|
72
90
|
await self.datadisplay.grab_data(broadcast)
|
|
73
91
|
self.exposures_left -= 1
|
|
74
92
|
self.signal_update_gui.emit()
|
|
75
93
|
|
|
76
94
|
def abort(self) -> None:
|
|
77
|
-
#
|
|
78
|
-
self.exposures_left
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
# do we have a running sequence?
|
|
96
|
+
if self.exposures_left == 0:
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
self.run_background(self._do_abort)
|
|
100
|
+
|
|
101
|
+
async def _do_abort(self) -> None:
|
|
102
|
+
# got spectra left?
|
|
103
|
+
if self.exposures_left > 1:
|
|
104
|
+
# soft-stop the sequence server-side (current spectrum finishes normally), if
|
|
105
|
+
# supported; otherwise just stop the client-side loop after the current spectrum
|
|
106
|
+
async with self.comm.safe_proxy(self.module, IDataSequence) as proxy:
|
|
107
|
+
if proxy is not None:
|
|
108
|
+
await proxy.abort_sequence()
|
|
109
|
+
return
|
|
110
|
+
self.exposures_left = 0
|
|
111
|
+
else:
|
|
112
|
+
async with self.comm.safe_proxy(self.module, IAbortable) as proxy:
|
|
113
|
+
if proxy is not None:
|
|
114
|
+
await proxy.abort()
|
|
84
115
|
|
|
85
116
|
def update_gui(self) -> None:
|
|
86
117
|
self.setEnabled(True)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Plan: qfitswidget — responsive Cuts/Stretch/Colormap toolbar (hide + overflow, not wrap)
|
|
2
2
|
|
|
3
|
-
Status: implemented
|
|
4
|
-
|
|
3
|
+
Status: implemented, closed — released in qfitswidget v1.1.3, `pyobs-gui`'s floor bumped to match
|
|
4
|
+
and released in v2.4.2. See "Implementation notes" below for the full list of real bugs found and
|
|
5
|
+
fixed during live verification (eight, not three), and one design change from what this doc
|
|
5
6
|
originally proposed (thresholds are measured at runtime, not hardcoded).
|
|
6
7
|
|
|
7
8
|
Repos: qfitswidget (all implementation here; surfaced from pyobs-gui work, see "Context" below)
|
|
@@ -13,13 +13,16 @@ ADRs that concern `pyobs-gui` live in `pyobs-core`'s `specs/` tree instead (`spe
|
|
|
13
13
|
further, instead of every such floor needing its own individual fix. The flagged
|
|
14
14
|
mouse-wheel-over-spinbox papercut was confirmed real (not just theoretical) and fixed via
|
|
15
15
|
`nowheelfilter.py`'s app-wide event filter.
|
|
16
|
-
- `2026-09-14-fitswidget-toolbar-overflow.md` — **implemented**. Repos: qfitswidget
|
|
17
|
-
since qfitswidget has no `specs/` of its own). Responsive Cuts/Stretch/Colormap
|
|
18
|
-
`QFitsWidget`: hide-then-overflow (not wrap) as width shrinks, `QMenu`/`QWidgetAction`
|
|
19
|
-
for the two checkboxes, manual-cuts fields hidden outright whenever Cuts ≠ Manual.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
- `2026-09-14-fitswidget-toolbar-overflow.md` — **implemented, closed**. Repos: qfitswidget
|
|
17
|
+
(hosted here since qfitswidget has no `specs/` of its own). Responsive Cuts/Stretch/Colormap
|
|
18
|
+
toolbar in `QFitsWidget`: hide-then-overflow (not wrap) as width shrinks, `QMenu`/`QWidgetAction`
|
|
19
|
+
overflow for the two checkboxes, manual-cuts fields hidden outright whenever Cuts ≠ Manual.
|
|
20
|
+
Released in qfitswidget v1.1.3; this repo's floor bumped and released in v2.4.2. Eight real bugs
|
|
21
|
+
found via live testing, not just the isolated headless tests — see the plan doc's
|
|
22
|
+
"Implementation notes" for the full list (a chicken-and-egg minimumSizeHint deadlock, missing
|
|
23
|
+
hysteresis, a `uv run` auto-sync trap that silently discarded an editable install across several
|
|
24
|
+
rounds of "still broken" reports, a `QWidgetAction` deleteLater() crash, and a one-tier-deep
|
|
25
|
+
repeat of the minimumSizeHint bug).
|
|
23
26
|
- `2026-08-31-irobotic-widgets.md` — **implemented, closed (issue #825, PR #155, `5794186`)**.
|
|
24
27
|
`RoboticWidget` / `ScheduleWidget` for `IRobotic`/`IRoboticScheduler`.
|
|
25
28
|
- `2026-08-28-structuredconfig-widget.md` — **proposed (issue #154)**. Generic
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from types import SimpleNamespace
|
|
2
|
+
from unittest.mock import AsyncMock
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from pyobs.interfaces import IAbortable, IDataSequence
|
|
7
|
+
from pyobs_gui.spectrographwidget import SpectrographWidget
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _AsyncProxyCM:
|
|
11
|
+
"""Mimics Comm.safe_proxy()'s async context manager, yielding a fixed proxy (or None)."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, value):
|
|
14
|
+
self._value = value
|
|
15
|
+
|
|
16
|
+
async def __aenter__(self):
|
|
17
|
+
return self._value
|
|
18
|
+
|
|
19
|
+
async def __aexit__(self, *exc_info):
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FakeComm:
|
|
24
|
+
"""Only implements what SpectrographWidget._grab_spectra()/_do_abort() touch; an interface
|
|
25
|
+
with no proxy given resolves to "not supported" (proxy is None)."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, data_sequence_proxy=None, abortable_proxy=None):
|
|
28
|
+
self._data_sequence_proxy = data_sequence_proxy
|
|
29
|
+
self._abortable_proxy = abortable_proxy
|
|
30
|
+
|
|
31
|
+
def safe_proxy(self, module, interface):
|
|
32
|
+
if interface is IDataSequence:
|
|
33
|
+
value = self._data_sequence_proxy
|
|
34
|
+
elif interface is IAbortable:
|
|
35
|
+
value = self._abortable_proxy
|
|
36
|
+
else:
|
|
37
|
+
value = None
|
|
38
|
+
return _AsyncProxyCM(value)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_widget(data_sequence_proxy=None, abortable_proxy=None) -> SpectrographWidget:
|
|
42
|
+
widget = SpectrographWidget()
|
|
43
|
+
widget.modules = ["spectrograph"]
|
|
44
|
+
widget._comm = FakeComm( # pyrefly: ignore [bad-assignment]
|
|
45
|
+
data_sequence_proxy=data_sequence_proxy, abortable_proxy=abortable_proxy
|
|
46
|
+
)
|
|
47
|
+
widget.datadisplay.grab_data = AsyncMock()
|
|
48
|
+
return widget
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.mark.asyncio
|
|
52
|
+
async def test_grab_spectra_uses_server_side_sequence_when_broadcasting(qapp) -> None:
|
|
53
|
+
proxy = SimpleNamespace(grab_sequence=AsyncMock())
|
|
54
|
+
widget = make_widget(proxy)
|
|
55
|
+
widget.checkBroadcast.setChecked(True)
|
|
56
|
+
widget.spinCount.setValue(3)
|
|
57
|
+
|
|
58
|
+
await widget._grab_spectra()
|
|
59
|
+
|
|
60
|
+
proxy.grab_sequence.assert_awaited_once_with(3, True)
|
|
61
|
+
widget.datadisplay.grab_data.assert_not_called()
|
|
62
|
+
widget.close()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.mark.asyncio
|
|
66
|
+
async def test_grab_spectra_falls_back_to_client_side_loop_when_not_broadcasting(qapp) -> None:
|
|
67
|
+
"""Without broadcasting, grab_sequence() has no way to hand a filename back to the caller,
|
|
68
|
+
so the GUI would have no way to learn a spectrum is ready and nothing would ever be
|
|
69
|
+
displayed -- same reasoning as CameraWidget's equivalent regression test (issue #122)."""
|
|
70
|
+
proxy = SimpleNamespace(grab_sequence=AsyncMock())
|
|
71
|
+
widget = make_widget(proxy)
|
|
72
|
+
widget.checkBroadcast.setChecked(False)
|
|
73
|
+
widget.spinCount.setValue(2)
|
|
74
|
+
|
|
75
|
+
await widget._grab_spectra()
|
|
76
|
+
|
|
77
|
+
proxy.grab_sequence.assert_not_called()
|
|
78
|
+
assert widget.datadisplay.grab_data.await_count == 2
|
|
79
|
+
widget.datadisplay.grab_data.assert_awaited_with(False)
|
|
80
|
+
widget.close()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.asyncio
|
|
84
|
+
async def test_grab_spectra_falls_back_when_module_has_no_data_sequence_support(qapp) -> None:
|
|
85
|
+
widget = make_widget(data_sequence_proxy=None)
|
|
86
|
+
widget.checkBroadcast.setChecked(True)
|
|
87
|
+
widget.spinCount.setValue(1)
|
|
88
|
+
|
|
89
|
+
await widget._grab_spectra()
|
|
90
|
+
|
|
91
|
+
assert widget.datadisplay.grab_data.await_count == 1
|
|
92
|
+
widget.datadisplay.grab_data.assert_awaited_with(True)
|
|
93
|
+
widget.close()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@pytest.mark.asyncio
|
|
97
|
+
async def test_abort_soft_stops_server_side_sequence_when_more_than_one_left(qapp) -> None:
|
|
98
|
+
proxy = SimpleNamespace(abort_sequence=AsyncMock())
|
|
99
|
+
widget = make_widget(proxy)
|
|
100
|
+
widget.exposures_left = 2
|
|
101
|
+
|
|
102
|
+
await widget._do_abort()
|
|
103
|
+
|
|
104
|
+
proxy.abort_sequence.assert_awaited_once()
|
|
105
|
+
widget.close()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.mark.asyncio
|
|
109
|
+
async def test_abort_hard_aborts_when_one_left(qapp) -> None:
|
|
110
|
+
proxy = SimpleNamespace(abort=AsyncMock())
|
|
111
|
+
widget = make_widget(abortable_proxy=proxy)
|
|
112
|
+
widget.exposures_left = 1
|
|
113
|
+
|
|
114
|
+
await widget._do_abort()
|
|
115
|
+
|
|
116
|
+
proxy.abort.assert_awaited_once()
|
|
117
|
+
widget.close()
|
|
@@ -2623,15 +2623,15 @@ wheels = [
|
|
|
2623
2623
|
|
|
2624
2624
|
[[package]]
|
|
2625
2625
|
name = "pandas-stubs"
|
|
2626
|
-
version = "3.0.5.
|
|
2626
|
+
version = "3.0.5.260914"
|
|
2627
2627
|
source = { registry = "https://pypi.org/simple" }
|
|
2628
2628
|
dependencies = [
|
|
2629
2629
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
|
2630
2630
|
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
|
2631
2631
|
]
|
|
2632
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
2632
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c1/93/8948ae6c1e1e3d6833596fd266f7be2d27c1451b8be094975ad42c5e842e/pandas_stubs-3.0.5.260914.tar.gz", hash = "sha256:3f6fc1f147f68fd89c007105e7c94a948acb4ecd7eb20dc1c02e153c4ed5c250", size = 117622, upload-time = "2026-09-14T16:42:35.065Z" }
|
|
2633
2633
|
wheels = [
|
|
2634
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
2634
|
+
{ url = "https://files.pythonhosted.org/packages/9a/cb/5ad79e02a556cc23fed5816de0109fa8af660c66cfa5f4af74c3e8d4cd26/pandas_stubs-3.0.5.260914-py3-none-any.whl", hash = "sha256:39a1300c5c5c55fdf609e3476805decce5d5015539a4dcb683449f8feaeee2fb", size = 177344, upload-time = "2026-09-14T16:42:33.771Z" },
|
|
2635
2635
|
]
|
|
2636
2636
|
|
|
2637
2637
|
[[package]]
|
|
@@ -3263,7 +3263,7 @@ wheels = [
|
|
|
3263
3263
|
|
|
3264
3264
|
[[package]]
|
|
3265
3265
|
name = "pyobs-core"
|
|
3266
|
-
version = "2.
|
|
3266
|
+
version = "2.10.3"
|
|
3267
3267
|
source = { registry = "https://pypi.org/simple" }
|
|
3268
3268
|
dependencies = [
|
|
3269
3269
|
{ name = "aiohttp" },
|
|
@@ -3295,14 +3295,14 @@ dependencies = [
|
|
|
3295
3295
|
{ name = "tenacity" },
|
|
3296
3296
|
{ name = "typing-extensions" },
|
|
3297
3297
|
]
|
|
3298
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
3298
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b8/3a/2c3d76a1b8ca96f689d34da610d96325292fdaac9df0c7d34451f2cb30c1/pyobs_core-2.10.3.tar.gz", hash = "sha256:27c965f626b6ef2502eda630da6841e7977ad29c65b6299a0403a4ed22eb7e12", size = 420378, upload-time = "2026-09-18T09:15:10.904Z" }
|
|
3299
3299
|
wheels = [
|
|
3300
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3300
|
+
{ url = "https://files.pythonhosted.org/packages/15/6b/0471ea25d46df94cc7c65d67e35ed52a851432978dc58a69bda8ec58bca9/pyobs_core-2.10.3-py3-none-any.whl", hash = "sha256:4ff3c86e23a86ab41541aedb25fa17e46bab832822097adccdb503de76454feb", size = 652925, upload-time = "2026-09-18T09:15:09.374Z" },
|
|
3301
3301
|
]
|
|
3302
3302
|
|
|
3303
3303
|
[[package]]
|
|
3304
3304
|
name = "pyobs-gui"
|
|
3305
|
-
version = "2.
|
|
3305
|
+
version = "2.5.1"
|
|
3306
3306
|
source = { editable = "." }
|
|
3307
3307
|
dependencies = [
|
|
3308
3308
|
{ name = "astroplan" },
|
|
@@ -3788,27 +3788,27 @@ wheels = [
|
|
|
3788
3788
|
|
|
3789
3789
|
[[package]]
|
|
3790
3790
|
name = "ruff"
|
|
3791
|
-
version = "0.16.
|
|
3792
|
-
source = { registry = "https://pypi.org/simple" }
|
|
3793
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
3794
|
-
wheels = [
|
|
3795
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3796
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3797
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3798
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3799
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3800
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3801
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3802
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3803
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3804
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3805
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3806
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3807
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3808
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3809
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3810
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3811
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
3791
|
+
version = "0.16.8"
|
|
3792
|
+
source = { registry = "https://pypi.org/simple" }
|
|
3793
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ba/78/449cb84790bd5cc3823b2652ee405a4558856e5c4195aee3a16bf7b3eb5d/ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b", size = 4938814, upload-time = "2026-09-16T15:54:46.688Z" }
|
|
3794
|
+
wheels = [
|
|
3795
|
+
{ url = "https://files.pythonhosted.org/packages/ac/25/6071aabc530e9be7e2c195e8fe3f7aea2735405b6cf447212832d7811831/ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38", size = 10048966, upload-time = "2026-09-16T15:53:57.605Z" },
|
|
3796
|
+
{ url = "https://files.pythonhosted.org/packages/54/98/07f90ecbc74dd5fb5764f11f2bc774d6a7cffef92d2ff5f5b4e9e23c754e/ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e", size = 10165498, upload-time = "2026-09-16T15:54:01.14Z" },
|
|
3797
|
+
{ url = "https://files.pythonhosted.org/packages/fe/1f/e6a712e3b47cad4a40600134105ed193cb773f618a42eb7ba323cb812cc0/ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170", size = 9830004, upload-time = "2026-09-16T15:54:03.998Z" },
|
|
3798
|
+
{ url = "https://files.pythonhosted.org/packages/23/f2/311a08776d75d81c7676e20b6b020ae63cbe881fcdc7a8dd64e6e18bdd93/ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659", size = 9986558, upload-time = "2026-09-16T15:54:06.804Z" },
|
|
3799
|
+
{ url = "https://files.pythonhosted.org/packages/f3/ed/37b6cb3d3ba8c73e68ae3eb1d502383beb5aa05a582bb7bb3a922f929f54/ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2", size = 9877332, upload-time = "2026-09-16T15:54:09.552Z" },
|
|
3800
|
+
{ url = "https://files.pythonhosted.org/packages/22/cc/40873a8f36ad084cc540d55fcca7077264d5b13b24659e9180c176fb2b08/ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812", size = 10507125, upload-time = "2026-09-16T15:54:12.152Z" },
|
|
3801
|
+
{ url = "https://files.pythonhosted.org/packages/c3/e4/fc91a642b78ccbab6b9477720f3644ae7a10a9bcce69a934679cd64f62bc/ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b", size = 11336694, upload-time = "2026-09-16T15:54:15.489Z" },
|
|
3802
|
+
{ url = "https://files.pythonhosted.org/packages/c2/3d/bbd2a9a600a4e73dc3e7548a249c8d1671273464b55822c6fae50f602dff/ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f", size = 10774448, upload-time = "2026-09-16T15:54:18.16Z" },
|
|
3803
|
+
{ url = "https://files.pythonhosted.org/packages/1a/41/d83af9879a7b6e8bf5fe16b1da0b134049d2f5d3afac12defb0897cb84bd/ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df", size = 10323796, upload-time = "2026-09-16T15:54:20.743Z" },
|
|
3804
|
+
{ url = "https://files.pythonhosted.org/packages/f5/2c/cefd07bfe914b84943ea769ade8d607bd22750b965d3228eefd7cebd15d0/ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e", size = 10514115, upload-time = "2026-09-16T15:54:23.497Z" },
|
|
3805
|
+
{ url = "https://files.pythonhosted.org/packages/f3/9d/76a2e26c79a23be6e6e3664c57bec9e9fc8de155cfb9e4b67ea91b64f9d7/ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f", size = 10072582, upload-time = "2026-09-16T15:54:26.185Z" },
|
|
3806
|
+
{ url = "https://files.pythonhosted.org/packages/2e/d4/f42edddb39668af1a559ceafa3823aedd65633a48dc9768e775485faa2c1/ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2", size = 9879644, upload-time = "2026-09-16T15:54:29.278Z" },
|
|
3807
|
+
{ url = "https://files.pythonhosted.org/packages/f8/d4/913e3195d95e0378786c6656945c865f534a3560e29139da4882aff630d1/ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9", size = 10231569, upload-time = "2026-09-16T15:54:32.036Z" },
|
|
3808
|
+
{ url = "https://files.pythonhosted.org/packages/2b/c4/8aa6ea0bdcedbd1bf87397e2fc4ed8406448ea5842f8660bc6e5f163039d/ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773", size = 10663666, upload-time = "2026-09-16T15:54:34.838Z" },
|
|
3809
|
+
{ url = "https://files.pythonhosted.org/packages/3d/02/7f10ef4700bc223c30a3fdd10631a29830c45524b810a3c7ed947af64591/ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f", size = 10093472, upload-time = "2026-09-16T15:54:37.47Z" },
|
|
3810
|
+
{ url = "https://files.pythonhosted.org/packages/1e/5d/a509c07d714b6da88f2c518b4637cf6f1d46b074be8f0f1e5fb9ff5126fe/ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa", size = 10586899, upload-time = "2026-09-16T15:54:40.488Z" },
|
|
3811
|
+
{ url = "https://files.pythonhosted.org/packages/fe/a0/50787329e4f20bf9dc9f6230015d46ec69c51a97ace5bc202dae4755365d/ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a", size = 10386316, upload-time = "2026-09-16T15:54:43.332Z" },
|
|
3812
3812
|
]
|
|
3813
3813
|
|
|
3814
3814
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_gui-2.4.2 → pyobs_gui-2.5.1}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|