diff --git a/BlocksScreen/lib/panels/filamentTab.py b/BlocksScreen/lib/panels/filamentTab.py index 65d89a69..f1a7f243 100644 --- a/BlocksScreen/lib/panels/filamentTab.py +++ b/BlocksScreen/lib/panels/filamentTab.py @@ -188,6 +188,12 @@ def in_case_error(self): self.load_state = False self.load_popup.hide() + @QtCore.pyqtSlot(name="hide_load_popup") + def hide_load_popup(self) -> None: + """Hide the load/unload popup; a later Loading/Unloading state shows it again.""" + self.load_state = False + self.load_popup.hide() + @QtCore.pyqtSlot(list, name="handle_moonraker_components") def handle_moonraker_components(self, components: list) -> None: """Re-check spoolman on each server.info: updates add it without a UI restart.""" diff --git a/BlocksScreen/lib/panels/mainWindow.py b/BlocksScreen/lib/panels/mainWindow.py index 40da2171..ec2410dc 100644 --- a/BlocksScreen/lib/panels/mainWindow.py +++ b/BlocksScreen/lib/panels/mainWindow.py @@ -216,6 +216,7 @@ def __init__(self): self.printPanel.on_cancel_print.connect(slot=self.on_cancel_print) self.in_case_error.connect(self.printPanel.in_case_error) self.in_case_error.connect(self.filamentPanel.in_case_error) + self.conn_window.page_shown.connect(self.filamentPanel.hide_load_popup) self.show_notifications.connect(self.notiPage.new_notication) diff --git a/BlocksScreen/lib/panels/widgets/MainWindow/connectionPage.py b/BlocksScreen/lib/panels/widgets/MainWindow/connectionPage.py index cc5f8d5e..96dae1e8 100644 --- a/BlocksScreen/lib/panels/widgets/MainWindow/connectionPage.py +++ b/BlocksScreen/lib/panels/widgets/MainWindow/connectionPage.py @@ -127,6 +127,9 @@ class ConnectionPage(QtWidgets.QFrame): call_cancel_panel: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( bool, name="call-cancel-panel" ) + page_shown: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + name="connection-page-shown" + ) def __init__(self, parent: QtWidgets.QWidget, ws: MoonWebSocket, /) -> None: super().__init__(parent) @@ -402,6 +405,8 @@ def showEvent(self, a0: QtGui.QShowEvent | None) -> None: self.ws.api.refresh_update_status() self.call_load_panel.emit(False, "", False) self.call_cancel_panel.emit(False) + # Top-level popups (e.g. filament load) would stay above this child frame. + self.page_shown.emit() super().showEvent(a0) def eventFilter(self, a0: QtCore.QObject | None, a1: QtCore.QEvent | None) -> bool: diff --git a/tests/widgets/test_connection_page.py b/tests/widgets/test_connection_page.py index 3375126d..cb25d155 100644 --- a/tests/widgets/test_connection_page.py +++ b/tests/widgets/test_connection_page.py @@ -146,6 +146,17 @@ def test_conn_toggle_false_suppresses_show(self, shown_page): shown_page._set_state(ConnectionState.DISCONNECTED) assert not shown_page.isVisible() + def test_show_emits_page_shown(self, shown_page, qtbot): + shown_page.hide() + with qtbot.waitSignal(shown_page.page_shown): + shown_page._set_state(ConnectionState.WEBSOCKET_LOST) + + def test_manual_show_emits_page_shown_with_toggle_off(self, page, qtbot): + # Top-level popups cover this frame whether or not it auto-showed. + page.hide() + with qtbot.waitSignal(page.page_shown): + page.show() + class TestRestartButton: def test_error_state_emits_firmware_restart(self, page, qtbot): diff --git a/tests/widgets/test_filament_tab_mmu_unit.py b/tests/widgets/test_filament_tab_mmu_unit.py index 7ad6055e..3df94f79 100644 --- a/tests/widgets/test_filament_tab_mmu_unit.py +++ b/tests/widgets/test_filament_tab_mmu_unit.py @@ -42,3 +42,10 @@ def test_none_state_after_klipper_restart_is_ignored() -> None: tab = MagicMock() FilamentTab.on_mmu_state_changed(tab, None) assert tab.mock_calls == [] + + +def test_hide_load_popup_clears_load_state_and_hides() -> None: + tab = MagicMock(load_state=True) + FilamentTab.hide_load_popup(tab) + assert tab.load_state is False + tab.load_popup.hide.assert_called_once_with()