Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5f7a88b
chore(ui): delete 11 unreferenced generated UI modules
gmmcosta15 Aug 31, 2026
974fdd3
test(ui): guard qrc resource keys against broken and stale references
gmmcosta15 Aug 31, 2026
b631019
fix(ui): repair broken resource keys and harden the BlocksLabel icon …
gmmcosta15 Sep 1, 2026
0df798a
fix(ui): delete dead pixmap references and repair the z-adjust back icon
gmmcosta15 Sep 1, 2026
2c028ca
fix(ui): give warning popups an icon and clear it for unknown severity
gmmcosta15 Sep 1, 2026
80a9c63
chore(ui): dedupe 10 double-exported assets and drop the dead system qrc
gmmcosta15 Sep 1, 2026
4ec1196
fix(topbar): render filament icons in the bundled Momcake face
gmmcosta15 Sep 1, 2026
f4d4483
chore(resources): drop 77 assets no branch references
gmmcosta15 Sep 1, 2026
25aca92
test(resources): scan .ui files and space-containing resource keys
gmmcosta15 Sep 1, 2026
71d697e
feat(pixmap): add BlocksPixmap registry and Icon key enum
gmmcosta15 Sep 1, 2026
de0b292
refactor(pixmap): route network, header and files icons through Block…
gmmcosta15 Sep 1, 2026
9dc9b4e
refactor(pixmap): route all panel icons through BlocksPixmap
gmmcosta15 Sep 2, 2026
e5e05b0
test(resources): scan stylesheet url() keys and collapse comments to …
gmmcosta15 Sep 2, 2026
103065b
perf(pixmap): unify the registry on get() and bound the caches by bytes
gmmcosta15 Sep 2, 2026
489c304
fix(loadwidget): scope intro gif to boot only, drop dead divided-intr…
gmmcosta15 Sep 3, 2026
cba9498
chore(resources): rename/dedupe asset filenames, drop 59 dead SVGs, a…
gmmcosta15 Sep 3, 2026
9b12433
chore(resources): reorganize btn_icons into per-prefix subfolders
gmmcosta15 Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 5 additions & 2 deletions BlocksScreen/BlocksScreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from configfile import get_configparser
from lib.panels.mainWindow import MainWindow # noqa: E402
from lib.panels.mainWindow import MainWindow
from lib.utils.blocks_pixmap import BlocksPixmap
from logger import CrashHandler, LogManager, install_crash_handler, setup_logging
from PyQt6 import QtCore, QtGui, QtWidgets # noqa: E402
from PyQt6 import QtCore, QtGui, QtWidgets
from tools.configuration_manager import ConfigManager

install_crash_handler()
Expand Down Expand Up @@ -167,6 +168,8 @@ def _handler(__signum: int, __frame: typing.Any) -> None:

def on_quit() -> None:
logging.info("Final exit cleanup")
# aboutToQuit still has a live qApp, which QPixmap destruction requires.
BlocksPixmap.clear()
LogManager.shutdown()


Expand Down
6 changes: 0 additions & 6 deletions BlocksScreen/lib/panels/controlTab.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,6 @@ def __init__(
)
)

self.path = {
"fan_cage": QtGui.QPixmap(":/fan_related/media/btn_icons/fan_cage.svg"),
"blower": QtGui.QPixmap(":/fan_related/media/btn_icons/blower.svg"),
"fan": QtGui.QPixmap(":/fan_related/media/btn_icons/fan.svg"),
}

self.panel.cp_z_tilt_btn.clicked.connect(lambda: self.handle_ztilt())

self.printcores_page.pc_accept.clicked.connect(self.handle_swapcore)
Expand Down
24 changes: 10 additions & 14 deletions BlocksScreen/lib/panels/filamentTab.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging
from collections import deque
from typing import Deque

from devices.amu import AMUManager
from devices.amu.models import GateStatus
Expand All @@ -18,6 +17,7 @@
from lib.utils.blocks_button import BlocksCustomButton
from lib.utils.blocks_frame import BlocksCustomFrame
from lib.utils.blocks_linedit import BlocksCustomLinEdit
from lib.utils.blocks_pixmap import BlocksPixmap, Icon
from lib.utils.icon_button import IconButton
from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem
from PyQt6 import QtCore, QtGui, QtWidgets
Expand Down Expand Up @@ -52,7 +52,7 @@ def __init__(

self._previous_gate_states: dict[int, bool] = {}
self.pre_gate_idx = {}
self.popup_gates: Deque = deque()
self.popup_gates: deque = deque()
self._spool_id_map: dict[str, dict] = {}
self._current_field: QtWidgets.QLineEdit | None = None
self._color_target_field = None
Expand Down Expand Up @@ -175,7 +175,7 @@ def _build_form_page(self) -> QtWidgets.QWidget:
back_btn = IconButton(page)
back_btn.setFixedSize(QtCore.QSize(60, 60))
back_btn.setFlat(True)
back_btn.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back.svg"))
back_btn.setPixmap(BlocksPixmap.get(Icon.BACK))
back_btn.clicked.connect(lambda: self.popup.hide())
hdr.addWidget(back_btn)

Expand Down Expand Up @@ -290,17 +290,15 @@ def _update_swatch():
skip_btn.setText("Skip")
skip_btn.setFont(font)
skip_btn.clicked.connect(self.handle_skip_button)
skip_btn.setPixmap(
QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg")
)
skip_btn.setPixmap(BlocksPixmap.get(Icon.RIGHT_ARROW))
btn_row.addWidget(skip_btn)

accept_btn = BlocksCustomButton(page)
accept_btn.setFixedSize(230, 80)
accept_btn.setText("Accept")
accept_btn.setFont(font)
accept_btn.clicked.connect(self.on_popup_accept)
accept_btn.setPixmap(QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg"))
accept_btn.setPixmap(BlocksPixmap.get(Icon.YES))
btn_row.addWidget(accept_btn)

root.addLayout(btn_row)
Expand Down Expand Up @@ -331,7 +329,7 @@ def _build_spool_page(self) -> QtWidgets.QWidget:
back_btn = IconButton(page)
back_btn.setFixedSize(QtCore.QSize(60, 60))
back_btn.setFlat(True)
back_btn.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back.svg"))
back_btn.setPixmap(BlocksPixmap.get(Icon.BACK))
back_btn.clicked.connect(lambda: self.popup.hide())
hdr.addWidget(back_btn)

Expand Down Expand Up @@ -486,15 +484,13 @@ def _add_row(title_text: str) -> QtWidgets.QLabel:
self.skip_btn.setText("Skip")
self.skip_btn.setFixedSize(QtCore.QSize(230, 80))
self.skip_btn.setFont(font)
self.skip_btn.setPixmap(
QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg")
)
self.skip_btn.setPixmap(BlocksPixmap.get(Icon.RIGHT_ARROW))
self.skip_btn.clicked.connect(self.handle_skip_button)

self.accept_btn = BlocksCustomButton(frame_2)
self.accept_btn.setText("Accept")
self.accept_btn.setFixedSize(QtCore.QSize(230, 80))
self.accept_btn.setPixmap(QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg"))
self.accept_btn.setPixmap(BlocksPixmap.get(Icon.YES))
self.accept_btn.setFont(font)
self.accept_btn.clicked.connect(lambda: self._on_spool_selected())

Expand Down Expand Up @@ -958,7 +954,7 @@ def setupUi(self):
self.fp_button_1.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.fp_button_1.setProperty(
"icon_pixmap",
QtGui.QPixmap(":/filament_related/media/btn_icons/load_filament.svg"),
BlocksPixmap.get(Icon.LOAD_FILAMENT),
)
self.fp_button_1.setObjectName("fp_button_1")

Expand All @@ -973,7 +969,7 @@ def setupUi(self):
self.fp_button_2.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.fp_button_2.setProperty(
"icon_pixmap",
QtGui.QPixmap(":/filament_related/media/btn_icons/base dados spool 1.svg"),
BlocksPixmap.get(Icon.SPOOL_DATABASE_1),
)
self.fp_button_2.setObjectName("fp_button_2")

Expand Down
70 changes: 39 additions & 31 deletions BlocksScreen/lib/panels/mainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from devices.amu import AMUManager
from devices.storage import USBManager
from lib.files import Files
from lib.klipper_message_filter import ( # noqa: F405
from lib.klipper_message_filter import (
MessageSource,
Severity,
match_message,
Expand All @@ -19,7 +19,7 @@
from lib.network import WifiIconKey
from lib.panels.controlTab import ControlTab
from lib.panels.filamentTab import FilamentTab
from lib.panels.networkWindow import NetworkControlWindow, PixmapCache
from lib.panels.networkWindow import NetworkControlWindow
from lib.panels.printTab import PrintTab
from lib.panels.utilitiesTab import UtilitiesTab
from lib.panels.widgets.basePopup import BasePopup
Expand All @@ -35,9 +35,10 @@
from lib.ui.resources.graphic_resources_rc import *
from lib.ui.resources.icon_resources_rc import *
from lib.ui.resources.main_menu_resources_rc import *
from lib.ui.resources.system_resources_rc import *
from lib.ui.resources.top_bar_resources_rc import *
from lib.updater_worker import UpdaterWorker
from lib.utils.blocks_pixmap import BlocksPixmap, Icon
from lib.utils.fonts import register_momcake
from PyQt6 import QtCore, QtGui, QtWidgets
from screensaver import ScreenSaver

Expand Down Expand Up @@ -66,29 +67,15 @@ def wrapper(*args, **kwargs):
class HeaderWifiIconProvider:
"""Resolves WifiIconKey integer values to cached QPixmaps for the header bar."""

_WIFI_PATHS: dict[tuple[int, bool], str] = {
(
b,
p,
): f":/network/media/btn_icons/network/{b}bar_wifi{'_protected' if p else ''}.svg"
for b in range(5)
for p in (False, True)
}
_ETHERNET_PATH = ":/network/media/btn_icons/network/ethernet_connected.svg"
_HOTSPOT_PATH = ":/network/media/btn_icons/hotspot.svg"

@classmethod
def get_pixmap(cls, icon_key: int) -> QtGui.QPixmap:
"""Resolve an icon key to a QPixmap (cached via PixmapCache)."""
"""Resolve an icon key to a QPixmap (cached via BlocksPixmap)."""
key = WifiIconKey(icon_key)
if key is WifiIconKey.ETHERNET:
return PixmapCache.get(cls._ETHERNET_PATH)
return BlocksPixmap.get(Icon.ETHERNET_CONNECTED)
if key is WifiIconKey.HOTSPOT:
return PixmapCache.get(cls._HOTSPOT_PATH)
path = cls._WIFI_PATHS.get(
(key.bars, key.is_protected), cls._WIFI_PATHS[(0, False)]
)
return PixmapCache.get(path)
return BlocksPixmap.get(Icon.HOTSPOT)
return BlocksPixmap.get(Icon.wifi(key.bars, key.is_protected))


class MainWindow(QtWidgets.QMainWindow):
Expand Down Expand Up @@ -120,8 +107,10 @@ class MainWindow(QtWidgets.QMainWindow):

def __init__(self):
"""Set up UI, instantiate subsystems, and wire all inter-component signals."""
super(MainWindow, self).__init__()
super().__init__()
self.config: BlocksScreenConfig = get_configparser()
# Before setupUi: the topbar .svg icons carry text and paint on first show.
register_momcake()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.screensaver = ScreenSaver(self)
Expand All @@ -139,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 @@ -319,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 @@ -352,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 @@ -388,11 +392,14 @@ def show_loadscreen(
if not force:
if _sender is self.update_page:
self._update_in_progress = show
if not show and self._post_update_reconnect:
return
elif not show and self._update_in_progress:
return
elif not show and self._klipper_auto_restart_pending:
if (
not show
and self._post_update_reconnect
or not show
and self._update_in_progress
or not show
and self._klipper_auto_restart_pending
):
return

if _sender == self.filamentPanel:
Expand Down Expand Up @@ -438,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 @@ -450,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 @@ -526,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 @@ -538,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
Loading
Loading