pyobs-gui 2.7.0__tar.gz → 2.7.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.
Files changed (216) hide show
  1. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/CHANGELOG.md +7 -0
  2. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/PKG-INFO +2 -2
  3. pyobs_gui-2.7.1/pyobs_gui/appicon.py +19 -0
  4. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/gui.py +2 -0
  5. pyobs_gui-2.7.1/pyobs_gui/icons/README.md +13 -0
  6. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-128.png +0 -0
  7. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-16.png +0 -0
  8. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-24.png +0 -0
  9. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-256.png +0 -0
  10. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-32.png +0 -0
  11. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-48.png +0 -0
  12. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-512.png +0 -0
  13. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui-64.png +0 -0
  14. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui.ico +0 -0
  15. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui.png +0 -0
  16. pyobs_gui-2.7.1/pyobs_gui/icons/pyobs-gui.svg +13 -0
  17. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/loginwindow.py +2 -0
  18. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/mainwindow.py +2 -0
  19. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/notifier_backend.py +3 -2
  20. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/settingsdialog.py +2 -0
  21. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/windows_identity.py +26 -1
  22. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyproject.toml +5 -2
  23. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pysidedeploy.spec +2 -2
  24. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_notifier_backend.py +11 -0
  25. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_windows_identity.py +53 -1
  26. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/uv.lock +214 -173
  27. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.claude/skills/verify/SKILL.md +0 -0
  28. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.github/dependabot.yml +0 -0
  29. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.github/workflows/dependabot-automerge.yml +0 -0
  30. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.github/workflows/pypi.yml +0 -0
  31. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.github/workflows/pyrefly.yml +0 -0
  32. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.github/workflows/ruff.yml +0 -0
  33. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.gitignore +0 -0
  34. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.pre-commit-config.yaml +0 -0
  35. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/.readthedocs.yml +0 -0
  36. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/CLAUDE.md +0 -0
  37. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/CONTRIBUTING.md +0 -0
  38. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/LICENSE +0 -0
  39. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/README.md +0 -0
  40. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/bin/mypyuic5 +0 -0
  41. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/docs/Makefile +0 -0
  42. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/docs/make.bat +0 -0
  43. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/docs/requirements.txt +0 -0
  44. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/docs/source/_static/pyobs.gif +0 -0
  45. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/docs/source/conf.py +0 -0
  46. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/docs/source/index.rst +0 -0
  47. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/mypy.ini +0 -0
  48. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/__init__.py +0 -0
  49. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/__main__.py +0 -0
  50. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/_nuitka_astropy_patch.py +0 -0
  51. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/accounts.py +0 -0
  52. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/acquisitionwidget.py +0 -0
  53. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/autofocuswidget.py +0 -0
  54. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/autoguidingwidget.py +0 -0
  55. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/base.py +0 -0
  56. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/camerawidget.py +0 -0
  57. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/commandinputwidget.py +0 -0
  58. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/compassmovewidget.py +0 -0
  59. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/coolingwidget.py +0 -0
  60. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/datadisplaywidget.py +0 -0
  61. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/eventswidget.py +0 -0
  62. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/filterwidget.py +0 -0
  63. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/fitsheaderswidget.py +0 -0
  64. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/focuswidget.py +0 -0
  65. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/guisignals.py +0 -0
  66. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/livestream.py +0 -0
  67. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/login.py +0 -0
  68. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/logmodel.py +0 -0
  69. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/modewidget.py +0 -0
  70. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/modifiedmixin.py +0 -0
  71. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/modulegui.py +0 -0
  72. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/movestack.py +0 -0
  73. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/notificationmanager.py +0 -0
  74. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/notifications.py +0 -0
  75. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/notifier.py +0 -0
  76. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/nowheelfilter.py +0 -0
  77. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/__init__.py +0 -0
  78. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/acquisitionwidget.ui +0 -0
  79. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/acquisitionwidget_ui.py +0 -0
  80. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/autofocuswidget.ui +0 -0
  81. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/autofocuswidget_ui.py +0 -0
  82. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/autoguidingwidget.ui +0 -0
  83. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/autoguidingwidget_ui.py +0 -0
  84. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/camerawidget.ui +0 -0
  85. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/camerawidget_ui.py +0 -0
  86. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/compassmoveplugin.py +0 -0
  87. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/compassmovewidget.py +0 -0
  88. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/compassmovewidget.ui +0 -0
  89. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/compassmovewidget_ui.py +0 -0
  90. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/compile.sh +0 -0
  91. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/coolingwidget.ui +0 -0
  92. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/coolingwidget_ui.py +0 -0
  93. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/datadisplayplugin.py +0 -0
  94. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/datadisplaywidget.ui +0 -0
  95. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/datadisplaywidget_ui.py +0 -0
  96. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/designer.sh +0 -0
  97. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/eventswidget.ui +0 -0
  98. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/eventswidget_ui.py +0 -0
  99. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/filterwidget.ui +0 -0
  100. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/filterwidget_ui.py +0 -0
  101. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/fitsheaderswidget.ui +0 -0
  102. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/fitsheaderswidget_ui.py +0 -0
  103. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/focuswidget.ui +0 -0
  104. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/focuswidget_ui.py +0 -0
  105. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/mainwindow.ui +0 -0
  106. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/mainwindow_ui.py +0 -0
  107. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/modewidget.ui +0 -0
  108. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/modewidget_ui.py +0 -0
  109. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/arrow-alt-circle-down-solid.svg +0 -0
  110. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/arrow-alt-circle-left-solid.svg +0 -0
  111. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/arrow-alt-circle-right-solid.svg +0 -0
  112. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/arrow-alt-circle-up-solid.svg +0 -0
  113. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg +0 -0
  114. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/arrows-to-eye-solid.svg +0 -0
  115. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/chart-line-solid.svg +0 -0
  116. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/circle-question-solid.svg +0 -0
  117. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/cloud-sun-solid.svg +0 -0
  118. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/edit-solid.svg +0 -0
  119. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/search-solid.svg +0 -0
  120. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources/undo-solid.svg +0 -0
  121. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources.qrc +0 -0
  122. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/resources_rc.py +0 -0
  123. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/roboticwidget.ui +0 -0
  124. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/roboticwidget_ui.py +0 -0
  125. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/roofwidget.ui +0 -0
  126. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/roofwidget_ui.py +0 -0
  127. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/schedulewidget.ui +0 -0
  128. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/schedulewidget_ui.py +0 -0
  129. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/shellwidget.ui +0 -0
  130. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/shellwidget_ui.py +0 -0
  131. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/spectrographwidget.ui +0 -0
  132. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/spectrographwidget_ui.py +0 -0
  133. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/telescopewidget.ui +0 -0
  134. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/telescopewidget_ui.py +0 -0
  135. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/temperaturesplotwidget.ui +0 -0
  136. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/temperaturesplotwidget_ui.py +0 -0
  137. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/temperatureswidget.ui +0 -0
  138. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/temperatureswidget_ui.py +0 -0
  139. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/videograbwidget.ui +0 -0
  140. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/videograbwidget_ui.py +0 -0
  141. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/videowidget.ui +0 -0
  142. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/videowidget_ui.py +0 -0
  143. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/weatherwidget.ui +0 -0
  144. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/qt/weatherwidget_ui.py +0 -0
  145. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/roboticwidget.py +0 -0
  146. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/roofwidget.py +0 -0
  147. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/schedulewidget.py +0 -0
  148. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/settings.py +0 -0
  149. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/shellwidget.py +0 -0
  150. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/spectrographwidget.py +0 -0
  151. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/statuswidget.py +0 -0
  152. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/structuredconfigwidget.py +0 -0
  153. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/telescopewidget.py +0 -0
  154. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/temperaturesplotwidget.py +0 -0
  155. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/temperatureswidget.py +0 -0
  156. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/utils.py +0 -0
  157. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/videograbwidget.py +0 -0
  158. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/videowidget.py +0 -0
  159. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/watchedlabel.py +0 -0
  160. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/pyobs_gui/weatherwidget.py +0 -0
  161. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-08-20-gui-remote-call-error-handling.md +0 -0
  162. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-08-21-gui-widget-startup-responsiveness.md +0 -0
  163. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-08-28-gui-main-vs-sidebar-widgets.md +0 -0
  164. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-08-28-structuredconfig-widget.md +0 -0
  165. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-08-31-irobotic-widgets.md +0 -0
  166. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-09-01-gui-video-widget-split.md +0 -0
  167. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-09-14-fitswidget-toolbar-overflow.md +0 -0
  168. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-09-14-stacked-widget-scroll-fallback.md +0 -0
  169. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-09-29-live-view-mjpeg-raw.md +0 -0
  170. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-10-04-desktop-notifications.md +0 -0
  171. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-10-04-settings-dialog.md +0 -0
  172. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/2026-10-14-typed-config-choices.md +0 -0
  173. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/index.md +0 -0
  174. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/plans/2026-10-04-desktop-notifications.md +0 -0
  175. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/plans/2026-10-04-settings-dialog.md +0 -0
  176. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/specs/plans/index.md +0 -0
  177. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/spike_standalone/main.py +0 -0
  178. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/spike_standalone/pysidedeploy.spec +0 -0
  179. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/acquisition.yaml +0 -0
  180. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/autofocus.yaml +0 -0
  181. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/camera.yaml +0 -0
  182. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/full.yaml +0 -0
  183. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/guiding.yaml +0 -0
  184. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/mode.yaml +0 -0
  185. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/robotic.yaml +0 -0
  186. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/roof.yaml +0 -0
  187. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/spectrograph.yaml +0 -0
  188. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/structuredconfig.yaml +0 -0
  189. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/telescope.yaml +0 -0
  190. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/telescope_acl.yaml +0 -0
  191. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/telescope_acl_denied.yaml +0 -0
  192. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/test/video.yaml +0 -0
  193. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/__init__.py +0 -0
  194. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/conftest.py +0 -0
  195. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_accounts.py +0 -0
  196. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_basewidget_discard.py +0 -0
  197. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_camerawidget.py +0 -0
  198. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_gui_notifications.py +0 -0
  199. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_import_order.py +0 -0
  200. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_livestream.py +0 -0
  201. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_loginwindow.py +0 -0
  202. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_mainwindow_startup.py +0 -0
  203. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_missing_vfs_root.py +0 -0
  204. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_modewidget.py +0 -0
  205. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_multiwidget_pages.py +0 -0
  206. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_notificationmanager.py +0 -0
  207. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_notifications.py +0 -0
  208. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_roboticwidget.py +0 -0
  209. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_schedulewidget.py +0 -0
  210. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_settings.py +0 -0
  211. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_settingsdialog.py +0 -0
  212. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_spectrographwidget.py +0 -0
  213. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_structuredconfigwidget.py +0 -0
  214. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_video_refresh.py +0 -0
  215. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_videograbwidget.py +0 -0
  216. {pyobs_gui-2.7.0 → pyobs_gui-2.7.1}/tests/test_videowidget.py +0 -0
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  Entries for releases before this file existed were generated from commit subjects.
9
9
 
10
+ ## [Unreleased]
11
+
12
+ ### Added
13
+
14
+ - Magenta observing-monitor app icon for Qt windows, desktop notifications and
15
+ Windows Start Menu shortcuts; update existing shortcuts without changing their targets (#197).
16
+
10
17
  ## [2.7.0] - 2026-10-15
11
18
 
12
19
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyobs-gui
3
- Version: 2.7.0
3
+ Version: 2.7.1
4
4
  Summary: A remote GUI for pyobs
5
5
  Project-URL: Homepage, https://www.pyobs.org
6
6
  Project-URL: Documentation, https://docs.pyobs.org
@@ -19,7 +19,7 @@ Requires-Dist: keyring<26,>=25.7.0
19
19
  Requires-Dist: matplotlib<4,>=3.10.1
20
20
  Requires-Dist: pandas>=2.2.3
21
21
  Requires-Dist: pydantic<3,>=2.12.5
22
- Requires-Dist: pyobs-core<3,>=2.18.0
22
+ Requires-Dist: pyobs-core<3,>=2.18.1
23
23
  Requires-Dist: pyside6-essentials>=6.10.1
24
24
  Requires-Dist: pywin32>=312; sys_platform == 'win32'
25
25
  Requires-Dist: pyyaml<7,>=6.0.2
@@ -0,0 +1,19 @@
1
+ """Shared app artwork as real files for Qt, notifications and Windows shortcuts."""
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Literal
5
+
6
+ if TYPE_CHECKING:
7
+ from PySide6.QtGui import QIcon
8
+
9
+
10
+ def icon_path(format: Literal["png", "ico"] = "png") -> Path:
11
+ """Installed package data, also placed beside this module by Nuitka."""
12
+ return Path(__file__).resolve().with_name("icons") / f"pyobs-gui.{format}"
13
+
14
+
15
+ def window_icon() -> "QIcon":
16
+ """Qt reads all resolutions in the ICO, including the small window-title icon."""
17
+ from PySide6.QtGui import QIcon
18
+
19
+ return QIcon(str(icon_path("ico")))
@@ -12,6 +12,7 @@ if TYPE_CHECKING:
12
12
  from pyobs.interfaces import FitsHeaderEntry, IFitsHeaderBefore
13
13
  from pyobs.modules import Module
14
14
 
15
+ from .appicon import window_icon
15
16
  from .nowheelfilter import NoWheelWhenUnfocused
16
17
  from .guisignals import gui_signals
17
18
  from .notificationmanager import NotificationManager
@@ -147,6 +148,7 @@ class GUI(Module, IFitsHeaderBefore):
147
148
  @staticmethod
148
149
  def new_event_loop() -> asyncio.AbstractEventLoop:
149
150
  GUI.app = QtWidgets.QApplication(sys.argv)
151
+ GUI.app.setWindowIcon(window_icon())
150
152
  # standalone/login mode briefly has zero visible top-level windows while the login
151
153
  # dialog closes and the (old or new) MainWindow hasn't opened yet -- Qt's default
152
154
  # "quit when the last window closes" would tear down the whole event loop right there,
@@ -0,0 +1,13 @@
1
+ # pyobs GUI icon
2
+
3
+ White observing monitor and crosshair on magenta (`#d63384`), following the fleet's
4
+ white-symbol-on-colour app icons. Original artwork created for pyobs-gui and
5
+ approved on 2026-10-15; distributed under this repository's MIT licence.
6
+
7
+ `pyobs-gui.svg` is the editable source. PNG exports cover 16, 24, 32, 48, 64,
8
+ 128, 256 and 512 pixels. `pyobs-gui.png` is the 256-pixel notification icon;
9
+ `pyobs-gui.ico` contains 16 through 256-pixel renditions for Qt and Windows.
10
+
11
+ These files must remain ordinary package data: desktop-notifier and the Windows
12
+ shortcut require filesystem paths, rather than Qt resource URLs. Nuitka includes
13
+ them with `--include-package-data=pyobs_gui` in `pysidedeploy.spec`.
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
2
+ <title>pyobs GUI icon</title>
3
+ <desc>A white observing monitor with a crosshair on a magenta rounded square.</desc>
4
+ <rect width="256" height="256" rx="52" fill="#d63384"/>
5
+ <g fill="none" stroke="#fff" stroke-width="14" stroke-linecap="round" stroke-linejoin="round">
6
+ <rect x="42" y="53" width="172" height="119" rx="13"/>
7
+ <path d="M128 174v29M96 204h64"/>
8
+ </g>
9
+ <g fill="none" stroke="#fff" stroke-width="10" stroke-linecap="round">
10
+ <circle cx="128" cy="112" r="20"/>
11
+ <path d="M128 78v16M128 130v16M94 112h16M146 112h16"/>
12
+ </g>
13
+ </svg>
@@ -14,6 +14,7 @@ from dataclasses import dataclass
14
14
  from PySide6 import QtCore, QtGui, QtWidgets
15
15
  from pyobs.comm.xmpp import is_valid_jid
16
16
 
17
+ from .appicon import window_icon
17
18
  from .accounts import SavedAccountsModel
18
19
 
19
20
  log = logging.getLogger(__name__)
@@ -86,6 +87,7 @@ def build_connection_request(
86
87
  class LoginWindow(QtWidgets.QDialog):
87
88
  def __init__(self, accounts: SavedAccountsModel | None = None, parent: QtWidgets.QWidget | None = None) -> None:
88
89
  super().__init__(parent)
90
+ self.setWindowIcon(window_icon())
89
91
  self.setWindowTitle("pyobs - Sign in")
90
92
  self.resize(640, 480)
91
93
 
@@ -37,6 +37,7 @@ from pyobs.interfaces import (
37
37
  IStructuredConfig,
38
38
  )
39
39
 
40
+ from .appicon import window_icon
40
41
  from .base import BaseWindow, BaseWidget, cancel_and_drain
41
42
  from .acquisitionwidget import AcquisitionWidget
42
43
  from .autofocuswidget import AutoFocusWidget
@@ -641,6 +642,7 @@ class MainWindow(QtWidgets.QMainWindow, BaseWindow, Ui_MainWindow): # type: ign
641
642
  QtWidgets.QMainWindow.__init__(self)
642
643
  BaseWindow.__init__(self)
643
644
  self.setupUi(self) # type: ignore
645
+ self.setWindowIcon(window_icon())
644
646
  self.resize(1600, 900)
645
647
 
646
648
  # store stuff
@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Any
11
11
 
12
12
  from PySide6 import QtCore # type: ignore
13
13
 
14
+ from .appicon import icon_path
14
15
  from .notifier import NotifierError
15
16
 
16
17
  if TYPE_CHECKING:
@@ -77,9 +78,9 @@ class DesktopNotifierBackend:
77
78
  if self._factory is not None:
78
79
  self._notifier = self._factory()
79
80
  else:
80
- from desktop_notifier import DesktopNotifier
81
+ from desktop_notifier import DesktopNotifier, Icon
81
82
 
82
- self._notifier = DesktopNotifier(app_name=APP_NAME)
83
+ self._notifier = DesktopNotifier(app_name=APP_NAME, app_icon=Icon(path=icon_path()))
83
84
  return self._notifier
84
85
 
85
86
  async def ensure_authorisation(self) -> None:
@@ -18,6 +18,7 @@ from pydantic import ValidationError
18
18
  from pyobs.object import get_class_from_string
19
19
  from PySide6 import QtWidgets # type: ignore
20
20
 
21
+ from .appicon import window_icon
21
22
  from .notifier import NotifierError
22
23
  from .settings import GuiSettings, NotificationSettings, VfsSettings
23
24
 
@@ -83,6 +84,7 @@ class SettingsDialog(QtWidgets.QDialog):
83
84
  parent: QtWidgets.QWidget | None = None,
84
85
  ) -> None:
85
86
  super().__init__(parent)
87
+ self.setWindowIcon(window_icon())
86
88
  self.setWindowTitle("Settings")
87
89
  self.resize(640, 480)
88
90
  self._on_apply = on_apply
@@ -19,6 +19,8 @@ from typing import TYPE_CHECKING
19
19
  if TYPE_CHECKING:
20
20
  from collections.abc import Callable
21
21
 
22
+ from .appicon import icon_path
23
+
22
24
  log = logging.getLogger(__name__)
23
25
 
24
26
  SHORTCUT_NAME = "pyobs-gui.lnk"
@@ -52,6 +54,7 @@ def create_link_com(path: Path, target: str, arguments: str, app_id: str) -> Non
52
54
 
53
55
  link = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
54
56
  link.SetPath(target)
57
+ link.SetIconLocation(str(icon_path("ico")), 0)
55
58
  if arguments:
56
59
  link.SetArguments(arguments)
57
60
  store = link.QueryInterface(propsys.IID_IPropertyStore)
@@ -60,15 +63,36 @@ def create_link_com(path: Path, target: str, arguments: str, app_id: str) -> Non
60
63
  link.QueryInterface(pythoncom.IID_IPersistFile).Save(str(path), 0)
61
64
 
62
65
 
66
+ def update_link_icon_com(path: Path) -> bool:
67
+ """Upgrade an existing shortcut's icon, preserving its target and app identity.
68
+
69
+ Like create_link_com(), this still needs verification on Windows (#192).
70
+ """
71
+ import pythoncom # type: ignore[import-not-found]
72
+ from win32com.shell import shell # type: ignore[import-not-found]
73
+
74
+ link = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
75
+ persist = link.QueryInterface(pythoncom.IID_IPersistFile)
76
+ persist.Load(str(path))
77
+ location = (str(icon_path("ico")), 0)
78
+ if link.GetIconLocation() == location:
79
+ return False
80
+ link.SetIconLocation(*location)
81
+ persist.Save(str(path), 0)
82
+ return True
83
+
84
+
63
85
  def ensure_shortcut(
64
86
  app_id: str,
65
87
  path: Path | None = None,
66
88
  command: tuple[str, str] | None = None,
67
89
  create_link: Callable[[Path, str, str, str], None] = create_link_com,
90
+ update_icon: Callable[[Path], bool] = update_link_icon_com,
68
91
  ) -> bool:
69
92
  """Make sure the Start Menu shortcut with the AppUserModelID exists.
70
93
 
71
- An existing shortcut is left alone, whatever it points to.
94
+ An existing shortcut keeps its target and identity; only its icon is updated.
95
+ Repeated starts leave the file alone when the icon already matches.
72
96
 
73
97
  Returns:
74
98
  Whether a shortcut was created.
@@ -78,6 +102,7 @@ def ensure_shortcut(
78
102
  """
79
103
  path = shortcut_path() if path is None else path
80
104
  if path.exists():
105
+ update_icon(path)
81
106
  return False
82
107
  target, arguments = launch_command() if command is None else command
83
108
  path.parent.mkdir(parents=True, exist_ok=True)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-gui"
3
- version = "2.7.0"
3
+ version = "2.7.1"
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"
@@ -15,7 +15,7 @@ dependencies = [
15
15
  "astroquery>=0.4.10,<0.5",
16
16
  "QtAwesome>=1.4.0,<2",
17
17
  "qfitswidget>=1.2.0",
18
- "pyobs-core>=2.18.0,<3",
18
+ "pyobs-core>=2.18.1,<3",
19
19
  "pyside6-essentials>=6.10.1",
20
20
  "astroplan>=0.10.1",
21
21
  "astropy>=7.0.1,<8",
@@ -58,6 +58,9 @@ dev = [
58
58
  requires = ["hatchling"]
59
59
  build-backend = "hatchling.build"
60
60
 
61
+ [tool.hatch.build.targets.wheel]
62
+ packages = ["pyobs_gui"]
63
+
61
64
  [tool.black]
62
65
  line-length = 120
63
66
  target-version = ['py311']
@@ -16,7 +16,7 @@ exec_directory = dist
16
16
  project_file =
17
17
 
18
18
  # application icon
19
- icon = /home/husser/code/pyobs/pyobs-gui/.venv/lib/python3.13/site-packages/PySide6/scripts/deploy_lib/pyside_icon.jpg
19
+ icon = pyobs_gui/icons/pyobs-gui.png
20
20
 
21
21
  [python]
22
22
 
@@ -68,7 +68,7 @@ macos.permissions =
68
68
  mode = standalone
69
69
 
70
70
  # specify any extra nuitka arguments
71
- extra_args = --quiet --noinclude-qt-translations --python-flag=-m --include-package=astropy.units.format --include-package=astropy.units.format.generic_parsetab --include-package=astropy.units.format.generic_lextab --include-package=astropy.units.format.cds_parsetab --include-package=astropy.units.format.cds_lextab --include-package=astropy.units.format.ogip_parsetab --include-package=astropy.units.format.ogip_lextab --include-package=astropy.coordinates.angles --include-package=astropy.coordinates.angles.angle_parsetab --include-package=astropy.coordinates.angles.angle_lextab --include-package=astropy.extern.ply --include-package-data=astropy --include-package-data=sunpy --include-package-data=matplotlib --include-package-data=qfitswidget --include-package-data=astropy_iers_data --include-package=pyobs.vfs --include-package-data=astroquery --include-package=slixmpp --include-package=pyobs_gui --noinclude-dlls=*webengine* --noinclude-dlls=*WebEngine* --include-distribution-metadata=astropy --include-distribution-metadata=numpy --include-distribution-metadata=PyYAML --include-distribution-metadata=sunpy --include-distribution-metadata=matplotlib --include-distribution-metadata=qfitswidget --include-distribution-metadata=astroquery --include-distribution-metadata=pyobs-core --include-distribution-metadata=pyobs-gui
71
+ extra_args = --quiet --noinclude-qt-translations --python-flag=-m --include-package=astropy.units.format --include-package=astropy.units.format.generic_parsetab --include-package=astropy.units.format.generic_lextab --include-package=astropy.units.format.cds_parsetab --include-package=astropy.units.format.cds_lextab --include-package=astropy.units.format.ogip_parsetab --include-package=astropy.units.format.ogip_lextab --include-package=astropy.coordinates.angles --include-package=astropy.coordinates.angles.angle_parsetab --include-package=astropy.coordinates.angles.angle_lextab --include-package=astropy.extern.ply --include-package-data=astropy --include-package-data=sunpy --include-package-data=matplotlib --include-package-data=qfitswidget --include-package-data=astropy_iers_data --include-package=pyobs.vfs --include-package-data=astroquery --include-package=slixmpp --include-package=pyobs_gui --include-package-data=pyobs_gui --noinclude-dlls=*webengine* --noinclude-dlls=*WebEngine* --include-distribution-metadata=astropy --include-distribution-metadata=numpy --include-distribution-metadata=PyYAML --include-distribution-metadata=sunpy --include-distribution-metadata=matplotlib --include-distribution-metadata=qfitswidget --include-distribution-metadata=astroquery --include-distribution-metadata=pyobs-core --include-distribution-metadata=pyobs-gui
72
72
 
73
73
  [buildozer]
74
74
 
@@ -203,3 +203,14 @@ async def test_guard_passes_everything_on() -> None:
203
203
 
204
204
  def test_app_name_is_what_windows_uses_as_the_id() -> None:
205
205
  assert APP_NAME == "pyobs-gui"
206
+
207
+
208
+ def test_default_notifier_uses_packaged_app_icon(monkeypatch: pytest.MonkeyPatch) -> None:
209
+ from desktop_notifier import Icon
210
+ from pyobs_gui.appicon import icon_path
211
+
212
+ factory = MagicMock()
213
+ monkeypatch.setattr("desktop_notifier.DesktopNotifier", factory)
214
+ backend = DesktopNotifierBackend(ensure_identity=MagicMock())
215
+ backend._get()
216
+ factory.assert_called_once_with(app_name=APP_NAME, app_icon=Icon(path=icon_path()))
@@ -1,6 +1,7 @@
1
1
  import sys
2
2
  from pathlib import Path
3
3
  from typing import Any
4
+ from unittest.mock import MagicMock
4
5
 
5
6
  import pytest
6
7
 
@@ -34,7 +35,9 @@ def test_an_existing_shortcut_is_left_alone(tmp_path: Path) -> None:
34
35
  path = tmp_path / SHORTCUT_NAME
35
36
  path.write_text("mine")
36
37
  create = Recorder()
37
- assert ensure_shortcut("pyobs-gui", path, ("x", ""), create) is False
38
+ update = MagicMock(return_value=True)
39
+ assert ensure_shortcut("pyobs-gui", path, ("x", ""), create, update) is False
40
+ update.assert_called_once_with(path)
38
41
  assert create.calls == [] and path.read_text() == "mine"
39
42
 
40
43
 
@@ -67,3 +70,52 @@ def test_launch_command_for_the_pip_launcher(monkeypatch: pytest.MonkeyPatch, tm
67
70
  def test_launch_command_otherwise_starts_the_module(monkeypatch: pytest.MonkeyPatch, argv0: Any) -> None:
68
71
  monkeypatch.setattr(sys, "argv", [argv0])
69
72
  assert launch_command() == (sys.executable, "-m pyobs_gui")
73
+
74
+
75
+ @pytest.fixture
76
+ def com_link(monkeypatch: pytest.MonkeyPatch) -> MagicMock:
77
+ """Exercise our COM calls without requiring a Windows notification service."""
78
+ from types import ModuleType
79
+
80
+ link = MagicMock()
81
+ pythoncom = MagicMock()
82
+ pythoncom.CoCreateInstance.return_value = link
83
+ monkeypatch.setitem(sys.modules, "pythoncom", pythoncom)
84
+ monkeypatch.setitem(sys.modules, "win32com", ModuleType("win32com"))
85
+ monkeypatch.setitem(sys.modules, "win32com.shell", MagicMock())
86
+ monkeypatch.setitem(sys.modules, "win32com.propsys", MagicMock())
87
+ return link
88
+
89
+
90
+ def test_new_com_shortcut_has_the_packaged_icon(com_link: MagicMock, tmp_path: Path) -> None:
91
+ from pyobs_gui.appicon import icon_path
92
+ from pyobs_gui.windows_identity import create_link_com
93
+
94
+ create_link_com(tmp_path / SHORTCUT_NAME, "python.exe", "-m pyobs_gui", "pyobs-gui")
95
+ com_link.SetIconLocation.assert_called_once_with(str(icon_path("ico")), 0)
96
+ com_link.SetPath.assert_called_once_with("python.exe")
97
+ com_link.SetArguments.assert_called_once_with("-m pyobs_gui")
98
+
99
+
100
+ def test_existing_com_shortcut_gets_icon_without_changing_target(com_link: MagicMock, tmp_path: Path) -> None:
101
+ from pyobs_gui.appicon import icon_path
102
+ from pyobs_gui.windows_identity import update_link_icon_com
103
+
104
+ path = tmp_path / SHORTCUT_NAME
105
+ com_link.GetIconLocation.return_value = ("python.exe", 0)
106
+ assert update_link_icon_com(path) is True
107
+ com_link.QueryInterface.return_value.Load.assert_called_once_with(str(path))
108
+ com_link.SetIconLocation.assert_called_once_with(str(icon_path("ico")), 0)
109
+ com_link.QueryInterface.return_value.Save.assert_called_once_with(str(path), 0)
110
+ com_link.SetPath.assert_not_called()
111
+ com_link.SetArguments.assert_not_called()
112
+
113
+
114
+ def test_matching_com_shortcut_is_not_rewritten(com_link: MagicMock, tmp_path: Path) -> None:
115
+ from pyobs_gui.appicon import icon_path
116
+ from pyobs_gui.windows_identity import update_link_icon_com
117
+
118
+ com_link.GetIconLocation.return_value = (str(icon_path("ico")), 0)
119
+ assert update_link_icon_com(tmp_path / SHORTCUT_NAME) is False
120
+ com_link.SetIconLocation.assert_not_called()
121
+ com_link.QueryInterface.return_value.Save.assert_not_called()