diff --git a/CHANGELOG.md b/CHANGELOG.md index 17052b2..2a161cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Development version +* Migrate away from `brother_ql.devicedependent`. + # Version 0.4.0 - 2024-10-17 * Drop support for Python 3.8. diff --git a/brother_ql_web/cli.py b/brother_ql_web/cli.py index 36e5b87..d424527 100644 --- a/brother_ql_web/cli.py +++ b/brother_ql_web/cli.py @@ -4,9 +4,11 @@ import random import sys from argparse import ArgumentParser, Namespace +from operator import attrgetter from typing import cast -from brother_ql.devicedependent import models, label_sizes +from brother_ql.labels import ALL_LABELS +from brother_ql.models import ALL_MODELS from brother_ql_web.configuration import Configuration, Font from brother_ql_web.utils import collect_fonts @@ -46,7 +48,7 @@ def get_parameters() -> Namespace: parser.add_argument( "--model", default=False, - choices=models, + choices=list(map(attrgetter("identifier"), ALL_MODELS)), help="The model of your printer (default: QL-500)", ) parser.add_argument( @@ -127,6 +129,7 @@ def update_configuration_from_parameters( configuration.label.default_orientation = parameters.default_orientation # Configuration issues. + label_sizes = list(map(attrgetter("identifier"), ALL_LABELS)) if configuration.label.default_size not in label_sizes: raise InvalidLabelSize( "Invalid default label size. Please choose one of the following:\n" diff --git a/brother_ql_web/labels.py b/brother_ql_web/labels.py index 2607881..36ff191 100644 --- a/brother_ql_web/labels.py +++ b/brother_ql_web/labels.py @@ -6,13 +6,7 @@ from typing import cast, Literal from brother_ql import BrotherQLRaster, create_label -from brother_ql.devicedependent import ( - ENDLESS_LABEL, - DIE_CUT_LABEL, - ROUND_DIE_CUT_LABEL, - label_type_specs, -) -from brother_ql.labels import FormFactor +from brother_ql.labels import ALL_LABELS, FormFactor, Label from brother_ql_web.configuration import Configuration from brother_ql_web import utils from PIL import Image, ImageDraw, ImageFont @@ -46,9 +40,16 @@ class LabelParameters: # doubled. The generator/calculation methods have to be updated accordingly. high_quality: bool = False + @property + def _label(self) -> Label: + for label in ALL_LABELS: + if label.identifier == self.label_size: + return label + raise LookupError("Unknown label_size") + @property def kind(self) -> FormFactor: - return cast(FormFactor, label_type_specs[self.label_size]["kind"]) + return self._label.form_factor def _scale_margin(self, margin: int) -> int: return int(self.font_size * margin / 100.0) @@ -88,12 +89,7 @@ def font_path(self) -> str: @property def width_height(self) -> tuple[int, int]: - try: - width, height = cast( - tuple[int, int], label_type_specs[self.label_size]["dots_printable"] - ) - except KeyError: - raise LookupError("Unknown label_size") + width, height = self._label.dots_printable if height > width: width, height = height, width @@ -122,14 +118,14 @@ def _determine_image_dimensions( text_width, text_height = (right - left, bottom - top) width, height = parameters.width_height if parameters.orientation == "standard": - if parameters.kind in (ENDLESS_LABEL,): + if parameters.kind in (FormFactor.ENDLESS,): height = ( text_height + parameters.margin_top_scaled + parameters.margin_bottom_scaled ) elif parameters.orientation == "rotated": - if parameters.kind in (ENDLESS_LABEL,): + if parameters.kind in (FormFactor.ENDLESS,): width = ( text_width + parameters.margin_left_scaled @@ -146,7 +142,7 @@ def _determine_text_offsets( parameters: LabelParameters, ) -> tuple[int, int]: if parameters.orientation == "standard": - if parameters.kind in (DIE_CUT_LABEL, ROUND_DIE_CUT_LABEL): + if parameters.kind in (FormFactor.DIE_CUT, FormFactor.ROUND_DIE_CUT): vertical_offset = (height - text_height) // 2 vertical_offset += ( parameters.margin_top_scaled - parameters.margin_bottom_scaled @@ -159,7 +155,7 @@ def _determine_text_offsets( vertical_offset += ( parameters.margin_top_scaled - parameters.margin_bottom_scaled ) // 2 - if parameters.kind in (DIE_CUT_LABEL, ROUND_DIE_CUT_LABEL): + if parameters.kind in (FormFactor.DIE_CUT, FormFactor.ROUND_DIE_CUT): horizontal_offset = max((width - text_width) // 2, 0) else: horizontal_offset = parameters.margin_left_scaled @@ -219,9 +215,9 @@ def generate_label( red: bool = "red" in parameters.label_size rotate: int | str = 0 - if parameters.kind == ENDLESS_LABEL: + if parameters.kind == FormFactor.ENDLESS: rotate = 0 if parameters.orientation == "standard" else 90 - elif parameters.kind in (ROUND_DIE_CUT_LABEL, DIE_CUT_LABEL): + elif parameters.kind in (FormFactor.DIE_CUT, FormFactor.ROUND_DIE_CUT): rotate = "auto" if parameters.high_quality: diff --git a/brother_ql_web/utils.py b/brother_ql_web/utils.py index 323cd7c..f7470bd 100644 --- a/brother_ql_web/utils.py +++ b/brother_ql_web/utils.py @@ -4,7 +4,7 @@ from typing import cast from brother_ql.backends import backend_factory, BrotherQLBackendGeneric, guess_backend -from brother_ql.devicedependent import label_type_specs, label_sizes +from brother_ql.labels import ALL_LABELS from brother_ql_web.configuration import Configuration from brother_ql_web.font_helpers import get_fonts @@ -24,7 +24,7 @@ def collect_fonts(configuration: Configuration) -> dict[str, dict[str, str]]: def get_label_sizes() -> list[tuple[str, str]]: - return [(name, cast(str, label_type_specs[name]["name"])) for name in label_sizes] + return [(label.identifier, label.name) for label in ALL_LABELS] class BackendGuessingError(ValueError): diff --git a/stubs/brother_ql/models.pyi b/stubs/brother_ql/models.pyi index 9256af9..2c66718 100644 --- a/stubs/brother_ql/models.pyi +++ b/stubs/brother_ql/models.pyi @@ -1,39 +1,42 @@ +from __future__ import annotations + from _typeshed import Incomplete +from typing import NoReturn from brother_ql.helpers import ElementsManager as ElementsManager class Model: - identifier: Incomplete - min_max_length_dots: Incomplete - min_max_feed: Incomplete - number_bytes_per_row: Incomplete - additional_offset_r: Incomplete - mode_setting: Incomplete - cutting: Incomplete - expanded_mode: Incomplete - compression: Incomplete - two_color: Incomplete + identifier: str + min_max_length_dots: tuple[int, int] + min_max_feed: tuple[int, int] + number_bytes_per_row: int + additional_offset_r: int + mode_setting: bool + cutting: bool + expanded_mode: bool + compression: bool + two_color: bool @property - def name(self): ... + def name(self) -> str: ... def __init__( self, - identifier, - min_max_length_dots, - min_max_feed, - number_bytes_per_row, - additional_offset_r, - mode_setting, - cutting, - expanded_mode, - compression, - two_color, - ) -> None: ... - def __lt__(self, other): ... - def __le__(self, other): ... - def __gt__(self, other): ... - def __ge__(self, other): ... + identifier: str, + min_max_length_dots: tuple[int, int], + min_max_feed: tuple[int, int] = (35, 1500), + number_bytes_per_row: int = 90, + additional_offset_r: int = 0, + mode_setting: bool = True, + cutting: bool = True, + expanded_mode: bool = True, + compression: bool = True, + two_color: bool = False, + ) -> NoReturn: ... + def __lt__(self, other: Model) -> bool: ... + def __le__(self, other: Model) -> bool: ... + def __gt__(self, other: Model) -> bool: ... + def __ge__(self, other: Model) -> bool: ... -ALL_MODELS: Incomplete +ALL_MODELS: list[Model] class ModelsManager(ElementsManager): - DEFAULT_ELEMENTS: Incomplete + DEFAULT_ELEMENTS: list[Model] ELEMENTS_NAME: str