pyobs-gui 2.0.0.dev8__tar.gz → 2.0.0.dev10__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.
Files changed (141) hide show
  1. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/PKG-INFO +1 -1
  2. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/acquisitionwidget.py +12 -3
  3. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/camerawidget.py +10 -6
  4. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyproject.toml +1 -1
  5. pyobs_gui-2.0.0.dev10/tests/test_camerawidget.py +85 -0
  6. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/uv.lock +1 -1
  7. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.claude/skills/verify/SKILL.md +0 -0
  8. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.github/dependabot.yml +0 -0
  9. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.github/workflows/pypi.yml +0 -0
  10. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.github/workflows/pyrefly.yml +0 -0
  11. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.github/workflows/ruff.yml +0 -0
  12. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.gitignore +0 -0
  13. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.pre-commit-config.yaml +0 -0
  14. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/.readthedocs.yml +0 -0
  15. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/CHANGELOG.rst +0 -0
  16. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/LICENSE +0 -0
  17. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/README.md +0 -0
  18. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/bin/mypyuic5 +0 -0
  19. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/docs/Makefile +0 -0
  20. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/docs/make.bat +0 -0
  21. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/docs/requirements.txt +0 -0
  22. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/docs/source/_static/pyobs.gif +0 -0
  23. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/docs/source/conf.py +0 -0
  24. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/docs/source/index.rst +0 -0
  25. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/mypy.ini +0 -0
  26. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/__init__.py +0 -0
  27. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/__main__.py +0 -0
  28. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/_nuitka_astropy_patch.py +0 -0
  29. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/accounts.py +0 -0
  30. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/autofocuswidget.py +0 -0
  31. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/autoguidingwidget.py +0 -0
  32. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/base.py +0 -0
  33. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/commandinputwidget.py +0 -0
  34. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/compassmovewidget.py +0 -0
  35. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/coolingwidget.py +0 -0
  36. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/datadisplaywidget.py +0 -0
  37. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/eventswidget.py +0 -0
  38. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/filterwidget.py +0 -0
  39. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/fitsheaderswidget.py +0 -0
  40. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/focuswidget.py +0 -0
  41. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/gui.py +0 -0
  42. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/login.py +0 -0
  43. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/loginwindow.py +0 -0
  44. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/logmodel.py +0 -0
  45. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/mainwindow.py +0 -0
  46. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/modewidget.py +0 -0
  47. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/modifiedmixin.py +0 -0
  48. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/modulegui.py +0 -0
  49. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/__init__.py +0 -0
  50. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/acquisitionwidget.ui +0 -0
  51. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/acquisitionwidget_ui.py +0 -0
  52. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/autofocuswidget.ui +0 -0
  53. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/autofocuswidget_ui.py +0 -0
  54. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/autoguidingwidget.ui +0 -0
  55. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/autoguidingwidget_ui.py +0 -0
  56. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/camerawidget.ui +0 -0
  57. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/camerawidget_ui.py +0 -0
  58. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/compassmoveplugin.py +0 -0
  59. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/compassmovewidget.py +0 -0
  60. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/compassmovewidget.ui +0 -0
  61. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/compassmovewidget_ui.py +0 -0
  62. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/compile.sh +0 -0
  63. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/coolingwidget.ui +0 -0
  64. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/coolingwidget_ui.py +0 -0
  65. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/datadisplayplugin.py +0 -0
  66. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/datadisplaywidget.ui +0 -0
  67. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/datadisplaywidget_ui.py +0 -0
  68. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/designer.sh +0 -0
  69. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/eventswidget.ui +0 -0
  70. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/eventswidget_ui.py +0 -0
  71. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/filterwidget.ui +0 -0
  72. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/filterwidget_ui.py +0 -0
  73. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/fitsheaderswidget.ui +0 -0
  74. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/fitsheaderswidget_ui.py +0 -0
  75. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/focuswidget.ui +0 -0
  76. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/focuswidget_ui.py +0 -0
  77. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/mainwindow.ui +0 -0
  78. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/mainwindow_ui.py +0 -0
  79. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/modewidget.ui +0 -0
  80. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/modewidget_ui.py +0 -0
  81. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/arrow-alt-circle-down-solid.svg +0 -0
  82. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/arrow-alt-circle-left-solid.svg +0 -0
  83. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/arrow-alt-circle-right-solid.svg +0 -0
  84. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/arrow-alt-circle-up-solid.svg +0 -0
  85. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg +0 -0
  86. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/arrows-to-eye-solid.svg +0 -0
  87. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/chart-line-solid.svg +0 -0
  88. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/circle-question-solid.svg +0 -0
  89. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/cloud-sun-solid.svg +0 -0
  90. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/edit-solid.svg +0 -0
  91. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/search-solid.svg +0 -0
  92. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources/undo-solid.svg +0 -0
  93. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources.qrc +0 -0
  94. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/resources_rc.py +0 -0
  95. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/roofwidget.ui +0 -0
  96. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/roofwidget_ui.py +0 -0
  97. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/shellwidget.ui +0 -0
  98. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/shellwidget_ui.py +0 -0
  99. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/spectrographwidget.ui +0 -0
  100. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/spectrographwidget_ui.py +0 -0
  101. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/telescopewidget.ui +0 -0
  102. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/telescopewidget_ui.py +0 -0
  103. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/temperaturesplotwidget.ui +0 -0
  104. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/temperaturesplotwidget_ui.py +0 -0
  105. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/temperatureswidget.ui +0 -0
  106. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/temperatureswidget_ui.py +0 -0
  107. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/videowidget.ui +0 -0
  108. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/videowidget_ui.py +0 -0
  109. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/weatherwidget.ui +0 -0
  110. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/qt/weatherwidget_ui.py +0 -0
  111. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/roofwidget.py +0 -0
  112. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/shellwidget.py +0 -0
  113. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/spectrographwidget.py +0 -0
  114. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/statuswidget.py +0 -0
  115. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/telescopewidget.py +0 -0
  116. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/temperaturesplotwidget.py +0 -0
  117. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/temperatureswidget.py +0 -0
  118. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/utils.py +0 -0
  119. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/videowidget.py +0 -0
  120. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/watchedlabel.py +0 -0
  121. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pyobs_gui/weatherwidget.py +0 -0
  122. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/pysidedeploy.spec +0 -0
  123. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/specs/README.md +0 -0
  124. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/spike_standalone/main.py +0 -0
  125. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/spike_standalone/pysidedeploy.spec +0 -0
  126. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/acquisition.yaml +0 -0
  127. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/autofocus.yaml +0 -0
  128. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/camera.yaml +0 -0
  129. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/full.yaml +0 -0
  130. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/guiding.yaml +0 -0
  131. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/mode.yaml +0 -0
  132. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/roof.yaml +0 -0
  133. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/spectrograph.yaml +0 -0
  134. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/telescope.yaml +0 -0
  135. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/telescope_acl.yaml +0 -0
  136. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/telescope_acl_denied.yaml +0 -0
  137. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/test/video.yaml +0 -0
  138. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/tests/__init__.py +0 -0
  139. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/tests/conftest.py +0 -0
  140. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/tests/test_accounts.py +0 -0
  141. {pyobs_gui-2.0.0.dev8 → pyobs_gui-2.0.0.dev10}/tests/test_loginwindow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-gui
3
- Version: 2.0.0.dev8
3
+ Version: 2.0.0.dev10
4
4
  Summary: A remote GUI for pyobs
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -123,19 +123,28 @@ class AcquisitionWidget(BaseWidget, Ui_AcquisitionWidget):
123
123
  xlabel, ylabel = "Offset 1 [deg]", "Offset 2 [deg]"
124
124
  if points:
125
125
  xs, ys = zip(*points, strict=True)
126
- self.ax2.axhline(0, color="gray", linewidth=0.5)
127
- self.ax2.axvline(0, color="gray", linewidth=0.5)
128
126
  self.ax2.plot(xs, ys, marker="o", color="tab:orange")
129
127
  self.ax2.plot(xs[0], ys[0], marker="s", color="tab:red", markersize=8, linestyle="", label="start")
130
128
  self.ax2.plot(xs[-1], ys[-1], marker="*", color="tab:green", markersize=12, linestyle="", label="latest")
131
129
  self.ax2.legend(fontsize="small")
132
- self.ax2.set_aspect("equal", adjustable="datalim")
133
130
  self.ax2.set_xlabel(xlabel)
134
131
  self.ax2.set_ylabel(ylabel)
135
132
  self.ax2.grid(linestyle=":", alpha=0.5)
136
133
  self.ax2.set_axisbelow(True)
137
134
 
138
135
  self.figure.tight_layout()
136
+
137
+ if points:
138
+ # lock in the aspect/view from the actual data -- after tight_layout, so the axes
139
+ # box size is already final -- before adding the crosshair lines below, which
140
+ # otherwise feed (0, 0) into the data limits and force the plot to zoom out to
141
+ # include the origin even when every point is far from it
142
+ self.ax2.set_aspect("equal", adjustable="datalim")
143
+ self.ax2.apply_aspect()
144
+ self.ax2.autoscale(False)
145
+ self.ax2.axhline(0, color="gray", linewidth=0.5, zorder=0)
146
+ self.ax2.axvline(0, color="gray", linewidth=0.5, zorder=0)
147
+
139
148
  self.canvas.draw()
140
149
 
141
150
  @qasync.asyncSlot() # type: ignore
@@ -332,13 +332,17 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
332
332
  self.exposures_left = self.spinCount.value()
333
333
  broadcast = self.checkBroadcast.isChecked()
334
334
 
335
- # if the module can grab a counted sequence server-side, let it
336
- async with self.comm.safe_proxy(self.module, IDataSequence) as proxy:
337
- if proxy is not None:
338
- await proxy.grab_sequence(self.exposures_left, broadcast)
339
- return
335
+ # if the module can grab a counted sequence server-side, let it -- but only when
336
+ # broadcasting, since grab_sequence() doesn't hand filenames back to the caller and
337
+ # the client has no other way to learn an image is ready to display
338
+ if broadcast:
339
+ async with self.comm.safe_proxy(self.module, IDataSequence) as proxy:
340
+ if proxy is not None:
341
+ await proxy.grab_sequence(self.exposures_left, broadcast)
342
+ return
340
343
 
341
- # fall back to a client-side loop for modules that don't support IDataSequence
344
+ # fall back to a client-side loop for modules that don't support IDataSequence, or
345
+ # when not broadcasting (grab_data() returns the filename directly for display)
342
346
  while self.exposures_left > 0:
343
347
  await self.datadisplay.grab_data(broadcast)
344
348
  self.exposures_left -= 1
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-gui"
3
- version = "2.0.0.dev8"
3
+ version = "2.0.0.dev10"
4
4
  description = "A remote GUI for pyobs"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11,<3.14"
@@ -0,0 +1,85 @@
1
+ from types import SimpleNamespace
2
+ from unittest.mock import AsyncMock
3
+
4
+ import pytest
5
+
6
+ from pyobs.interfaces import IDataSequence
7
+ from pyobs_gui.camerawidget import CameraWidget
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 CameraWidget.expose() touches; every optional interface except
25
+ IDataSequence resolves to "not supported" (proxy is None)."""
26
+
27
+ def __init__(self, data_sequence_proxy=None):
28
+ self._data_sequence_proxy = data_sequence_proxy
29
+
30
+ def safe_proxy(self, module, interface):
31
+ value = self._data_sequence_proxy if interface is IDataSequence else None
32
+ return _AsyncProxyCM(value)
33
+
34
+
35
+ def make_widget(data_sequence_proxy) -> CameraWidget:
36
+ widget = CameraWidget()
37
+ widget.modules = ["camera"]
38
+ widget._comm = FakeComm(data_sequence_proxy=data_sequence_proxy)
39
+ widget.datadisplay.grab_data = AsyncMock()
40
+ return widget
41
+
42
+
43
+ @pytest.mark.asyncio
44
+ async def test_expose_uses_server_side_sequence_when_broadcasting(qapp) -> None:
45
+ proxy = SimpleNamespace(grab_sequence=AsyncMock())
46
+ widget = make_widget(proxy)
47
+ widget.checkBroadcast.setChecked(True)
48
+ widget.spinCount.setValue(3)
49
+
50
+ await widget.expose()
51
+
52
+ proxy.grab_sequence.assert_awaited_once_with(3, True)
53
+ widget.datadisplay.grab_data.assert_not_called()
54
+ widget.close()
55
+
56
+
57
+ @pytest.mark.asyncio
58
+ async def test_expose_falls_back_to_client_side_loop_when_not_broadcasting(qapp) -> None:
59
+ """Regression test: grab_sequence() has no way to hand a filename back to the caller, so
60
+ without broadcasting, taking the server-side sequence path would leave the GUI with no way
61
+ to learn an image is ready and nothing would ever be displayed (issue #122)."""
62
+ proxy = SimpleNamespace(grab_sequence=AsyncMock())
63
+ widget = make_widget(proxy)
64
+ widget.checkBroadcast.setChecked(False)
65
+ widget.spinCount.setValue(2)
66
+
67
+ await widget.expose()
68
+
69
+ proxy.grab_sequence.assert_not_called()
70
+ assert widget.datadisplay.grab_data.await_count == 2
71
+ widget.datadisplay.grab_data.assert_awaited_with(False)
72
+ widget.close()
73
+
74
+
75
+ @pytest.mark.asyncio
76
+ async def test_expose_falls_back_when_module_has_no_data_sequence_support(qapp) -> None:
77
+ widget = make_widget(data_sequence_proxy=None)
78
+ widget.checkBroadcast.setChecked(True)
79
+ widget.spinCount.setValue(1)
80
+
81
+ await widget.expose()
82
+
83
+ assert widget.datadisplay.grab_data.await_count == 1
84
+ widget.datadisplay.grab_data.assert_awaited_with(True)
85
+ widget.close()
@@ -2375,7 +2375,7 @@ wheels = [
2375
2375
 
2376
2376
  [[package]]
2377
2377
  name = "pyobs-gui"
2378
- version = "2.0.0.dev8"
2378
+ version = "2.0.0.dev10"
2379
2379
  source = { editable = "." }
2380
2380
  dependencies = [
2381
2381
  { name = "astroplan" },
File without changes
File without changes