From a31283a5c611e3d172d58ebacc60d7d91e6a4694 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Mon, 13 Apr 2026 17:52:15 +0100 Subject: [PATCH 1/2] refactor: modernise typing to Python 3.11 native syntax (21 files) --- BlocksScreen/BlocksScreen.py | 3 +- BlocksScreen/lib/filament.py | 31 +++++++++---------- BlocksScreen/lib/files.py | 21 ++++++------- BlocksScreen/lib/moonrest.py | 9 +++--- .../lib/panels/widgets/bannerPopup.py | 2 ++ BlocksScreen/lib/panels/widgets/fansPage.py | 9 +++--- BlocksScreen/lib/panels/widgets/filesPage.py | 9 +++--- BlocksScreen/lib/panels/widgets/numpadPage.py | 3 +- .../lib/panels/widgets/optionCardWidget.py | 2 +- .../lib/panels/widgets/popupDialogWidget.py | 8 ++--- .../lib/panels/widgets/printcorePage.py | 2 ++ .../lib/panels/widgets/sensorWidget.py | 15 ++++----- BlocksScreen/lib/panels/widgets/updatePage.py | 21 +++++++------ BlocksScreen/lib/qrcode_gen.py | 3 +- BlocksScreen/lib/utils/blocks_button.py | 15 ++------- BlocksScreen/lib/utils/blocks_frame.py | 3 +- BlocksScreen/lib/utils/blocks_progressbar.py | 15 ++++++--- BlocksScreen/lib/utils/blocks_tabwidget.py | 2 +- BlocksScreen/lib/utils/check_button.py | 3 +- BlocksScreen/lib/utils/icon_button.py | 3 +- .../lib/utils/toggleAnimatedButton.py | 11 +++++-- tests/network/test_network_ui.py | 4 +-- 22 files changed, 99 insertions(+), 95 deletions(-) diff --git a/BlocksScreen/BlocksScreen.py b/BlocksScreen/BlocksScreen.py index ab198a7e..dab6cb59 100644 --- a/BlocksScreen/BlocksScreen.py +++ b/BlocksScreen/BlocksScreen.py @@ -1,6 +1,5 @@ import logging import sys -import typing from logger import CrashHandler, LogManager, install_crash_handler, setup_logging @@ -39,7 +38,7 @@ def notify(self, a0: QtCore.QObject, a1: QtCore.QEvent) -> bool: # type: ignore RESET = "\033[0m" -def show_splash(window: typing.Optional[QtWidgets.QWidget] = None): +def show_splash(window: QtWidgets.QWidget | None = None): """Show splash screen on app initialization""" logo = QtGui.QPixmap("BlocksScreen/BlocksScreen/lib/ui/resources/logoblocks.png") splash = QtWidgets.QSplashScreen(pixmap=logo) diff --git a/BlocksScreen/lib/filament.py b/BlocksScreen/lib/filament.py index cb4c0232..0c3d0b44 100644 --- a/BlocksScreen/lib/filament.py +++ b/BlocksScreen/lib/filament.py @@ -1,6 +1,7 @@ # Class that represents a filament spool -from typing import Optional +from __future__ import annotations + import enum @@ -29,17 +30,18 @@ def __init__( self, name: str, temperature: int, - brand: Optional[str] = None, - spool_type: Optional[SpoolMaterial] = None, - spool_weight: Optional[float] = None, + brand: str | None = None, + spool_type: SpoolMaterial | None = None, + spool_weight: float | None = None, ): if not isinstance(name, str) or not isinstance(temperature, int): raise TypeError("__init__() invalid argument type") self._name: str = name self._temperature: int = temperature - self._weight: Optional[float] = None - self._brand: Optional[str] = brand + self._weight: float | None = None + self._brand: str | None = brand + self._spool_type: Filament.SpoolMaterial | None = None if spool_type is not None and spool_type in self.SpoolMaterial: self._spool_type = spool_type @@ -55,7 +57,7 @@ def temperature(self) -> int: return self._temperature @property - def weight(self) -> Optional[float]: + def weight(self) -> float | None: if self._weight is None: return return self._weight @@ -65,22 +67,19 @@ def weight(self, new_value: float): self._weight = new_value @property - def brand(self) -> Optional[str]: + def brand(self) -> str | None: return self._brand @brand.setter - def brand(self, new_value: str) -> Optional[str]: + def brand(self, new_value: str) -> None: self._brand = new_value @property - def spool_type(self) -> Optional[SpoolMaterial]: + def spool_type(self) -> SpoolMaterial | None: return self._spool_type @spool_type.setter - def spool_type(self, new): - if new not in self.SpoolMaterial: - if isinstance(new, self.SpoolMaterial): - raise ValueError( - "Spool Material type is invalid" - ) # Correct type but invalid option + def spool_type(self, new) -> None: + if new is not None and not isinstance(new, self.SpoolMaterial): + raise ValueError(f"Spool Material type is invalid: {new!r}") self._spool_type = new diff --git a/BlocksScreen/lib/files.py b/BlocksScreen/lib/files.py index 412f0648..87396151 100644 --- a/BlocksScreen/lib/files.py +++ b/BlocksScreen/lib/files.py @@ -8,7 +8,6 @@ from pathlib import Path import events -from events import ReceivedFileData from lib.moonrakerComm import MoonWebSocket from PyQt6 import QtCore, QtGui, QtWidgets @@ -54,7 +53,7 @@ class FileMetadata: filename: str = "" thumbnail_images: list[QtGui.QImage] = field(default_factory=list) - filament_total: typing.Union[dict, str, float] = field(default_factory=dict) + filament_total: dict | str | float = field(default_factory=dict) estimated_time: int = 0 layer_count: int = -1 total_layer: int = -1 @@ -74,8 +73,8 @@ class FileMetadata: slicer_version: str = "Unknown" gcode_start_byte: int = 0 gcode_end_byte: int = 0 - print_start_time: typing.Optional[float] = None - job_id: typing.Optional[str] = None + print_start_time: float | None = None + job_id: str | None = None def to_dict(self) -> dict: """Convert to dictionary for signal emission.""" @@ -255,7 +254,7 @@ def is_loaded(self) -> bool: """Check if initial load is complete.""" return self._initial_load_complete - def get_file_metadata(self, filename: str) -> typing.Optional[FileMetadata]: + def get_file_metadata(self, filename: str) -> FileMetadata | None: """Get cached metadata for a file.""" return self._files_metadata.get(filename.removeprefix("/")) @@ -279,7 +278,7 @@ def initial_load(self) -> None: self._initial_load_complete = False self.request_dir_info[str, bool].emit("", True) - def handle_filelist_changed(self, data: typing.Union[dict, list]) -> None: + def handle_filelist_changed(self, data: dict | list) -> None: """Handle notify_filelist_changed from Moonraker.""" if isinstance(data, dict) and "params" in data: data = data.get("params", []) @@ -492,7 +491,7 @@ def _process_metadata(self, data: dict) -> None: self.fileinfo.emit(metadata.to_dict()) logger.debug(f"Metadata loaded for: {filename}") - def handle_metadata_error(self, error_data: typing.Union[str, dict]) -> None: + def handle_metadata_error(self, error_data: str | dict) -> None: """ Handle metadata request error from Moonraker. @@ -538,7 +537,7 @@ def _preload_usb_contents(self, usb_path: str) -> None: self._usb_preload_queue.append(usb_path) self.ws.api.get_dir_information(usb_path, True) - def get_cached_usb_files(self, usb_path: str) -> typing.Optional[list[dict]]: + def get_cached_usb_files(self, usb_path: str) -> list[dict] | None: """ Get cached files for a USB path if available. @@ -646,7 +645,7 @@ def on_request_fileinfo(self, filename: str) -> None: @QtCore.pyqtSlot(str, bool, name="get_dir_info") def get_dir_information( self, directory: str = "", extended: bool = True - ) -> typing.Optional[list]: + ) -> list | None: """Get directory information.""" self._current_directory = directory @@ -669,8 +668,8 @@ def eventFilter(self, obj: QtCore.QObject, event: QtCore.QEvent) -> bool: def event(self, event: QtCore.QEvent) -> bool: """Handle object-level events.""" - if event.type() == ReceivedFileData.type(): - if isinstance(event, ReceivedFileData): + if event.type() == events.ReceivedFileData.type(): + if isinstance(event, events.ReceivedFileData): self.handle_message_received(event.method, event.data, event.params) return True return super().event(event) diff --git a/BlocksScreen/lib/moonrest.py b/BlocksScreen/lib/moonrest.py index 2c663531..079ca577 100644 --- a/BlocksScreen/lib/moonrest.py +++ b/BlocksScreen/lib/moonrest.py @@ -29,7 +29,6 @@ import logging import requests -from requests import Request, Response logger = logging.getLogger(__name__) @@ -38,7 +37,7 @@ class UncallableError(Exception): """Raised when a method is not callable""" def __init__(self, message="Unable to call method", errors=None): - super(UncallableError, self).__init__(message, errors) + super().__init__(message, errors) self.errors = errors self.message = message @@ -128,7 +127,7 @@ def _request( _headers = {"x-api-key": self._api_key} if self._api_key else {} try: if hasattr(requests, request_type): - _request_method: Request = getattr(requests, request_type) + _request_method = getattr(requests, request_type) if not callable(_request_method): raise UncallableError( "Invalid request method", @@ -142,9 +141,9 @@ def _request( headers=_headers, timeout=timeout, ) - if isinstance(response, Response): + if isinstance(response, requests.Response): response.raise_for_status() return response.json() if json_response else response.content except Exception as e: - logger.info(f"Unexpected error while sending HTTP request: {e}") + logger.info("Unexpected error while sending HTTP request: %s", e) diff --git a/BlocksScreen/lib/panels/widgets/bannerPopup.py b/BlocksScreen/lib/panels/widgets/bannerPopup.py index 7db54547..99077539 100644 --- a/BlocksScreen/lib/panels/widgets/bannerPopup.py +++ b/BlocksScreen/lib/panels/widgets/bannerPopup.py @@ -80,6 +80,8 @@ def _calculate_target_geometry(self) -> QtCore.QRect: if isinstance(widget, QtWidgets.QMainWindow): main_window = widget break + if main_window is None: + return QtCore.QRect() parent_rect = main_window.geometry() width = int(parent_rect.width() * 0.35) height = 80 diff --git a/BlocksScreen/lib/panels/widgets/fansPage.py b/BlocksScreen/lib/panels/widgets/fansPage.py index 925c0230..c31a8600 100644 --- a/BlocksScreen/lib/panels/widgets/fansPage.py +++ b/BlocksScreen/lib/panels/widgets/fansPage.py @@ -1,15 +1,14 @@ from PyQt6 import QtCore, QtWidgets -import typing class FansPage(QtWidgets.QWidget): def __init__( self, - parent: typing.Optional["QtWidgets.QWidget"], - flags: typing.Optional["QtCore.Qt.WindowType"], + parent: QtWidgets.QWidget | None, + flags: QtCore.Qt.WindowType | None, ) -> None: if parent is not None and flags is not None: - super(FansPage, self).__init__(parent, flags) + super().__init__(parent, flags) else: - super(FansPage, self).__init__() + super().__init__() diff --git a/BlocksScreen/lib/panels/widgets/filesPage.py b/BlocksScreen/lib/panels/widgets/filesPage.py index 969399ac..d731eb9c 100644 --- a/BlocksScreen/lib/panels/widgets/filesPage.py +++ b/BlocksScreen/lib/panels/widgets/filesPage.py @@ -1,6 +1,5 @@ import json import logging -import typing import helper_methods from lib.utils.blocks_Scrollbar import CustomScrollBar @@ -34,13 +33,13 @@ class FilesPage(QtWidgets.QWidget): ICON_PATHS = { "back_folder": ":/ui/media/btn_icons/back_folder.svg", "folder": ":/ui/media/btn_icons/folderIcon.svg", - "right_arrow": ":/arrow_icons/media/btn_icons/right_arrow.svg", + "right_arrow": ":/arrow_icons/media/btn_icons/arrow_right.svg", "usb": ":/ui/media/btn_icons/usb_icon.svg", "back": ":/ui/media/btn_icons/back.svg", "refresh": ":/ui/media/btn_icons/refresh.svg", } - def __init__(self, parent: typing.Optional[QtWidgets.QWidget] = None) -> None: + def __init__(self, parent: QtWidgets.QWidget | None = None) -> None: super().__init__(parent) self._file_list: list[dict] = [] @@ -263,7 +262,7 @@ def _find_file_insert_position(self, modified_time: float) -> int: return insert_pos - def _find_file_key_by_display_name(self, display_name: str) -> typing.Optional[str]: + def _find_file_key_by_display_name(self, display_name: str) -> str | None: """Find the file key in _files_data by its display name.""" for key in self._files_data: if self._get_display_name(key) == display_name: @@ -691,7 +690,7 @@ def _add_file_to_list(self, file_item: dict) -> None: if item: self._model.add_item(item) - def _create_file_list_item(self, filedata: dict) -> typing.Optional[ListItem]: + def _create_file_list_item(self, filedata: dict) -> ListItem | None: """Create a ListItem from file metadata.""" filename = filedata.get("filename", "") if not filename: diff --git a/BlocksScreen/lib/panels/widgets/numpadPage.py b/BlocksScreen/lib/panels/widgets/numpadPage.py index b904645c..40e222cc 100644 --- a/BlocksScreen/lib/panels/widgets/numpadPage.py +++ b/BlocksScreen/lib/panels/widgets/numpadPage.py @@ -1,7 +1,6 @@ -from lib.utils.icon_button import IconButton from lib.utils.blocks_label import BlocksLabel +from lib.utils.icon_button import IconButton from lib.utils.numpad_button import NumpadButton - from PyQt6 import QtCore, QtGui, QtWidgets diff --git a/BlocksScreen/lib/panels/widgets/optionCardWidget.py b/BlocksScreen/lib/panels/widgets/optionCardWidget.py index 6fbfe20d..e331049d 100644 --- a/BlocksScreen/lib/panels/widgets/optionCardWidget.py +++ b/BlocksScreen/lib/panels/widgets/optionCardWidget.py @@ -1,7 +1,7 @@ import typing -from PyQt6 import QtCore, QtGui, QtWidgets from lib.utils.icon_button import IconButton +from PyQt6 import QtCore, QtGui, QtWidgets class OptionCard(QtWidgets.QAbstractButton): diff --git a/BlocksScreen/lib/panels/widgets/popupDialogWidget.py b/BlocksScreen/lib/panels/widgets/popupDialogWidget.py index d565bd3a..b702e840 100644 --- a/BlocksScreen/lib/panels/widgets/popupDialogWidget.py +++ b/BlocksScreen/lib/panels/widgets/popupDialogWidget.py @@ -1,6 +1,5 @@ import enum from collections import deque -from typing import Deque from lib.utils.icon_button import IconButton from PyQt6 import QtCore, QtGui, QtWidgets @@ -26,9 +25,9 @@ def __init__(self, parent) -> None: super().__init__(parent) self.timeout_timer = QtCore.QTimer(self) self.timeout_timer.setSingleShot(True) - self.messages: Deque = deque() + self.messages: deque = deque() self.isShown = False - self.persistent_notifications: Deque = deque() + self.persistent_notifications: deque = deque() self.message_type: Popup.MessageType = Popup.MessageType.INFO self.default_background_color = QtGui.QColor(164, 164, 164) self.info_icon = QtGui.QPixmap(":ui/media/btn_icons/info.svg") @@ -81,7 +80,8 @@ def _calculate_target_geometry(self) -> QtCore.QRect: if isinstance(widget, QtWidgets.QMainWindow): main_window = widget break - + if main_window is None: + return QtCore.QRect() parent_rect = main_window.geometry() width = int(parent_rect.width() * 0.85) diff --git a/BlocksScreen/lib/panels/widgets/printcorePage.py b/BlocksScreen/lib/panels/widgets/printcorePage.py index c2683cd1..b5942e54 100644 --- a/BlocksScreen/lib/panels/widgets/printcorePage.py +++ b/BlocksScreen/lib/panels/widgets/printcorePage.py @@ -34,6 +34,8 @@ def _geometry_calc(self) -> None: for widget in app_instance.allWidgets(): if isinstance(widget, QtWidgets.QMainWindow): main_window = widget + if main_window is None: + return x = main_window.geometry().x() y = main_window.geometry().y() width = main_window.width() diff --git a/BlocksScreen/lib/panels/widgets/sensorWidget.py b/BlocksScreen/lib/panels/widgets/sensorWidget.py index e0ed9955..23fc46dc 100644 --- a/BlocksScreen/lib/panels/widgets/sensorWidget.py +++ b/BlocksScreen/lib/panels/widgets/sensorWidget.py @@ -37,10 +37,11 @@ class SensorState(enum.IntEnum): def __init__(self, parent, sensor_name: str): super().__init__(parent) - self.name = str(sensor_name).split(" ")[1] + _parts = str(sensor_name).split(" ", 1) + self.name = _parts[1] if len(_parts) > 1 else _parts[0] self.sensor_type: SensorWidget.SensorType = ( self.SensorType.SWITCH - if "switch" in str(sensor_name).split(" ")[0].lower() + if "switch" in _parts[0].lower() else self.SensorType.MOTION ) @@ -96,13 +97,13 @@ def text(self, new_text) -> None: self._text_label.setText(f"{new_text}") self._text = new_text - @QtCore.pyqtSlot(FilamentState, name="change_fil_sensor_state") - def change_fil_sensor_state(self, state: FilamentState): - """Invert the filament state in response to a Klipper update""" + def set_filament_state(self, state: FilamentState) -> None: + """Set the filament state directly from a Klipper update.""" if not isinstance(state, SensorWidget.FilamentState): return - self.filament_state = SensorWidget.FilamentState(not state.value) - self.update() + if self.filament_state != state: + self.filament_state = state + self.update() def toggle_button_state(self, state: ToggleAnimatedButton.State) -> None: """Called when the Klipper firmware reports an update to the filament sensor state""" diff --git a/BlocksScreen/lib/panels/widgets/updatePage.py b/BlocksScreen/lib/panels/widgets/updatePage.py index b91e41d3..15e72b00 100644 --- a/BlocksScreen/lib/panels/widgets/updatePage.py +++ b/BlocksScreen/lib/panels/widgets/updatePage.py @@ -101,7 +101,7 @@ def on_request_reload(self, service: str | None = None) -> None: """Handles reload button click, requests update status refresh""" self.show_loading(True) if service: - self.request_refresh_update.emit([service]) + self.request_refresh_update[str].emit(service) else: self.request_refresh_update.emit() @@ -197,14 +197,16 @@ def on_item_clicked(self, item: ListItem) -> None: if not _remote_version: self.remote_version_title.hide() self.remote_version_tracking.hide() - self.remote_version_title.show() - self.remote_version_tracking.show() - self.remote_version_title.setText("Remote Version: ") - self.remote_version_tracking.setText(_remote_version) + else: + self.remote_version_title.show() + self.remote_version_tracking.show() + self.remote_version_title.setText("Remote Version: ") + self.remote_version_tracking.setText(_remote_version) _curr_version = cli_data.get("version", None) if not _curr_version: # There is no version information something is seriously wrong here self.action_btn.setText("Recover") + return self.version_title.show() self.version_tracking_info.show() self.version_tracking_info.setText(_curr_version) @@ -295,7 +297,8 @@ def _setupUI(self) -> None: font_id = QtGui.QFontDatabase.addApplicationFont( ":/font/media/fonts for text/Momcake-Bold.ttf" ) - font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0] + _families = QtGui.QFontDatabase.applicationFontFamilies(font_id) + font_family = _families[0] if _families else "" sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding, @@ -304,11 +307,9 @@ def _setupUI(self) -> None: sizePolicy.setVerticalStretch(1) self.setSizePolicy(sizePolicy) self.setObjectName("updatePage") - self.setStyleSheet( - """#updatePage { + self.setStyleSheet("""#updatePage { background-image: url(:/background/media/1st_background.png); - }""" - ) + }""") self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) self.update_page_content_layout = QtWidgets.QVBoxLayout() self.update_page_content_layout.setContentsMargins(15, 15, 15, 15) diff --git a/BlocksScreen/lib/qrcode_gen.py b/BlocksScreen/lib/qrcode_gen.py index 1840f0ae..1caab81a 100644 --- a/BlocksScreen/lib/qrcode_gen.py +++ b/BlocksScreen/lib/qrcode_gen.py @@ -1,7 +1,6 @@ import qrcode - -from PyQt6.QtGui import QImage, QColor, QPainter from PyQt6.QtCore import Qt +from PyQt6.QtGui import QColor, QImage, QPainter BLOCKS_URL = "https://blockstec.com" RF50_MANUAL_PAGE = "https://blockstec.com/RF50" diff --git a/BlocksScreen/lib/utils/blocks_button.py b/BlocksScreen/lib/utils/blocks_button.py index 292b5125..ec6b24c7 100644 --- a/BlocksScreen/lib/utils/blocks_button.py +++ b/BlocksScreen/lib/utils/blocks_button.py @@ -1,5 +1,6 @@ -import typing import enum +import typing + from PyQt6 import QtCore, QtGui, QtWidgets @@ -106,7 +107,7 @@ def setProperty(self, name: str, value: typing.Any): self.text_color = QtGui.QColor(value) self.update() - def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): + def paintEvent(self, e: QtGui.QPaintEvent | None): """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(painter.RenderHint.Antialiasing, True) @@ -116,17 +117,7 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): _style = self.style() if not _style or not _rect: return - # Flat button control opt = QtWidgets.QStyleOptionButton() - draw_frame = ( - not self._is_flat - or self.underMouse() - or opt.state & QtWidgets.QStyle.StateFlag.State_Sunken - ) - if draw_frame: - _style.drawControl( - QtWidgets.QStyle.ControlElement.CE_PushButtonLabel, opt, painter, self - ) _style.drawControl( QtWidgets.QStyle.ControlElement.CE_PushButtonLabel, opt, painter, self ) diff --git a/BlocksScreen/lib/utils/blocks_frame.py b/BlocksScreen/lib/utils/blocks_frame.py index 7de7514e..8bd2d6b8 100644 --- a/BlocksScreen/lib/utils/blocks_frame.py +++ b/BlocksScreen/lib/utils/blocks_frame.py @@ -1,6 +1,7 @@ -from PyQt6 import QtCore, QtGui, QtWidgets import typing +from PyQt6 import QtCore, QtGui, QtWidgets + class BlocksCustomFrame(QtWidgets.QFrame): def __init__(self, parent=None): diff --git a/BlocksScreen/lib/utils/blocks_progressbar.py b/BlocksScreen/lib/utils/blocks_progressbar.py index 414097bb..60950b67 100644 --- a/BlocksScreen/lib/utils/blocks_progressbar.py +++ b/BlocksScreen/lib/utils/blocks_progressbar.py @@ -1,5 +1,6 @@ import typing -from PyQt6 import QtWidgets, QtGui, QtCore + +from PyQt6 import QtCore, QtGui, QtWidgets class CustomProgressBar(QtWidgets.QProgressBar): @@ -30,6 +31,12 @@ def __init__(self, parent=None): self.setMinimumSize(100, 100) self._inner_rect: QtCore.QRectF = QtCore.QRectF() + def reset(self) -> None: + """Reset progress to zero.""" + self.progress_value = 0 + super().reset() + self.update() + def set_padding(self, value) -> None: """Set widget padding""" self._padding = value @@ -93,8 +100,8 @@ def setValue(self, value: float) -> None: Raises: ValueError: If provided value in not between 0.0 and 1.0 """ - if not (0 <= value <= 100): - raise ValueError("Argument `value` expected value between 0.0 and 1.0 ") + if not (0.0 <= value <= 1.0): + raise ValueError("Argument `value` expected value between 0.0 and 1.0") value *= 100 self.progress_value = value self.update() @@ -159,7 +166,7 @@ def _draw_circular_bar( bg_pen.setCapStyle(QtCore.Qt.PenCapStyle.RoundCap) painter.setPen(bg_pen) painter.drawArc(arc_rect, arc_start, arc_span) - if self.progress_value is not None: + if self.progress_value is not None and self.progress_value > 0: gradient = QtGui.QConicalGradient(arc_rect.center(), -90) gradient.setColorAt(0.0, self._bar_color) gradient.setColorAt(1.0, QtGui.QColor(100, 100, 100)) diff --git a/BlocksScreen/lib/utils/blocks_tabwidget.py b/BlocksScreen/lib/utils/blocks_tabwidget.py index 4696d967..cc403bd0 100644 --- a/BlocksScreen/lib/utils/blocks_tabwidget.py +++ b/BlocksScreen/lib/utils/blocks_tabwidget.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets, QtGui, QtCore +from PyQt6 import QtCore, QtGui, QtWidgets class NotificationTabBar(QtWidgets.QTabBar): diff --git a/BlocksScreen/lib/utils/check_button.py b/BlocksScreen/lib/utils/check_button.py index e5b184d5..79c998c1 100644 --- a/BlocksScreen/lib/utils/check_button.py +++ b/BlocksScreen/lib/utils/check_button.py @@ -1,4 +1,3 @@ -import typing from PyQt6 import QtCore, QtGui, QtWidgets @@ -39,7 +38,7 @@ def setText(self, text: str | None) -> None: self.update() return - def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): + def paintEvent(self, e: QtGui.QPaintEvent | None): """Re-implemented method, paint widget, optimized for performance.""" painter = QtGui.QPainter(self) diff --git a/BlocksScreen/lib/utils/icon_button.py b/BlocksScreen/lib/utils/icon_button.py index 3880d285..55d9ebc1 100644 --- a/BlocksScreen/lib/utils/icon_button.py +++ b/BlocksScreen/lib/utils/icon_button.py @@ -1,4 +1,5 @@ import typing + from PyQt6 import QtCore, QtGui, QtWidgets @@ -135,7 +136,7 @@ def setProperty(self, name: str, value: typing.Any) -> bool: elif name == "has_text": self.has_text = value elif name == "name": - self._name = name + self._name = value elif name == "text_color": self.text_color = value return super().setProperty(name, value) diff --git a/BlocksScreen/lib/utils/toggleAnimatedButton.py b/BlocksScreen/lib/utils/toggleAnimatedButton.py index b9555876..371802eb 100644 --- a/BlocksScreen/lib/utils/toggleAnimatedButton.py +++ b/BlocksScreen/lib/utils/toggleAnimatedButton.py @@ -1,5 +1,6 @@ import enum import typing + from PyQt6 import QtCore, QtGui, QtWidgets @@ -33,6 +34,7 @@ def __init__(self, parent) -> None: - self.handle_radius * 2 ) + self.trailPath: QtGui.QPainterPath | None = None self.icon_pixmap: QtGui.QPixmap = QtGui.QPixmap() self._backgroundColor: QtGui.QColor = QtGui.QColor(223, 223, 223) self._handleColor: QtGui.QColor = QtGui.QColor(255, 100, 10) @@ -179,7 +181,7 @@ def setup_animation(self) -> None: def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: """Re-implemented method, handle mouse press events""" - if self.trailPath: + if self.trailPath is not None: if self.trailPath.contains(e.pos().toPointF()) and self.underMouse(): if not self.slide_animation.state == self.slide_animation.State.Running: self._state = ToggleAnimatedButton.State(not self._state.value) @@ -214,7 +216,10 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: rect_norm = _rect.toRectF().normalized() min_x = rect_norm.x() max_x = rect_norm.x() + rect_norm.width() - rect_norm.height() * 0.80 - progress = (self._handle_position - min_x) / (max_x - min_x) + denominator = max_x - min_x + if denominator == 0: + return + progress = (self._handle_position - min_x) / denominator progress = max(0.0, min(1.0, progress)) # Inline color interpolation (no separate functions) @@ -237,6 +242,8 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: self.handleColor = QtGui.QColor(int(r), int(g), int(b), int(a)) + if self.trailPath is None: + return painter.fillPath( self.trailPath, bg_color if self.isEnabled() else self.disable_bg_color, diff --git a/tests/network/test_network_ui.py b/tests/network/test_network_ui.py index 466ec14a..a60a330a 100644 --- a/tests/network/test_network_ui.py +++ b/tests/network/test_network_ui.py @@ -693,7 +693,7 @@ def test_transient_mismatch_retries(self, win, qapp): message="not compatible with device", error_code="nm_error", ) - with patch("BlocksScreen.lib.panels.networkWindow.QTimer") as mock_timer: + with patch("BlocksScreen.lib.panels.networkWindow.QtCore.QTimer") as mock_timer: w._on_operation_complete(result) mock_timer.singleShot.assert_called_once() # Loading should still be visible — retry is pending @@ -740,7 +740,7 @@ def test_wifi_on_with_saved_networks_starts_connect(self, win): ) ] nm.saved_networks = saved - with patch("BlocksScreen.lib.panels.networkWindow.QTimer") as mock_timer: + with patch("BlocksScreen.lib.panels.networkWindow.QtCore.QTimer") as mock_timer: w._handle_wifi_toggle(True) mock_timer.singleShot.assert_called() assert w._pending_operation == PendingOperation.WIFI_ON From ec206ca49220de4c5a977a8695f4f575d48fc88d Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Mon, 13 Apr 2026 18:24:29 +0100 Subject: [PATCH 2/2] revert: test_network_ui belongs with networkWindow changes --- tests/network/test_network_ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/network/test_network_ui.py b/tests/network/test_network_ui.py index a60a330a..466ec14a 100644 --- a/tests/network/test_network_ui.py +++ b/tests/network/test_network_ui.py @@ -693,7 +693,7 @@ def test_transient_mismatch_retries(self, win, qapp): message="not compatible with device", error_code="nm_error", ) - with patch("BlocksScreen.lib.panels.networkWindow.QtCore.QTimer") as mock_timer: + with patch("BlocksScreen.lib.panels.networkWindow.QTimer") as mock_timer: w._on_operation_complete(result) mock_timer.singleShot.assert_called_once() # Loading should still be visible — retry is pending @@ -740,7 +740,7 @@ def test_wifi_on_with_saved_networks_starts_connect(self, win): ) ] nm.saved_networks = saved - with patch("BlocksScreen.lib.panels.networkWindow.QtCore.QTimer") as mock_timer: + with patch("BlocksScreen.lib.panels.networkWindow.QTimer") as mock_timer: w._handle_wifi_toggle(True) mock_timer.singleShot.assert_called() assert w._pending_operation == PendingOperation.WIFI_ON