Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions BlocksScreen/lib/panels/mainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ def __init__(self):
self._klipper_restart_timeout.setSingleShot(True)
self._klipper_restart_timeout.setInterval(30_000)
self._klipper_restart_timeout.timeout.connect(self._on_klipper_restart_timeout)

self._intro_timeout = QtCore.QTimer(self)
self._intro_timeout.setSingleShot(True)
self._intro_timeout.setInterval(15_000)

self.ui.main_content_widget.setCurrentIndex(0)

usb_config = self.config.get_section("usb_manager", fallback=None)
Expand Down Expand Up @@ -308,6 +313,11 @@ def __init__(self):
self, LoadingOverlayWidget.AnimationGIF.DEFAULT
)
self.loadscreen.add_widget(self.loadwidget)
self.introscreen = BasePopup(self, floating=False, dialog=False)
self.introwidget = LoadingOverlayWidget(
self, LoadingOverlayWidget.AnimationGIF.PLACEHOLDER
)
self.introscreen.add_widget(self.introwidget)
self.controlPanel.toggle_conn_page.connect(self.conn_window.set_toggle)
self.cancelpage = CancelPage(self, ws=self.ws)
self.cancelpage.request_file_info.connect(self.file_data.on_request_fileinfo)
Expand Down Expand Up @@ -341,6 +351,11 @@ def __init__(self):
self.reset_tab_indexes()
self.conn_window.show()

self.introscreen.show()
self.introscreen.raise_()
self._intro_timeout.timeout.connect(self.introscreen.hide)
self._intro_timeout.start()

@QtCore.pyqtSlot(str, str, name="handleDisplayUpdate")
@QtCore.pyqtSlot(str, float, name="handleDisplayUpdate")
def _handle_display_status(self, name, value: str | float) -> None:
Expand Down Expand Up @@ -430,6 +445,9 @@ def show_update_page(self, fullscreen: bool):
@QtCore.pyqtSlot(str, name="on-klippy-state")
def _on_klippy_state(self, state: str) -> None:
self._klippy_ready = state == "ready"
if state == "ready" and self._intro_timeout.isActive():
self._intro_timeout.stop()
self.introscreen.hide()
if state == "shutdown":
if self._update_in_progress:
_logger.warning("Klipper E-stop detected — cancelling active update")
Expand All @@ -442,8 +460,6 @@ def _on_klippy_state(self, state: str) -> None:
):
_logger.info("Klipper disconnected — auto-restarting service")
self._klipper_auto_restart_pending = True
self.loadwidget.set_status_message("Restarting Klipper...")
self.loadscreen.show()
self._klipper_restart_timeout.start()
self.ws.api.restart_service("klipper")
elif state == "ready" and self._klipper_auto_restart_pending:
Expand Down Expand Up @@ -518,7 +534,7 @@ def on_updater_unavailable(self) -> None:

@QtCore.pyqtSlot(name="on-post-update-reconnect")
def _on_post_update_reconnect(self) -> None:
"""Called when an update finishes: hold the loading screen and retry Moonraker until it's up."""
"""Called when an update finishes: drop the overlay and retry Moonraker until it's up."""
_logger.debug(
"_on_post_update_reconnect: ws.connected=%s update_in_progress=%s",
self.ws.connected,
Expand All @@ -530,7 +546,7 @@ def _on_post_update_reconnect(self) -> None:
return
self._post_update_reconnect = True
self._reconnect_retries = 0
self.loadwidget.set_status_message("Reconnecting...")
self.loadscreen.hide()
self.ws.retry_wb_conn()
self._reconnect_timer.start()

Expand Down
81 changes: 34 additions & 47 deletions BlocksScreen/lib/panels/widgets/loadWidget.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from PyQt6 import QtCore, QtGui, QtWidgets
import enum
import os
from configfile import BlocksScreenConfig, get_configparser
from pathlib import Path

from PyQt6 import QtCore, QtGui, QtWidgets

_INTRO_GIF = Path(__file__).parents[2] / "ui" / "resources" / "intro_blocks.gif"


class LoadingOverlayWidget(QtWidgets.QLabel):
Expand Down Expand Up @@ -31,36 +33,14 @@ def __init__(

self._setupUI()

config: BlocksScreenConfig = get_configparser()
animation_path = None

if initial_anim_type == LoadingOverlayWidget.AnimationGIF.PLACEHOLDER:
animation_path = (
"~/BlocksScreen/BlocksScreen/lib/ui/resources/intro_blocks.gif"
)
self.anim_type = initial_anim_type

else:
try:
loading_config = config.loading
animation_path = loading_config.get(
str(initial_anim_type.name),
)
self.anim_type = initial_anim_type
except Exception:
self.anim_type = LoadingOverlayWidget.AnimationGIF.DEFAULT

if (
self.anim_type != LoadingOverlayWidget.AnimationGIF.DEFAULT
and animation_path
):
abs_animation_path = os.path.expanduser(animation_path)

self.movie = QtGui.QMovie(abs_animation_path)
self.anim_type = initial_anim_type
if self.anim_type == LoadingOverlayWidget.AnimationGIF.PLACEHOLDER:
self.movie = QtGui.QMovie(str(_INTRO_GIF))
self._movie_size = QtGui.QImageReader(str(_INTRO_GIF)).size()

if self.movie.isValid():
self.gifshow.setMovie(self.movie)
self.gifshow.setScaledContents(True)
self.gifshow.setScaledContents(False)
self.movie.start()
self.gifshow.show()
else:
Expand All @@ -77,19 +57,22 @@ def __init__(
self.label.setText("Loading...")
self.repaint()

def set_animation_path(self, path: str) -> None:
def set_animation_path(self, path: str | Path) -> None:
"""Set widget animation path"""
abs_animation_path = os.path.expanduser(path)
if os.path.isfile(abs_animation_path):
self.movie = QtGui.QMovie(abs_animation_path)
if self.movie.isValid():
self.gifshow.setMovie(self.movie)
self.gifshow.setScaledContents(True)
self.movie.start()
self.gifshow.show()
self.anim_type = LoadingOverlayWidget.AnimationGIF.PLACEHOLDER
if self.timer.isActive():
self.timer.stop()
gif = Path(path).expanduser()
if not gif.is_file():
return
self.movie = QtGui.QMovie(str(gif))
self._movie_size = QtGui.QImageReader(str(gif)).size()

if self.movie.isValid():
self.gifshow.setMovie(self.movie)
self.gifshow.setScaledContents(False)
self.movie.start()
self.gifshow.show()
self.anim_type = LoadingOverlayWidget.AnimationGIF.PLACEHOLDER
if self.timer.isActive():
self.timer.stop()

def set_status_message(self, message: str) -> None:
"""Set widget message"""
Expand Down Expand Up @@ -161,12 +144,16 @@ def resizeEvent(self, a0: QtGui.QResizeEvent | None) -> None:
margin = 20
self.label.setGeometry(label_x, label_y, label_width, label_height)
gifshow_max_height = label_y - margin
size = min(self.width() - margin * 2, gifshow_max_height)

gifshow_x = (self.width() - size) // 2
gifshow_y = (gifshow_max_height - size) // 2

self.gifshow.setGeometry(gifshow_x, gifshow_y, size, size)
self.gifshow.setGeometry(
margin, 0, self.width() - margin * 2, gifshow_max_height
)
if self.anim_type != LoadingOverlayWidget.AnimationGIF.DEFAULT:
self.movie.setScaledSize(
self._movie_size.scaled(
self.gifshow.size(), QtCore.Qt.AspectRatioMode.KeepAspectRatio
)
)

def show(self) -> None:
"""Re-implemented method, show widget"""
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed BlocksScreen/lib/ui/resources/media/intro_blocks.gif
Diff not rendered.
3 changes: 2 additions & 1 deletion tests/panels/test_main_window_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def _make_fake_window(*, manual_restart_pending: bool) -> SimpleNamespace:
_klipper_auto_restart_pending=False,
_post_update_reconnect=False,
_klipper_restart_timeout=MagicMock(),
_intro_timeout=MagicMock(),
updater_worker=MagicMock(),
conn_window=SimpleNamespace(manual_restart_pending=manual_restart_pending),
loadwidget=MagicMock(),
Expand All @@ -30,7 +31,7 @@ def test_auto_restarts_when_no_manual_restart_pending(self) -> None:
MainWindow._on_klippy_state(fake_window, "disconnected")

fake_window.ws.api.restart_service.assert_called_once_with("klipper")
fake_window.loadscreen.show.assert_called_once()
fake_window.loadscreen.show.assert_not_called()
assert fake_window._klipper_auto_restart_pending is True

def test_skips_auto_restart_when_manual_restart_pending(self) -> None:
Expand Down
Loading