From e9edd74aa3229a11139d27693187baa7f8c72a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Luiz=20Bortot=20Monteiro=20do=20Ros=C3=A1rio?= <60971278+PedroBMR@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:47:24 -0300 Subject: [PATCH] Format codebase for Ruff compliance --- compile/tspl.py | 14 ++++-- compile/zpl.py | 10 +++- db/__init__.py | 4 +- db/migrations.py | 5 +- db/store.py | 9 ++-- editor/arrange_dialog.py | 5 +- editor/history.py | 14 ++++-- editor/layers.py | 10 ++-- editor/preset_dialog.py | 2 +- editor/roll_preview.py | 2 +- editor/scene.py | 11 +++- editor/variables.py | 9 ++-- editor/widget.py | 5 +- editor/window.py | 52 ++++++++++++------- fonts.py | 10 +++- imaging/loader.py | 12 +++-- imaging/raster.py | 35 ++++++++++--- model/template.py | 12 +++-- printing/__init__.py | 30 ++++++++--- printing_utils.py | 3 +- quick_print.py | 55 +++++++++++++++----- tests/db/test_init_and_migrate.py | 1 - tests/db/test_print_jobs_queue.py | 6 ++- tests/db/test_templates_assets_cache.py | 24 ++++++--- tests/test_db_store.py | 25 +++++---- tests/test_persistence.py | 1 + tests/test_preset_manager.py | 2 +- tests/test_printing_new.py | 2 +- tests/test_quick_print.py | 54 +++++++++++++++----- tests/test_raster_cache.py | 16 +++++- tests/test_ui_config_dialog.py | 18 +++++-- ui/__init__.py | 67 ++++++++++++++++++------- ui/print_history.py | 29 ++++++++--- ui/tour.py | 26 +++++++--- utils/__init__.py | 2 +- utils/fs.py | 8 ++- 36 files changed, 428 insertions(+), 162 deletions(-) diff --git a/compile/tspl.py b/compile/tspl.py index 9533ea7..2337628 100644 --- a/compile/tspl.py +++ b/compile/tspl.py @@ -53,7 +53,9 @@ def render(self, context: Mapping[str, Any]) -> bytes: result = handler(element, context) if not result: continue - if isinstance(result, Iterable) and not isinstance(result, (bytes, bytearray)): + if isinstance(result, Iterable) and not isinstance( + result, (bytes, bytearray) + ): parts.extend(self._ensure_bytes(item) for item in result) else: parts.append(self._ensure_bytes(result)) @@ -102,7 +104,9 @@ def _int_setting(value: Any) -> int | None: gap_offset = float(self.template.get("gap_offset_mm", 0.0)) mark_mm = self.template.get("mark_mm", self.template.get("black_mark_mm")) mark_offset = float( - self.template.get("mark_offset_mm", self.template.get("black_mark_offset_mm", 0.0)) + self.template.get( + "mark_offset_mm", self.template.get("black_mark_offset_mm", 0.0) + ) ) fine_offset = float(self.template.get("fine_offset_mm", 0.0)) calibrate = bool(self.template.get("calibrate_next_print")) @@ -248,7 +252,11 @@ def _resolve_raster_settings(self, element: Mapping[str, Any]) -> dict[str, Any] return settings def _bitmap_command( - self, element: Mapping[str, Any], image: Image.Image, *, digest: str | None = None + self, + element: Mapping[str, Any], + image: Image.Image, + *, + digest: str | None = None, ) -> bytes: settings = self._resolve_raster_settings(element) width = self.optional_dimension(element, "width", axis="x") diff --git a/compile/zpl.py b/compile/zpl.py index 1aaaec5..5fc3ab6 100644 --- a/compile/zpl.py +++ b/compile/zpl.py @@ -44,7 +44,9 @@ def render(self, context: Mapping[str, Any]) -> bytes: result = handler(element, context) if not result: continue - if isinstance(result, Iterable) and not isinstance(result, (bytes, bytearray)): + if isinstance(result, Iterable) and not isinstance( + result, (bytes, bytearray) + ): parts.extend(self._ensure_bytes(item) for item in result) else: parts.append(self._ensure_bytes(result)) @@ -215,7 +217,11 @@ def _resolve_raster_settings(self, element: Mapping[str, Any]) -> dict[str, Any] return settings def _graphic_field( - self, element: Mapping[str, Any], image: Image.Image, *, digest: str | None = None + self, + element: Mapping[str, Any], + image: Image.Image, + *, + digest: str | None = None, ) -> bytes: settings = self._resolve_raster_settings(element) width = self.optional_dimension(element, "width", axis="x") diff --git a/db/__init__.py b/db/__init__.py index a49629f..6241ffd 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -6,13 +6,13 @@ default_db_path, delete_vars_preset, find_raster_cache, - get_conn, get_asset_id_by_hash, + get_conn, get_template_id_by_name, init, - load_printer_profile, insert_or_get_raster_cache, list_vars_presets, + load_printer_profile, load_vars_preset, save_printer_profile, save_vars_preset, diff --git a/db/migrations.py b/db/migrations.py index 97c8b2e..d2515ff 100644 --- a/db/migrations.py +++ b/db/migrations.py @@ -2,10 +2,10 @@ from __future__ import annotations -from collections.abc import Callable import hashlib import json import sqlite3 +from collections.abc import Callable def add_column_if_missing( @@ -215,7 +215,8 @@ def table_columns(name: str) -> set[str]: conn.execute(create_print_jobs_sql) cursor = conn.execute( - "SELECT id, created_at, template_name, payload, status FROM print_jobs_legacy" + "SELECT id, created_at, template_name, payload, status " + "FROM print_jobs_legacy" ) for row in cursor.fetchall(): legacy_id, created_at, template_name, payload_value, status = row diff --git a/db/store.py b/db/store.py index 172773f..8168cc1 100644 --- a/db/store.py +++ b/db/store.py @@ -10,10 +10,11 @@ from pathlib import Path from typing import Mapping, Sequence -from . import migrations from utils import recurso_caminho from utils.fs import canonical_path as canonical_file_path +from . import migrations + __all__ = [ "backup", "configure_default_path", @@ -253,7 +254,8 @@ def load_printer_profile( with contextlib.closing(get_conn(database)) as conn: row = conn.execute( - "SELECT settings, calibrate_next_print FROM printer_profiles WHERE name = ?", + "SELECT settings, calibrate_next_print " + "FROM printer_profiles WHERE name = ?", (name,), ).fetchone() @@ -989,7 +991,8 @@ def get_job_payload( with contextlib.closing(get_conn(database)) as conn: cursor = conn.execute( - f"SELECT payload FROM print_jobs WHERE {where_clause} ORDER BY id DESC LIMIT 1", + "SELECT payload FROM print_jobs " + f"WHERE {where_clause} ORDER BY id DESC LIMIT 1", params, ) row = cursor.fetchone() diff --git a/editor/arrange_dialog.py b/editor/arrange_dialog.py index fa5e0bc..a1c7259 100644 --- a/editor/arrange_dialog.py +++ b/editor/arrange_dialog.py @@ -23,7 +23,10 @@ def __init__(self, parent=None) -> None: form_layout.addRow("Colunas (N):", self._columns) form_layout.addRow("Linhas (M):", self._rows) - button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self) + button_box = QDialogButtonBox( + QDialogButtonBox.Ok | QDialogButtonBox.Cancel, + self, + ) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) diff --git a/editor/history.py b/editor/history.py index 787e318..9b66971 100644 --- a/editor/history.py +++ b/editor/history.py @@ -5,13 +5,13 @@ from typing import Optional from qt_compat import ( + QIcon, QListWidget, QListWidgetItem, QSize, + Qt, QVBoxLayout, QWidget, - QIcon, - Qt, ) from .scene import LabelScene @@ -20,7 +20,11 @@ class UndoHistoryPanel(QWidget): """Display undo/redo history entries with canvas thumbnails.""" - def __init__(self, scene: Optional[LabelScene] = None, parent: QWidget | None = None) -> None: + def __init__( + self, + scene: Optional[LabelScene] = None, + parent: QWidget | None = None, + ) -> None: super().__init__(parent) self._scene: LabelScene | None = None self._updating_selection = False @@ -77,7 +81,9 @@ def _refresh_items(self) -> None: count = stack.count() snapshots = scene.history_snapshots() if len(snapshots) < count + 1: - snapshots.extend(scene.capture_thumbnail() for _ in range(count + 1 - len(snapshots))) + snapshots.extend( + scene.capture_thumbnail() for _ in range(count + 1 - len(snapshots)) + ) self._updating_selection = True try: diff --git a/editor/layers.py b/editor/layers.py index eea6b48..68b56ab 100644 --- a/editor/layers.py +++ b/editor/layers.py @@ -13,8 +13,8 @@ QInputDialog, QListWidget, QListWidgetItem, - QToolButton, Qt, + QToolButton, QVBoxLayout, QWidget, pyqtSignal, @@ -130,8 +130,12 @@ def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) self._scene: LabelScene | None = None self._updating_selection = False - self._row_items: Dict[GraphicsElementItem | TextGraphicsItem, QListWidgetItem] = {} - self._widgets: Dict[GraphicsElementItem | TextGraphicsItem, _LayerRow] = {} + self._row_items: Dict[ + GraphicsElementItem | TextGraphicsItem, QListWidgetItem + ] = {} + self._widgets: Dict[ + GraphicsElementItem | TextGraphicsItem, _LayerRow + ] = {} self._change_handlers: Dict[ GraphicsElementItem | TextGraphicsItem, object ] = {} diff --git a/editor/preset_dialog.py b/editor/preset_dialog.py index 35dd3b4..6bf5bb5 100644 --- a/editor/preset_dialog.py +++ b/editor/preset_dialog.py @@ -11,8 +11,8 @@ QListWidgetItem, QMessageBox, QPushButton, - QVBoxLayout, Qt, + QVBoxLayout, ) from .preset_manager import PresetManager diff --git a/editor/roll_preview.py b/editor/roll_preview.py index 43396c4..e43a2e7 100644 --- a/editor/roll_preview.py +++ b/editor/roll_preview.py @@ -14,8 +14,8 @@ QHBoxLayout, QLabel, QPainter, - QPointF, QPixmap, + QPointF, QSlider, QStackedWidget, Qt, diff --git a/editor/scene.py b/editor/scene.py index b00a29b..62def47 100644 --- a/editor/scene.py +++ b/editor/scene.py @@ -362,7 +362,9 @@ def can_duplicate_selected_items(self) -> bool: def duplicate_selected_items(self) -> bool: selected = [ - item for item in self.selected_items() if not getattr(item, "_locked", False) + item + for item in self.selected_items() + if not getattr(item, "_locked", False) ] if not selected: return False @@ -463,7 +465,12 @@ def set_alignment_tolerance(self, tolerance: float) -> None: """Update the snapping tolerance used for object alignment.""" tolerance = max(0.0, float(tolerance)) - if math.isclose(tolerance, self._alignment_tolerance, rel_tol=0.0, abs_tol=1e-6): + if math.isclose( + tolerance, + self._alignment_tolerance, + rel_tol=0.0, + abs_tol=1e-6, + ): return self._alignment_tolerance = tolerance self.clear_guides() diff --git a/editor/variables.py b/editor/variables.py index 14af519..3552826 100644 --- a/editor/variables.py +++ b/editor/variables.py @@ -8,27 +8,26 @@ from model.template import TemplateVariable from qt_compat import ( + QAbstractItemView, + QCheckBox, QColor, QComboBox, - QCheckBox, - QAbstractItemView, - QHeaderView, QHBoxLayout, + QHeaderView, QIcon, QLabel, QLineEdit, QPlainTextEdit, QPushButton, QStyle, + Qt, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, - Qt, pyqtSignal, ) - _TYPE_INFO: dict[str, tuple[str, str]] = { "text": ("Texto", "#eef2ff"), "multiline": ("Texto", "#eef2ff"), diff --git a/editor/widget.py b/editor/widget.py index c6f82c0..6aa648f 100644 --- a/editor/widget.py +++ b/editor/widget.py @@ -12,8 +12,8 @@ from qt_compat import ( QBrush, QCheckBox, - QComboBox, QColor, + QComboBox, QGraphicsPixmapItem, QGraphicsScene, QGraphicsView, @@ -21,8 +21,8 @@ QHBoxLayout, QLabel, QPainter, - QPointF, QPixmap, + QPointF, QSlider, QSplitter, QStackedWidget, @@ -43,7 +43,6 @@ from .scene import LabelScene, build_model_from_scene from .view import LabelView - MM_PER_INCH = 25.4 diff --git a/editor/window.py b/editor/window.py index 0aa7ac8..d80a432 100644 --- a/editor/window.py +++ b/editor/window.py @@ -3,9 +3,11 @@ from __future__ import annotations import re +from functools import partial from pathlib import Path from typing import Any +from db import store from editor.arrange_dialog import ArrangeGridDialog from editor.preset_dialog import PresetManagerDialog from editor.preset_manager import PresetManager @@ -19,8 +21,9 @@ ) from qt_compat import ( QAction, - QDockWidget, + QCursor, QDialog, + QDockWidget, QFileDialog, QInputDialog, QKeySequence, @@ -33,12 +36,11 @@ QTimer, QToolBar, QWidget, - QCursor, ) +from template_store import template_store from ui.tour import GuidedTourDialog, TourStep + from .variables import VariablePanel -from template_store import template_store -from db import store __all__ = ["LabelEditorWindow"] @@ -120,13 +122,13 @@ def __init__(self, parent: QWidget | None = None) -> None: self.addAction(self._tour_action) self.editor.splitter.splitterMoved.connect(self._on_splitter_moved) self.property_dock.visibilityChanged.connect( - lambda visible, dock=self.property_dock: self._save_dock_state(dock, visible) + partial(self._save_dock_state, self.property_dock) ) self.variable_dock.visibilityChanged.connect( - lambda visible, dock=self.variable_dock: self._save_dock_state(dock, visible) + partial(self._save_dock_state, self.variable_dock) ) self.history_dock.visibilityChanged.connect( - lambda visible, dock=self.history_dock: self._save_dock_state(dock, visible) + partial(self._save_dock_state, self.history_dock) ) QTimer.singleShot(0, self._restore_state) @@ -488,7 +490,9 @@ def _write_template(self, path: Path, *, overwrite: bool = False) -> None: def _show_guided_tour(self, force: bool = False) -> None: if not force and self._tour_shown: return - skip_onboarding = bool(self._settings.value("onboarding/skip", False, type=bool)) + skip_onboarding = bool( + self._settings.value("onboarding/skip", False, type=bool) + ) if not force and skip_onboarding: return @@ -536,8 +540,10 @@ def _build_tour_steps(self) -> list[TourStep]: TourStep( title="Canvas de edição", body=( - "Arraste os elementos diretamente sobre o canvas para reposicioná-los. " - "Use Ctrl + rolagem do mouse para ajustar o zoom e Espaço para arrastar a área visível." + "Arraste os elementos diretamente sobre o canvas " + "para reposicioná-los. " + "Use Ctrl + rolagem do mouse para ajustar o zoom " + "e Espaço para arrastar a área visível." ), anchor=self.editor.view, highlight_padding=28, @@ -548,8 +554,10 @@ def _build_tour_steps(self) -> list[TourStep]: TourStep( title="Painéis de propriedades", body=( - "Selecione um item para ajustar fonte, cores e alinhamento neste painel lateral. " - "A aba Variáveis permite informar os valores dinâmicos usados no modelo." + "Selecione um item para ajustar fonte, cores e alinhamento " + "neste painel lateral. " + "A aba Variáveis permite informar os valores dinâmicos usados " + "no modelo." ), anchor=self.property_dock, highlight_padding=20, @@ -560,15 +568,21 @@ def _build_tour_steps(self) -> list[TourStep]: TourStep( title="Pré-visualização", body=( - "A pré-visualização renderiza o resultado final com os dados atuais. " - "Ajuste o zoom pelos controles inferiores para conferir os detalhes antes de exportar." + "A pré-visualização renderiza o resultado final " + "com os dados atuais. " + "Ajuste o zoom pelos controles inferiores " + "para conferir os detalhes antes de exportar." ), anchor=self.editor.preview_widget, highlight_padding=28, ) ) - return [step for step in steps if step.anchor is None or step.anchor.isVisible()] + return [ + step + for step in steps + if step.anchor is None or step.anchor.isVisible() + ] # ------------------------------------------------------------------ def _add_element(self, kind: str) -> None: @@ -813,7 +827,10 @@ def _toggle_fullscreen(self) -> None: else self._normal_splitter_sizes ) if sizes: - QTimer.singleShot(0, lambda s=list(sizes): self.editor.splitter.setSizes(s)) + QTimer.singleShot( + 0, + partial(self.editor.splitter.setSizes, list(sizes)), + ) else: current = self.editor.splitter.sizes() if self.compact_action.isChecked(): @@ -849,7 +866,8 @@ def _save_dock_state(self, dock: QDockWidget, visible: bool) -> None: def _write_sizes(self, key: str, sizes: list[int]) -> None: if not sizes: return - self._settings.setValue(key, ",".join(str(int(max(0, value))) for value in sizes)) + formatted = ",".join(str(int(max(0, value))) for value in sizes) + self._settings.setValue(key, formatted) def _read_sizes(self, key: str) -> list[int] | None: value = self._settings.value(key) diff --git a/fonts.py b/fonts.py index 7a50f78..576b037 100644 --- a/fonts.py +++ b/fonts.py @@ -92,7 +92,10 @@ def ensure_qt_font(self, font_id: str) -> list[str]: payload = base64.b64decode(entry["data"]) font_index = QFontDatabase.addApplicationFontFromData(QByteArray(payload)) if font_index >= 0: - families = [str(name) for name in QFontDatabase.applicationFontFamilies(font_index)] + families = [ + str(name) + for name in QFontDatabase.applicationFontFamilies(font_index) + ] else: # pragma: no cover - depends on Qt availability families = [] if families: @@ -145,7 +148,10 @@ def metadata_for(self, font_id: str) -> dict[str, Any] | None: return result # ------------------------------------------------------------------ - def export_fonts(self, font_ids: Mapping[str, Any] | set[str] | list[str]) -> dict[str, Any]: + def export_fonts( + self, + font_ids: Mapping[str, Any] | set[str] | list[str], + ) -> dict[str, Any]: """Return metadata for the provided ``font_ids``.""" result: dict[str, Any] = {} diff --git a/imaging/loader.py b/imaging/loader.py index 7bd15fb..82e0f8a 100644 --- a/imaging/loader.py +++ b/imaging/loader.py @@ -5,7 +5,7 @@ import io import uuid from pathlib import Path -from typing import TYPE_CHECKING, Any, BinaryIO +from typing import Any, BinaryIO from PIL import Image @@ -13,9 +13,6 @@ __all__ = ["load_image"] -if TYPE_CHECKING: # pragma: no cover - type checking only - from db import store as db_store_module - def _guess_kind(name: str | None, image: Image.Image) -> str: if name: @@ -132,7 +129,12 @@ def load_image(source: Any) -> Image.Image: pass buffer = io.BytesIO(payload) image = Image.open(buffer) - _record_asset(image, name=stream_name if isinstance(stream_name, str) else None, path=None, data=payload) + _record_asset( + image, + name=stream_name if isinstance(stream_name, str) else None, + path=None, + data=payload, + ) return image raise TypeError(f"Unsupported image source: {type(source)!r}") diff --git a/imaging/raster.py b/imaging/raster.py index 6cab5cf..77d6ac7 100644 --- a/imaging/raster.py +++ b/imaging/raster.py @@ -4,8 +4,8 @@ import hashlib import io +from collections.abc import Iterable, MutableMapping from pathlib import Path -from typing import Iterable, Mapping, MutableMapping from PIL import Image, ImageFilter, ImageOps @@ -121,7 +121,10 @@ def _resize_image( if fit_mode == "stretch": width = width or src_w height = height or src_h - return image.resize((max(1, width), max(1, height)), Image.Resampling.LANCZOS) + return image.resize( + (max(1, width), max(1, height)), + Image.Resampling.LANCZOS, + ) scale_x = width / src_w if width else None scale_y = height / src_h if height else None @@ -137,7 +140,10 @@ def _resize_image( new_width = max(1, int(round(src_w * scale))) new_height = max(1, int(round(src_h * scale))) - resized = image.resize((new_width, new_height), Image.Resampling.LANCZOS) + resized = image.resize( + (new_width, new_height), + Image.Resampling.LANCZOS, + ) if fit_mode == "fill" and width and height: left = max(0, (resized.width - width) // 2) @@ -149,7 +155,12 @@ def _resize_image( return resized -def _dither_to_mono(image: Image.Image, *, method: str, threshold: int = 128) -> Image.Image: +def _dither_to_mono( + image: Image.Image, + *, + method: str, + threshold: int = 128, +) -> Image.Image: if method == "threshold": cut = max(0, min(255, int(threshold))) table = [0] * 256 @@ -323,9 +334,19 @@ def prepare_bitmap_for_tspl( cache_dir = None if cache_dir is not None: - descriptor = ( - f"{file_hash or asset_id}:{padded_width}:{mono.height}:{rounded_x}:{rounded_y}:" - f"{method}:{gamma_value}:{int(bool(sharpen))}:{int(bool(invert))}:{threshold_value}" + descriptor = ":".join( + [ + str(file_hash or asset_id), + str(padded_width), + str(mono.height), + str(rounded_x), + str(rounded_y), + method, + str(gamma_value), + str(int(bool(sharpen))), + str(int(bool(invert))), + str(threshold_value), + ] ) digest = hashlib.sha1(descriptor.encode("utf-8")).hexdigest() prefix = file_hash or f"asset{asset_id}" diff --git a/model/template.py b/model/template.py index b8c6683..33522ed 100644 --- a/model/template.py +++ b/model/template.py @@ -7,9 +7,9 @@ import re import unicodedata from dataclasses import dataclass, field +from datetime import UTC, datetime from pathlib import Path from string import Formatter -from datetime import UTC, datetime from typing import Any, ClassVar, Iterable, Mapping, Sequence from db import store as db_store @@ -399,7 +399,11 @@ def _record_index( width_mm=width_mm, height_mm=height_mm, dpi=dpi, - kind=str(self.page.get("backend") or self.page.get("type") or "unknown"), + kind=str( + self.page.get("backend") + or self.page.get("type") + or "unknown" + ), tags=tags, updated_at=timestamp, ) @@ -408,7 +412,9 @@ def _record_index( "Falha ao atualizar índice de templates: %s", self.name ) - def _index_metadata(self) -> tuple[float | None, float | None, int | None, list[str]]: + def _index_metadata( + self, + ) -> tuple[float | None, float | None, int | None, list[str]]: def _to_float(value: Any) -> float | None: if isinstance(value, (int, float)): return float(value) diff --git a/printing/__init__.py b/printing/__init__.py index 379e06c..3fbb37c 100644 --- a/printing/__init__.py +++ b/printing/__init__.py @@ -11,7 +11,6 @@ import logging import re import socket -import time import unicodedata from dataclasses import dataclass, field from pathlib import Path @@ -19,8 +18,8 @@ from PIL import Image -from db import store as db_store from compile import Calibration, CompilationError, SafeDict, get_backend +from db import store as db_store from fonts import font_store from model.template import TemplateDocument, TemplateValidationError from persistence import carregar_config, salvar_config @@ -28,7 +27,7 @@ from utils import recurso_caminho from utils.fs import canonical_path as canonical_file_path -from .queue import PrinterTarget, enqueue_job, shutdown as shutdown_print_queue, start as start_print_queue, wait_for_all as wait_for_all_jobs +from .queue import PrinterTarget, enqueue_job # ---------------------------------------------------------------------------- @@ -382,7 +381,11 @@ def _coerce_bool(value: Any) -> bool | None: if gap_mm is None: gap_mm = 0.0 - gap_offset_candidate = _profile_value("gap_offset_mm", "media_gap_offset_mm", "media_offset_mm") + gap_offset_candidate = _profile_value( + "gap_offset_mm", + "media_gap_offset_mm", + "media_offset_mm", + ) if gap_offset_candidate is None: gap_offset_candidate = _config_value( "media_gap_offset_mm", "media_offset_mm" @@ -463,8 +466,14 @@ def apply_media_settings(template: Template, settings: Mapping[str, Any]) -> Tem document = template.document.copy() page = document.page - media_type = str(settings.get("media_type", page.get("media_type", "gap"))).lower() - media_type = "black_mark" if media_type in {"black_mark", "blackmark", "mark"} else "gap" + media_type = str( + settings.get("media_type", page.get("media_type", "gap")) + ).lower() + media_type = ( + "black_mark" + if media_type in {"black_mark", "blackmark", "mark"} + else "gap" + ) page["media_type"] = media_type def _coerce(key: str, default: float = 0.0) -> float: @@ -476,7 +485,10 @@ def _coerce(key: str, default: float = 0.0) -> float: gap_mm = _coerce("gap_mm", float(page.get("gap_mm", 0.0))) gap_offset = _coerce("gap_offset_mm", float(page.get("gap_offset_mm", 0.0))) - mark_mm = _coerce("mark_mm", float(page.get("mark_mm", page.get("black_mark_mm", gap_mm)))) + mark_mm = _coerce( + "mark_mm", + float(page.get("mark_mm", page.get("black_mark_mm", gap_mm))), + ) mark_offset = _coerce( "mark_offset_mm", float(page.get("mark_offset_mm", page.get("black_mark_offset_mm", gap_offset))), @@ -887,7 +899,9 @@ def export_labels( if batch and total > 1 and formato_normalizado in {"png", "svg"}: for indice, imagem in enumerate(images, start=1): sufixo = f"-{indice:03d}" - target = destino_path.with_name(destino_path.stem + sufixo + destino_path.suffix) + target = destino_path.with_name( + destino_path.stem + sufixo + destino_path.suffix + ) if formato_normalizado == "png": saved.append(_write_png(imagem, target)) else: diff --git a/printing_utils.py b/printing_utils.py index 9485a3d..119e432 100644 --- a/printing_utils.py +++ b/printing_utils.py @@ -98,7 +98,8 @@ def emit_tspl_media_setup(params: TSPLMediaParams) -> list[str]: commands.append(f"DENSITY {int(params.density)}") commands.append(f"DIRECTION {int(params.direction)}") - commands.append(f"REFERENCE {max(params.reference_x, 0)},{max(params.reference_y, 0)}") + reference = f"{max(params.reference_x, 0)},{max(params.reference_y, 0)}" + commands.append(f"REFERENCE {reference}") if media_type == "black_mark": mark = params.mark_mm if params.mark_mm is not None else params.gap_mm or 0.0 diff --git a/quick_print.py b/quick_print.py index 50afa97..c7ddef6 100644 --- a/quick_print.py +++ b/quick_print.py @@ -9,6 +9,7 @@ from db import store as db_store from log import logger +from persistence import carregar_config from printing import ( WindowsSpoolerTransport, compile_model_silent, @@ -36,8 +37,6 @@ from render import render_template from template_store import template_store -from persistence import carregar_config - class QuickPrintDialog(QDialog): """Allow users to print labels by selecting only the template.""" @@ -69,15 +68,25 @@ def __init__(self, parent=None) -> None: self.model_combo = QComboBox() self.model_combo.setEditable(False) self.model_combo.setStyleSheet( - "QComboBox{background:#252525;color:#f0f0f0;padding:6px;border:1px solid #333;border-radius:6px;}" - "QComboBox QAbstractItemView{background:#1e1e1e;color:#f0f0f0;}" + "".join( + [ + "QComboBox{background:#252525;color:#f0f0f0;padding:6px;", + "border:1px solid #333;border-radius:6px;}", + "QComboBox QAbstractItemView{background:#1e1e1e;color:#f0f0f0;}", + ] + ) ) self.copies_spin = QSpinBox() self.copies_spin.setMinimum(1) self.copies_spin.setValue(1) self.copies_spin.setStyleSheet( - "QSpinBox{background:#252525;color:#f0f0f0;padding:6px;border:1px solid #333;border-radius:6px;}" + "".join( + [ + "QSpinBox{background:#252525;color:#f0f0f0;padding:6px;", + "border:1px solid #333;border-radius:6px;}", + ] + ) ) self.printer_combo = QComboBox() @@ -86,8 +95,13 @@ def __init__(self, parent=None) -> None: if self.printer_combo.lineEdit(): self.printer_combo.lineEdit().setPlaceholderText("Impressora padrão") self.printer_combo.setStyleSheet( - "QComboBox{background:#252525;color:#f0f0f0;padding:6px;border:1px solid #333;border-radius:6px;}" - "QComboBox QAbstractItemView{background:#1e1e1e;color:#f0f0f0;}" + "".join( + [ + "QComboBox{background:#252525;color:#f0f0f0;padding:6px;", + "border:1px solid #333;border-radius:6px;}", + "QComboBox QAbstractItemView{background:#1e1e1e;color:#f0f0f0;}", + ] + ) ) self.preview_check = QCheckBox("Mostrar pré-visualização antes de enviar") @@ -119,22 +133,37 @@ def _row(label_text: str, widget) -> None: self.print_btn.setAutoDefault(True) self.print_btn.clicked.connect(self._on_print) self.print_btn.setStyleSheet( - "QPushButton{background:#2f855a;color:white;padding:8px 18px;border:none;border-radius:6px;}" - "QPushButton:pressed{background:#276749;}" + "".join( + [ + "QPushButton{background:#2f855a;color:white;padding:8px 18px;", + "border:none;border-radius:6px;}", + "QPushButton:pressed{background:#276749;}", + ] + ) ) self.reprint_btn = QPushButton("Reimprimir última") self.reprint_btn.clicked.connect(self._on_reprint_last) self.reprint_btn.setStyleSheet( - "QPushButton{background:#2d3748;color:#f0f0f0;padding:8px 18px;border:none;border-radius:6px;}" - "QPushButton:pressed{background:#1a202c;}" + "".join( + [ + "QPushButton{background:#2d3748;color:#f0f0f0;padding:8px 18px;", + "border:none;border-radius:6px;}", + "QPushButton:pressed{background:#1a202c;}", + ] + ) ) self.close_btn = QPushButton("Fechar") self.close_btn.clicked.connect(self.reject) self.close_btn.setStyleSheet( - "QPushButton{background:#4a5568;color:#f0f0f0;padding:8px 18px;border:none;border-radius:6px;}" - "QPushButton:pressed{background:#2d3748;}" + "".join( + [ + "QPushButton{background:#4a5568;color:#f0f0f0;padding:8px 18px;", + "border:none;border-radius:6px;}", + "QPushButton:pressed{background:#2d3748;}", + ] + ) ) button_row.addWidget(self.print_btn) diff --git a/tests/db/test_init_and_migrate.py b/tests/db/test_init_and_migrate.py index bb0ecc3..a2a3693 100644 --- a/tests/db/test_init_and_migrate.py +++ b/tests/db/test_init_and_migrate.py @@ -15,7 +15,6 @@ from db import migrations, store - EXPECTED_TABLE_COLUMNS: dict[str, set[str]] = { "meta": {"key", "value"}, "templates": { diff --git a/tests/db/test_print_jobs_queue.py b/tests/db/test_print_jobs_queue.py index 4085295..323f95f 100644 --- a/tests/db/test_print_jobs_queue.py +++ b/tests/db/test_print_jobs_queue.py @@ -118,7 +118,11 @@ def test_print_job_retries_and_failure(monkeypatch: pytest.MonkeyPatch): real_update = db_store.update_print_job_status def tracking_update( - *, status: str, job_id: int | None = None, payload_hash: str | None = None, **kwargs + *, + status: str, + job_id: int | None = None, + payload_hash: str | None = None, + **kwargs, ): if job_id is not None: status_transitions.setdefault(job_id, []).append(status) diff --git a/tests/db/test_templates_assets_cache.py b/tests/db/test_templates_assets_cache.py index f3371d1..a5f47a9 100644 --- a/tests/db/test_templates_assets_cache.py +++ b/tests/db/test_templates_assets_cache.py @@ -46,8 +46,10 @@ def test_upsert_template_insert_and_update(db_path: Path, tmp_path: Path) -> Non with store.get_conn(db_path) as conn: row = conn.execute( - "SELECT name, kind, path, file_hash, width_mm, height_mm, dpi, tags, updated_at, data" - " FROM templates WHERE id = ?", + ( + "SELECT name, kind, path, file_hash, width_mm, height_mm, dpi, " + "tags, updated_at, data FROM templates WHERE id = ?" + ), (template_id,), ).fetchone() @@ -82,8 +84,10 @@ def test_upsert_template_insert_and_update(db_path: Path, tmp_path: Path) -> Non with store.get_conn(db_path) as conn: updated_row = conn.execute( - "SELECT kind, file_hash, width_mm, height_mm, dpi, tags, updated_at, data" - " FROM templates WHERE id = ?", + ( + "SELECT kind, file_hash, width_mm, height_mm, dpi, tags, " + "updated_at, data FROM templates WHERE id = ?" + ), (template_id,), ).fetchone() @@ -122,8 +126,10 @@ def test_upsert_asset_round_trip(db_path: Path, tmp_path: Path) -> None: with store.get_conn(db_path) as conn: row = conn.execute( - "SELECT name, kind, path, file_hash, width_px, height_px, dpi_x, dpi_y, tags, data, updated_at" - " FROM assets WHERE id = ?", + ( + "SELECT name, kind, path, file_hash, width_px, height_px, dpi_x, " + "dpi_y, tags, data, updated_at FROM assets WHERE id = ?" + ), (asset_id,), ).fetchone() @@ -160,8 +166,10 @@ def test_upsert_asset_round_trip(db_path: Path, tmp_path: Path) -> None: with store.get_conn(db_path) as conn: updated_row = conn.execute( - "SELECT file_hash, width_px, height_px, dpi_x, dpi_y, tags, data, updated_at" - " FROM assets WHERE id = ?", + ( + "SELECT file_hash, width_px, height_px, dpi_x, dpi_y, tags, data, " + "updated_at FROM assets WHERE id = ?" + ), (asset_id,), ).fetchone() diff --git a/tests/test_db_store.py b/tests/test_db_store.py index 50abba3..c210b57 100644 --- a/tests/test_db_store.py +++ b/tests/test_db_store.py @@ -6,7 +6,6 @@ import types from pathlib import Path - if "PIL" not in sys.modules: pil_module = types.ModuleType("PIL") image_module = types.ModuleType("PIL.Image") @@ -273,8 +272,10 @@ def test_upsert_template_persists_metadata(tmp_path): with store.get_conn(db_path) as conn: row = conn.execute( - "SELECT name, kind, path, file_hash, width_mm, height_mm, dpi, tags, updated_at" - " FROM templates WHERE name = ?", + ( + "SELECT name, kind, path, file_hash, width_mm, height_mm, dpi, tags, " + "updated_at FROM templates WHERE name = ?" + ), ("example",), ).fetchone() @@ -304,8 +305,10 @@ def test_upsert_template_persists_metadata(tmp_path): with store.get_conn(db_path) as conn: row = conn.execute( - "SELECT kind, file_hash, width_mm, height_mm, dpi, tags, updated_at" - " FROM templates WHERE name = ?", + ( + "SELECT kind, file_hash, width_mm, height_mm, dpi, tags, updated_at " + "FROM templates WHERE name = ?" + ), ("example",), ).fetchone() @@ -436,8 +439,10 @@ def test_upsert_asset_persists_metadata(tmp_path): with store.get_conn(db_path) as conn: row = conn.execute( - "SELECT name, kind, path, file_hash, width_px, height_px, dpi_x, dpi_y, tags, data, updated_at" - " FROM assets WHERE name = ?", + ( + "SELECT name, kind, path, file_hash, width_px, height_px, dpi_x, " + "dpi_y, tags, data, updated_at FROM assets WHERE name = ?" + ), ("logo",), ).fetchone() @@ -470,8 +475,10 @@ def test_upsert_asset_persists_metadata(tmp_path): with store.get_conn(db_path) as conn: row = conn.execute( - "SELECT file_hash, width_px, height_px, dpi_x, dpi_y, tags, data, updated_at" - " FROM assets WHERE name = ?", + ( + "SELECT file_hash, width_px, height_px, dpi_x, dpi_y, tags, data, " + "updated_at FROM assets WHERE name = ?" + ), ("logo",), ).fetchone() diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 29fab3d..1c911c2 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -71,6 +71,7 @@ def test_carregar_contagem_reseta_mes(monkeypatch, tmp_path): def test_gerar_relatorio_mensal(monkeypatch, tmp_path): import csv + from db import store as db_store persistence = _patch_paths(monkeypatch, tmp_path) diff --git a/tests/test_preset_manager.py b/tests/test_preset_manager.py index 7de89a7..71799d4 100644 --- a/tests/test_preset_manager.py +++ b/tests/test_preset_manager.py @@ -1,8 +1,8 @@ from __future__ import annotations -from pathlib import Path import sys import types +from pathlib import Path if "PIL" not in sys.modules: # pragma: no cover - test isolation helper pil_module = types.ModuleType("PIL") diff --git a/tests/test_printing_new.py b/tests/test_printing_new.py index 4e2014e..4e47e5c 100644 --- a/tests/test_printing_new.py +++ b/tests/test_printing_new.py @@ -6,10 +6,10 @@ from db import store as db_store from model.template import TemplateDocument, TemplateVariable from printing import ( - TransportError, Printer, PrinterConfig, Template, + TransportError, apply_calibration_adjustment, apply_media_settings, build_calibration_template, diff --git a/tests/test_quick_print.py b/tests/test_quick_print.py index 2885fc7..5218e1a 100644 --- a/tests/test_quick_print.py +++ b/tests/test_quick_print.py @@ -9,9 +9,8 @@ pytest.skip("Qt bindings are not installed", allow_module_level=True) import quick_print -from quick_print import QuickPrintDialog - from db import store as db_store +from quick_print import QuickPrintDialog def _ensure_app() -> QApplication: @@ -40,12 +39,22 @@ def fake_templates() -> list[str]: monkeypatch.setattr(quick_print, "listar_templates", fake_templates) monkeypatch.setattr(quick_print, "listar_impressoras", lambda: []) - monkeypatch.setattr(quick_print, "compile_model_silent", lambda *a, **k: b"") - monkeypatch.setattr( - quick_print, - "prepare_quick_job", - lambda *a, **k: (SimpleNamespace(document=SimpleNamespace(page={}, elements=[]), calibration=None), {}), - ) + + def _empty_compile(*_args, **_kwargs): + return b"" + + monkeypatch.setattr(quick_print, "compile_model_silent", _empty_compile) + + def _fake_prepare_quick_job(*_args, **_kwargs): + return ( + SimpleNamespace( + document=SimpleNamespace(page={}, elements=[]), + calibration=None, + ), + {}, + ) + + monkeypatch.setattr(quick_print, "prepare_quick_job", _fake_prepare_quick_job) dialog = QuickPrintDialog() try: @@ -112,11 +121,22 @@ def test_quick_print_print_sends_payload(monkeypatch): media_settings = {"media_type": "gap", "calibrate_next_print": False} monkeypatch.setattr(quick_print, "carregar_config", lambda: dict(config)) - monkeypatch.setattr(quick_print.db_store, "load_printer_profile", lambda name: profile) + def fake_load_profile(_name): + return profile + + monkeypatch.setattr(quick_print.db_store, "load_printer_profile", fake_load_profile) captured_prepare: dict[str, Any] = {} - def fake_prepare(model, overrides, *, silent, config, printer_name, printer_profile): + def fake_prepare( + model, + overrides, + *, + silent, + config, + printer_name, + printer_profile, + ): captured_prepare.update( { "model": model, @@ -128,7 +148,10 @@ def fake_prepare(model, overrides, *, silent, config, printer_name, printer_prof } ) return ( - SimpleNamespace(document=SimpleNamespace(page={}, elements=[]), calibration=None), + SimpleNamespace( + document=SimpleNamespace(page={}, elements=[]), + calibration=None, + ), overrides, ) @@ -149,7 +172,14 @@ def fake_compile(model, overrides, *, silent, printer_name, printer_profile): return b"PRINT 1\r\n" monkeypatch.setattr(quick_print, "compile_model_silent", fake_compile) - monkeypatch.setattr(quick_print, "resolve_media_settings", lambda *a, **k: dict(media_settings)) + def fake_resolve_media_settings(*_args, **_kwargs): + return dict(media_settings) + + monkeypatch.setattr( + quick_print, + "resolve_media_settings", + fake_resolve_media_settings, + ) jobs: list[tuple[str | None, bytes, quick_print.PrinterTarget, int, dict]] = [] diff --git a/tests/test_raster_cache.py b/tests/test_raster_cache.py index 225f6ec..aa66a9d 100644 --- a/tests/test_raster_cache.py +++ b/tests/test_raster_cache.py @@ -24,8 +24,20 @@ def test_prepare_bitmap_uses_disk_cache_hit(tmp_path, monkeypatch): from db import store as db_store monkeypatch.setattr(db_store, "get_asset_id_by_hash", lambda *_args, **_kwargs: 42) - monkeypatch.setattr(db_store, "find_raster_cache", lambda *args, **kwargs: cache_path) - monkeypatch.setattr(db_store, "insert_or_get_raster_cache", lambda **_kwargs: cache_path) + + def fake_find_raster_cache(*_args, **_kwargs): + return cache_path + + monkeypatch.setattr(db_store, "find_raster_cache", fake_find_raster_cache) + + def fake_insert_raster_cache(**_kwargs): + return cache_path + + monkeypatch.setattr( + db_store, + "insert_or_get_raster_cache", + fake_insert_raster_cache, + ) def fail(*_args, **_kwargs): # pragma: no cover - sanity guard raise AssertionError("Rasterisation should not occur on cache hit") diff --git a/tests/test_ui_config_dialog.py b/tests/test_ui_config_dialog.py index e42c138..3c203e9 100644 --- a/tests/test_ui_config_dialog.py +++ b/tests/test_ui_config_dialog.py @@ -1,8 +1,7 @@ from __future__ import annotations -from typing import Any - import logging +from typing import Any import pytest @@ -181,14 +180,20 @@ def test_config_dialog_backup_button_runs_worker(monkeypatch, tmp_path, caplog): backup_target = tmp_path / "backup.sqlite" - monkeypatch.setattr(ui.QFileDialog, "getSaveFileName", lambda *a, **k: (str(backup_target), "")) + def fake_get_save_file_name(*_args, **_kwargs): + return str(backup_target), "" + + monkeypatch.setattr(ui.QFileDialog, "getSaveFileName", fake_get_save_file_name) monkeypatch.setattr(ui, "listar_impressoras", lambda: []) monkeypatch.setattr(ui, "listar_templates", lambda: []) monkeypatch.setattr(ui, "descobrir_impressora_padrao", lambda: "") dialog = ConfigDialog(None, _base_settings()) statuses: list[tuple[str, str]] = [] - dialog._status_callback = lambda msg, color="white": statuses.append((msg, color)) + def _capture_status(message, color="white"): + statuses.append((message, color)) + + dialog._status_callback = _capture_status called: dict[str, Any] = {} @@ -211,7 +216,10 @@ def _fake_start(action, function, args, kwargs, context): assert called["args"][0] == dialog._db_path assert called["context"]["out_path"] == backup_target assert statuses[-1][0].startswith("💾 Backup concluído") - assert any("Backup do banco de dados" in record.message for record in caplog.records) + assert any( + "Backup do banco de dados" in record.message + for record in caplog.records + ) finally: dialog.close() diff --git a/ui/__init__.py b/ui/__init__.py index e21fa09..9a6c424 100644 --- a/ui/__init__.py +++ b/ui/__init__.py @@ -5,10 +5,9 @@ import json import os -import sys from datetime import datetime -from pathlib import Path from functools import partial +from pathlib import Path from typing import Any, Callable, Mapping, TypedDict from _version import __version__ @@ -16,7 +15,6 @@ from editor.window import LabelEditorWindow from log import LOG_FILE, logger, register_status_callback from model.template import TemplateDocument -from quick_print import QuickPrintDialog from persistence import ( atualizar_recentes, carregar_config, @@ -29,13 +27,13 @@ salvar_contagem, ) from printing import ( + Printer, + PrinterConfig, Template, aplicar_template, apply_media_settings, descobrir_impressora_padrao, export_labels, - Printer, - PrinterConfig, imprimir_pagina_teste, listar_impressoras, listar_templates, @@ -53,15 +51,16 @@ QDialog, QDialogButtonBox, QDoubleSpinBox, + QFileDialog, QFont, QFormLayout, - QFileDialog, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QIcon, QInputDialog, + QKeySequence, QLabel, QLineEdit, QMenu, @@ -77,7 +76,7 @@ QSpacerItem, QSpinBox, Qt, - QKeySequence, + QThread, QTime, QTimeEdit, QTimer, @@ -85,13 +84,13 @@ QUrl, QVBoxLayout, QWidget, - QThread, pyqtSignal, pyqtSlot, ) +from quick_print import QuickPrintDialog from template_store import template_store -from utils import backup_automatico, normalize_text, recurso_caminho from ui.print_history import PrintHistoryWidget +from utils import backup_automatico, normalize_text, recurso_caminho CATEGORIAS_PADRAO = [ "LIMPEZA, COPA, COZINHA", @@ -652,17 +651,29 @@ def _coerce_int(value: Any, default: int) -> int: except (TypeError, ValueError): return default - self._gap_size = _coerce_float(data.get("media_gap_mm"), self._default_gap_size) + self._gap_size = _coerce_float( + data.get("media_gap_mm"), + self._default_gap_size, + ) self._gap_offset = _coerce_float( data.get("media_gap_offset_mm"), self._default_gap_offset ) - self._mark_size = _coerce_float(data.get("media_mark_mm"), self._default_mark_size) + self._mark_size = _coerce_float( + data.get("media_mark_mm"), + self._default_mark_size, + ) self._mark_offset = _coerce_float( data.get("media_mark_offset_mm"), self._default_mark_offset ) - media_type = str(data.get("media_type", self._default_media_type) or "gap").lower() - media_type = "black_mark" if media_type in {"black_mark", "blackmark", "mark"} else "gap" + media_type = str( + data.get("media_type", self._default_media_type) or "gap" + ).lower() + media_type = ( + "black_mark" + if media_type in {"black_mark", "blackmark", "mark"} + else "gap" + ) current_type = str(self.media_type_combo.currentData()) if media_type != current_type: block = self.media_type_combo.blockSignals(True) @@ -692,7 +703,9 @@ def _coerce_int(value: Any, default: int) -> int: ) self.media_fine_offset_spin.setValue(fine_offset_value) - calibrate = bool(data.get("media_calibrate_next", self._default_media_calibrate)) + calibrate = bool( + data.get("media_calibrate_next", self._default_media_calibrate) + ) self.media_calibrate_check.setChecked(calibrate) self._update_media_fields() @@ -1106,7 +1119,11 @@ def _refresh_print_history(self) -> None: if hasattr(self, "print_history") and self.print_history is not None: self.print_history.refresh() - def _register_pending_job(self, job_id: int | None, context: dict[str, Any]) -> None: + def _register_pending_job( + self, + job_id: int | None, + context: dict[str, Any], + ) -> None: if job_id is None: return self._pending_jobs[job_id] = context @@ -1333,7 +1350,13 @@ def _resend_job( template_name = str(record.get("template_name") or "") or None try: - novo_job, status = enqueue_job(template_name, payload, target, copies=1, params=params) + novo_job, status = enqueue_job( + template_name, + payload, + target, + copies=1, + params=params, + ) except Exception as exc: logger.exception("Erro ao reenfileirar trabalho salvo") QMessageBox.critical( @@ -1579,7 +1602,11 @@ def _media_settings(self, *, consume: bool) -> dict[str, Any]: profile_calibrate = False if profile: raw_flag = None - for key in ("media_calibrate_next", "calibrate_next", "calibrate_next_print"): + for key in ( + "media_calibrate_next", + "calibrate_next", + "calibrate_next_print", + ): if key in profile: raw_flag = profile[key] break @@ -1897,7 +1924,11 @@ def _exportar_etiqueta(self, formato: str, *, batch: bool) -> None: "svg": ".svg", }.get(formato_norm, ".dat") - sugestao = f"etiqueta-{saida or datetime.now().strftime('%Y%m%d-%H%M%S')}{sufixo}" + sugestao = ( + f"etiqueta-" + f"{saida or datetime.now().strftime('%Y%m%d-%H%M%S')}" + f"{sufixo}" + ) caminho, _ = QFileDialog.getSaveFileName( self, "Exportar etiquetas", diff --git a/ui/print_history.py b/ui/print_history.py index 20acc3a..ea95202 100644 --- a/ui/print_history.py +++ b/ui/print_history.py @@ -8,15 +8,15 @@ from db import store as db_store from qt_compat import ( QAbstractItemView, - QHeaderView, QHBoxLayout, + QHeaderView, QLabel, QPushButton, + Qt, QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget, - Qt, pyqtSignal, ) @@ -129,7 +129,10 @@ def _populate(self, rows: Iterable[dict[str, object]]) -> None: job_id = row.get("id") status_text = str(row.get("status") or "").upper() status_item = QTableWidgetItem(status_text) - status_item.setData(Qt.UserRole, int(job_id) if isinstance(job_id, int) else job_id) + status_item.setData( + Qt.UserRole, + int(job_id) if isinstance(job_id, int) else job_id, + ) self._table.setItem(row_idx, 0, status_item) printer = str(row.get("printer_name") or "") @@ -146,10 +149,22 @@ def _populate(self, rows: Iterable[dict[str, object]]) -> None: self._table.setItem(row_idx, 3, QTableWidgetItem(transport)) created = row.get("pending_at") or row.get("created_at") - self._table.setItem(row_idx, 4, QTableWidgetItem(_format_timestamp(created))) - - updated = row.get("finished_at") or row.get("failed_at") or row.get("running_at") - self._table.setItem(row_idx, 5, QTableWidgetItem(_format_timestamp(updated))) + self._table.setItem( + row_idx, + 4, + QTableWidgetItem(_format_timestamp(created)), + ) + + updated = ( + row.get("finished_at") + or row.get("failed_at") + or row.get("running_at") + ) + self._table.setItem( + row_idx, + 5, + QTableWidgetItem(_format_timestamp(updated)), + ) error_text = str(row.get("error_text") or "") self._table.setItem(row_idx, 6, QTableWidgetItem(error_text)) diff --git a/ui/tour.py b/ui/tour.py index 3eba7f9..b7e2806 100644 --- a/ui/tour.py +++ b/ui/tour.py @@ -19,14 +19,14 @@ QPainterPath, QPen, QPixmap, + QPoint, QPushButton, QRect, - QTimer, + QSize, Qt, + QTimer, QVBoxLayout, QWidget, - QPoint, - QSize, ) @@ -258,9 +258,15 @@ def _position_card(self) -> None: highlight = self._highlight_rect positions: list[QPoint] = [] right_pos = QPoint(highlight.right() + margin, highlight.top()) - left_pos = QPoint(highlight.left() - card_size.width() - margin, highlight.top()) + left_pos = QPoint( + highlight.left() - card_size.width() - margin, + highlight.top(), + ) bottom_pos = QPoint(highlight.left(), highlight.bottom() + margin) - top_pos = QPoint(highlight.left(), highlight.top() - card_size.height() - margin) + top_pos = QPoint( + highlight.left(), + highlight.top() - card_size.height() - margin, + ) positions.extend([right_pos, left_pos, bottom_pos, top_pos]) for pos in positions: @@ -270,7 +276,10 @@ def _position_card(self) -> None: return fallback_x = min( - max(available_rect.left(), highlight.center().x() - card_size.width() // 2), + max( + available_rect.left(), + highlight.center().x() - card_size.width() // 2, + ), available_rect.right() - card_size.width(), ) fallback_y = min( @@ -282,7 +291,10 @@ def _position_card(self) -> None: center_x = available_rect.center().x() - card_size.width() // 2 center_y = available_rect.center().y() - card_size.height() // 2 - self._card.move(max(available_rect.left(), center_x), max(available_rect.top(), center_y)) + self._card.move( + max(available_rect.left(), center_x), + max(available_rect.top(), center_y), + ) # ------------------------------------------------------------------ def _go_previous(self) -> None: diff --git a/utils/__init__.py b/utils/__init__.py index 2fc57c4..e0504cb 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -143,9 +143,9 @@ def melhorar_logo( def backup_automatico() -> None: """Realiza cópia de segurança dos arquivos de dados.""" + from db import store as db_store from log import logger from persistence import carregar_config - from db import store as db_store base = _base_dir() origem = os.path.join(base, "assets") diff --git a/utils/fs.py b/utils/fs.py index b431377..d5c947b 100644 --- a/utils/fs.py +++ b/utils/fs.py @@ -4,6 +4,7 @@ import hashlib from pathlib import Path + __all__ = ["canonical_path", "file_hash"] @@ -20,7 +21,12 @@ def canonical_path(path: str | Path) -> Path: return target.expanduser().resolve(strict=False) -def file_hash(path: str | Path, *, algorithm: str = "sha256", chunk_size: int = 65536) -> str: +def file_hash( + path: str | Path, + *, + algorithm: str = "sha256", + chunk_size: int = 65536, +) -> str: """Return the hexadecimal hash for ``path`` using ``algorithm``. Args: