Skip to content
Open
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
e14dc70
bugfix: fix 30+ Pi runtime crashes across UI panels and widgets
gmmcosta15 Apr 13, 2026
bc9f17c
test: update networkWindow QTimer patch path to QtCore.QTimer
gmmcosta15 Apr 13, 2026
965193b
fix: add set_filament_state to SensorWidget; remove dead change_fil_s…
gmmcosta15 Apr 13, 2026
f1ebc12
Merge branch 'dev' into bugfix/ui-panel-crashes
gmmcosta15 May 25, 2026
4686a2b
Merge remote-tracking branch 'origin/dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 16, 2026
ae97e93
fix:resolve conflicts in filamentTab/mainWindow/connectionPage/notifi…
gmmcosta15 Jun 16, 2026
61c40c5
Merge branch 'dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 16, 2026
e26bdae
Merge branch 'dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 16, 2026
fca4c48
Merge branch 'dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 17, 2026
ecde9eb
Merge remote-tracking branch 'origin/dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 19, 2026
23c0ffe
Merge branch 'dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 19, 2026
49c65e1
Merge remote-tracking branch 'origin/dev' into bugfix/ui-panel-crashes
gmmcosta15 Jun 30, 2026
077da1e
Merge branch 'bugfix/ui-panel-crashes' into dev
gmmcosta15 Aug 20, 2026
d53f7a1
Merge remote-tracking branch 'origin/dev' into dev
gmmcosta15 Aug 20, 2026
2e96451
style: ruff format after origin/dev merge
gmmcosta15 Aug 20, 2026
357846b
debug: add DIAG state-transition logging for cancel-page completion-t…
gmmcosta15 Aug 28, 2026
37e095d
fix(cancelPage): clear stale reason text on new print + reword comple…
gmmcosta15 Aug 31, 2026
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
5 changes: 2 additions & 3 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 Down Expand Up @@ -52,7 +51,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 @@ -118,7 +117,7 @@ def __init__(
def handle_moonraker_components(self):
if self.moonraker_run:
components = self.ws._moonRest.get_server_info()
if "spoolman" not in components["result"].get("components", []):
if "spoolman" not in components.get("result", {}).get("components", []):
self.fp_button_2.hide()
self._popup_stack.addWidget(self._build_form_page())
self._popup_stack.addWidget(self._build_spool_page())
Expand Down
21 changes: 12 additions & 9 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 Down Expand Up @@ -120,7 +120,7 @@ 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()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
Expand Down Expand Up @@ -320,7 +320,7 @@ def __init__(self):
)
self.loadscreen.add_widget(self.loadwidget)
self.controlPanel.toggle_conn_page.connect(self.conn_window.set_toggle)
self.cancelpage = CancelPage(self, ws=self.ws)
self.cancelpage = CancelPage(self)
self.cancelpage.request_file_info.connect(self.file_data.on_request_fileinfo)
self.cancelpage.run_gcode.connect(self.ws.api.run_gcode)
self.printer.print_stats_update[str, str].connect(
Expand Down Expand Up @@ -388,11 +388,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 @@ -1052,7 +1055,7 @@ def _on_probe_notification(
@api_handler
def _handle_notify_gcode_response_message(self, method, data, metadata) -> None:
"""Handle websocket gcode responses messages"""
_gcode_response = data.get("params")
_gcode_response = data.get("params", [])
self.gcode_response[list].emit(_gcode_response)
if _gcode_response:
if self._popup_toggle:
Expand Down
43 changes: 24 additions & 19 deletions BlocksScreen/lib/panels/networkWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
from lib.utils.icon_button import IconButton
from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import QTimer, pyqtSlot

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -255,15 +254,15 @@ def _prefill_ip_from_os(self) -> None:
except OSError:
continue

@pyqtSlot()
@QtCore.pyqtSlot()
def _on_reconnect_complete(self) -> None:
"""Navigate back to the main panel after a static-IP or DHCP-reset operation."""
logger.debug("reconnect_complete received — navigating to main_network_page")
self.setCurrentIndex(self.indexOf(self.main_network_page))

def _init_timers(self) -> None:
"""Initialize timers."""
self._load_timer = QTimer(self)
self._load_timer = QtCore.QTimer(self)
self._load_timer.setSingleShot(True)
self._load_timer.timeout.connect(self._handle_load_timeout)

Expand All @@ -277,7 +276,7 @@ def _init_model_view(self) -> None:
self._entry_delegate.item_selected.connect(self._on_ssid_item_clicked)
self._configure_list_view_palette()

@pyqtSlot(NetworkState)
@QtCore.pyqtSlot(NetworkState)
def _on_network_state_changed(self, state: NetworkState) -> None:
"""React to a NetworkState update: sync toggles, populate header and connection info."""
logger.debug(
Expand Down Expand Up @@ -438,7 +437,7 @@ def _on_network_state_changed(self, state: NetworkState) -> None:
self._emit_status_icon(state)
self._sync_active_network_list_icon(state)

@pyqtSlot(list)
@QtCore.pyqtSlot(list)
def _on_scan_complete(self, networks: list[NetworkInfo]) -> None:
"""Receive scan results, filter/sort them, and rebuild the SSID list view.

Expand All @@ -457,9 +456,11 @@ def _on_scan_complete(self, networks: list[NetworkInfo]) -> None:
# Stamp the connected AP as ACTIVE so the list is correct on first
# render even when the scan ran before the connection fully settled.
filtered = [
replace(net, network_status=NetworkStatus.ACTIVE)
if net.ssid == current_ssid
else net
(
replace(net, network_status=NetworkStatus.ACTIVE)
if net.ssid == current_ssid
else net
)
for net in filtered
]
active = next((n for n in filtered if n.ssid == current_ssid), None)
Expand All @@ -478,12 +479,12 @@ def _on_scan_complete(self, networks: list[NetworkInfo]) -> None:
state = self._nm.current_state
self._emit_status_icon(state)

@pyqtSlot(list)
@QtCore.pyqtSlot(list)
def _on_saved_networks_loaded(self, networks: list[SavedNetwork]) -> None:
"""Receive saved-network data and update the priority spinbox for the active SSID."""
logger.debug("Loaded %d saved networks", len(networks))

@pyqtSlot(ConnectionResult)
@QtCore.pyqtSlot(ConnectionResult)
def _on_operation_complete(self, result: ConnectionResult) -> None:
"""Handle network operation completion."""
logger.debug("Operation: success=%s, msg=%s", result.success, result.message)
Expand Down Expand Up @@ -570,15 +571,15 @@ def _on_operation_complete(self, result: ConnectionResult) -> None:
result.message,
)
ssid = self._target_ssid
QTimer.singleShot(
QtCore.QTimer.singleShot(
2000, lambda _ssid=ssid: self._nm.connect_network(_ssid)
)
return # Keep loading visible; state machine handles completion

self._clear_loading()
self._show_error_popup(result.message)

@pyqtSlot(str, str)
@QtCore.pyqtSlot(str, str)
def _on_network_error(self, operation: str, message: str) -> None:
"""Log network errors and surface critical failures in the info box."""
logger.error("Network error [%s]: %s", operation, message)
Expand Down Expand Up @@ -658,13 +659,15 @@ def _sync_active_network_list_icon(self, state: NetworkState) -> None:

# Update the cached entry with the authoritative signal and status
updated = [
replace(
net,
signal_strength=self._active_signal,
network_status=NetworkStatus.ACTIVE,
(
replace(
net,
signal_strength=self._active_signal,
network_status=NetworkStatus.ACTIVE,
)
if net.ssid == state.current_ssid
else net
)
if net.ssid == state.current_ssid
else net
for net in self._cached_scan_networks
]

Expand Down Expand Up @@ -1063,7 +1066,9 @@ def _handle_wifi_toggle(self, is_on: bool) -> None:
# Non-blocking: disable hotspot then connect
self._nm.toggle_hotspot(False)
_ssid_to_connect = self._target_ssid
QTimer.singleShot(500, lambda: self._nm.connect_network(_ssid_to_connect))
QtCore.QTimer.singleShot(
500, lambda: self._nm.connect_network(_ssid_to_connect)
)

def _handle_hotspot_toggle(self, is_on: bool) -> None:
"""Enable or disable the hotspot, enforcing the ethernet/Wi-Fi mutual-exclusion rule."""
Expand Down
16 changes: 10 additions & 6 deletions BlocksScreen/lib/panels/utilitiesTab.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import re
import typing
from dataclasses import dataclass
Expand All @@ -15,6 +16,8 @@
from lib.utils.toggleAnimatedButton import ToggleAnimatedButton
from PyQt6 import QtCore, QtGui, QtWidgets

logger = logging.getLogger(__name__)


@dataclass
class LedState:
Expand Down Expand Up @@ -113,8 +116,8 @@ def __init__(
self.x_inputshaper: dict = {}
self.stepper_limits: dict = {}

self.current_object: typing.Optional[str] = None
self.current_process: typing.Optional[Process] = None
self.current_object: str | None = None
self.current_process: Process | None = None
self.axis_in: str = "x"
self.amount: int = 1
self.tb: bool = False
Expand Down Expand Up @@ -254,8 +257,9 @@ def handle_gcode_response(self, data: list[str]) -> None:
"""

if not isinstance(data, list) or len(data) != 1 or not isinstance(data[0], str):
print(
f"WARNING: Invalid input format. Expected a list with one string. Received: {data}"
logger.warning(
"handle_gcode_response: invalid input format. Expected list[str], received: %r",
data,
)
return

Expand Down Expand Up @@ -322,7 +326,7 @@ def handle_gcode_response(self, data: list[str]) -> None:

self.is_page.set_type_dictionary(self.is_types)
first_key = next(iter(reordered.keys()), None)
for key in reordered.keys():
for key in reordered:
if key == first_key:
self.is_page.add_type_entry(key, "Recommended type")
else:
Expand Down Expand Up @@ -375,7 +379,7 @@ def on_object_list(self, object_list: list) -> None:

@QtCore.pyqtSlot(dict, name="on_object_config")
@QtCore.pyqtSlot(list, name="on_object_config")
def on_object_config(self, config: typing.Union[dict, list]) -> None:
def on_object_config(self, config: dict | list) -> None:
"""Handle receiving printer object configurations"""
if not config:
return
Expand Down
34 changes: 11 additions & 23 deletions BlocksScreen/lib/panels/widgets/basePopup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import typing

from PyQt6 import QtCore, QtGui, QtWidgets


Expand Down Expand Up @@ -47,40 +45,33 @@ def __init__(
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True)
self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
else:
self.setStyleSheet(
"""
self.setStyleSheet("""
#MyParent {
background-image: url(:/background/media/1st_background.png);
}
"""
)
""")

def _update_button_style(self) -> None:
"""Applies the current color variables and adds the central border to the stylesheets."""
if not self.dialog:
return

if not self.floating:
self.confirm_button.setStyleSheet(
f"""
self.confirm_button.setStyleSheet(f"""
background-color: {self.confirm_bk_color};
color: {self.confirm_ft_color};
border: none;
padding: 10px;
"""
)
""")

self.cancel_button.setStyleSheet(
f"""
self.cancel_button.setStyleSheet(f"""
background-color: {self.cancel_bk_color};
color: {self.cancel_ft_color};
border: none;
padding: 10px;
"""
)
""")
else:
self.confirm_button.setStyleSheet(
f"""
self.confirm_button.setStyleSheet(f"""
background-color: {self.confirm_bk_color};
color: {self.confirm_ft_color};
border-top: none;
Expand All @@ -89,20 +80,17 @@ def _update_button_style(self) -> None:
border-right: 1px solid #80807e;
border-bottom-left-radius: 16px;
padding: 10px;
"""
)
""")

self.cancel_button.setStyleSheet(
f"""
self.cancel_button.setStyleSheet(f"""
background-color: {self.cancel_bk_color};
color: {self.cancel_ft_color};
border-left: 1px solid #80807e;;
border-bottom: 2px solid #80807e;
border-right: 2px solid #80807e;
border-bottom-right-radius: 16px;
padding: 10px;
"""
)
""")

def set_message(self, message: str) -> None:
self.label.setText(message)
Expand Down Expand Up @@ -152,7 +140,7 @@ def add_widget(self, widget: QtWidgets.QWidget) -> None:
layout.insertWidget(index, self.ui)
self.ui.show()

def _get_mainWindow_widget(self) -> typing.Optional[QtWidgets.QMainWindow]:
def _get_mainWindow_widget(self) -> QtWidgets.QMainWindow | None:
"""Get the main application window"""
app_instance = QtWidgets.QApplication.instance()
if not app_instance:
Expand Down
Loading
Loading