From d73626d09cc325307e3d070b11a0dbfaa81a8c71 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sat, 15 Jun 2024 09:38:30 +0000 Subject: [PATCH 01/34] Add "DYMO LabelMANAGER PC II" device ID --- src/labelle/lib/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index dc17da49..616b55da 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -37,6 +37,7 @@ SUPPORTED_PRODUCTS = { 0x0011: "DYMO LabelMANAGER PC", 0x0015: "LabelPoint 350", + 0x001C: "DYMO LabelMANAGER PC II", 0x1001: "LabelManager PnP (no mode switch)", 0x1002: "LabelManager PnP (mode switch)", 0x1003: f"LabelManager 420P (no mode switch) {UNCONFIRMED_MESSAGE}", From 06f61ea5af96b43c18760839c807fcfae5661322 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 16 Jun 2024 14:33:39 +0000 Subject: [PATCH 02/34] Add 24mm tape type --- src/labelle/lib/devices/dymo_labeler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 066309f6..3144c0c0 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -240,7 +240,7 @@ class DymoLabeler: LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 LABELER_PRINT_HEAD_HEIGHT_MM = 8.2 - SUPPORTED_TAPE_SIZES_MM = (19, 12, 9, 6) + SUPPORTED_TAPE_SIZES_MM = (24, 19, 12, 9, 6) DEFAULT_TAPE_SIZE_MM = 12 def __init__( From 3aecdc785e57d0bcdbc4ab1f50ea41f2b5b87037 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:01:26 +0000 Subject: [PATCH 03/34] device specific init WIP --- src/labelle/gui/gui.py | 2 +- src/labelle/gui/q_settings_toolbar.py | 2 +- src/labelle/lib/constants.py | 60 +++++++++++++++---- src/labelle/lib/devices/device_manager.py | 4 +- src/labelle/lib/devices/dymo_labeler.py | 70 +++++++++++++++++++++-- 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index 3bcc679f..e13fc052 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -63,7 +63,7 @@ def _init_elements(self) -> None: self._device_manager = DeviceManager() self._dymo_labeler = DymoLabeler() self._settings_toolbar.update_labeler_context( - supported_tape_sizes=self._dymo_labeler.SUPPORTED_TAPE_SIZES_MM, + supported_tape_sizes=self._dymo_labeler.labeler_supported_tape_sizes, installed_tape_size=self._dymo_labeler.tape_size_mm, minimum_horizontal_margin_mm=self._dymo_labeler.minimum_horizontal_margin_mm, ) diff --git a/src/labelle/gui/q_settings_toolbar.py b/src/labelle/gui/q_settings_toolbar.py index 57b45e6a..f633451a 100644 --- a/src/labelle/gui/q_settings_toolbar.py +++ b/src/labelle/gui/q_settings_toolbar.py @@ -57,7 +57,7 @@ def _init_elements(self) -> None: def update_labeler_context( self, - supported_tape_sizes: tuple[int, ...], + supported_tape_sizes: list[int], installed_tape_size: int, minimum_horizontal_margin_mm: float, ) -> None: diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index 616b55da..f48af6fe 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -34,20 +34,56 @@ "WARNING: This device is not confirmed to work with this software. Please " "report your experiences in https://github.com/labelle-org/labelle/issues/4" ) + + +# Supported USB device ID enumeration +class SUPPORTED_DEVICE_ID(Enum): + LABELMANAGER_PC = 0x0011 + LABELPOINT_350 = 0x0015 + LABELMANAGER_PC_II = 0x001C + LABELMANAGER_PNP_NO_MODE_SWITCH = 0x1001 + LABELMANAGER_PNP_MODE_SWITCH = 0x1002 + LABELMANAGER_420P_NO_MODE_SWITCH = 0x1003 + LABELMANAGER_420P_MODE_SWITCH = 0x1004 + LABELMANAGER_280P_NO_MODE_SWITCH = 0x1005 + LABELMANAGER_280P_MODE_SWITCH = 0x1006 + LABELMANAGER_WIRELESS_PNP_NO_MODE_SWITCH = 0x1007 + LABELMANAGER_WIRELESS_PNP_MODE_SWITCH = 0x1008 + MOBILE_LABELER = 0x1009 + + +# fmt: off +# Very bad I know, but it keeps a loop of removing the line breaks and errroring + SUPPORTED_PRODUCTS = { - 0x0011: "DYMO LabelMANAGER PC", - 0x0015: "LabelPoint 350", - 0x001C: "DYMO LabelMANAGER PC II", - 0x1001: "LabelManager PnP (no mode switch)", - 0x1002: "LabelManager PnP (mode switch)", - 0x1003: f"LabelManager 420P (no mode switch) {UNCONFIRMED_MESSAGE}", - 0x1004: f"LabelManager 420P (mode switch) {UNCONFIRMED_MESSAGE}", - 0x1005: "LabelManager 280 (no mode switch)", - 0x1006: "LabelManager 280 (no mode switch)", - 0x1007: f"LabelManager Wireless PnP (no mode switch) {UNCONFIRMED_MESSAGE}", - 0x1008: f"LabelManager Wireless PnP (mode switch) {UNCONFIRMED_MESSAGE}", - 0x1009: f"MobileLabeler {UNCONFIRMED_MESSAGE}", + SUPPORTED_DEVICE_ID.LABELMANAGER_PC: + "DYMO LabelMANAGER PC", + SUPPORTED_DEVICE_ID.LABELPOINT_350: + "LabelPoint 350", + SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II: + "DYMO LabelMANAGER PC II", + SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_NO_MODE_SWITCH: + "LabelManager PnP (no mode switch)", + SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_MODE_SWITCH: + "LabelManager PnP (mode switch)", + SUPPORTED_DEVICE_ID.LABELMANAGER_420P_NO_MODE_SWITCH: + f"LabelManager 420P (no mode switch) {UNCONFIRMED_MESSAGE}", + SUPPORTED_DEVICE_ID.LABELMANAGER_420P_MODE_SWITCH: + f"LabelManager 420P (mode switch) {UNCONFIRMED_MESSAGE}", + SUPPORTED_DEVICE_ID.LABELMANAGER_280P_NO_MODE_SWITCH: + "LabelManager 280 (no mode switch)", + SUPPORTED_DEVICE_ID.LABELMANAGER_280P_MODE_SWITCH: + "LabelManager 280 (no mode switch)", + SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_NO_MODE_SWITCH: + f"LabelManager Wireless PnP (no mode switch) {UNCONFIRMED_MESSAGE}", + SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_MODE_SWITCH: + f"LabelManager Wireless PnP (mode switch) {UNCONFIRMED_MESSAGE}", + SUPPORTED_DEVICE_ID.MOBILE_LABELER: + f"MobileLabeler {UNCONFIRMED_MESSAGE}", } +# fmt: on + + DEV_VENDOR = 0x0922 PRINTER_INTERFACE_CLASS = 0x07 diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 0cd9b508..0e198c7d 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -5,6 +5,7 @@ from usb.core import NoBackendError, USBError from labelle.lib.constants import ( + SUPPORTED_DEVICE_ID, SUPPORTED_PRODUCTS, UNCONFIRMED_MESSAGE, ) @@ -80,7 +81,8 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice LOG.debug(dev.device_info) dev = devices[0] if dev.is_supported: - msg = f"Recognized device as {SUPPORTED_PRODUCTS[dev.id_product]}" + msg = f"Recognized device as \ + {SUPPORTED_PRODUCTS[SUPPORTED_DEVICE_ID(dev.id_product)]}" else: msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}" LOG.debug(msg) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 3144c0c0..ff06bfcc 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -10,12 +10,13 @@ import array import logging import math +from typing import Any import usb from PIL import Image from usb.core import NoBackendError, USBError -from labelle.lib.constants import ESC, SYN +from labelle.lib.constants import ESC, SUPPORTED_DEVICE_ID, SYN from labelle.lib.devices.usb_device import UsbDevice, UsbDeviceError from labelle.lib.utils import mm_to_px @@ -240,9 +241,18 @@ class DymoLabeler: LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 LABELER_PRINT_HEAD_HEIGHT_MM = 8.2 - SUPPORTED_TAPE_SIZES_MM = (24, 19, 12, 9, 6) DEFAULT_TAPE_SIZE_MM = 12 + # Default supported tape sizes + labeler_supported_tape_sizes: list + + # Default printer head size in dots + labeler_print_head_width_pixels: int + + # Default printer margins (for each tape size) + # { tapeSizeMm : (firstVisiblePixel, lastVisiblePixel) } + labeler_label_vertical_margins: dict[int, Any] + def __init__( self, tape_size_mm: int | None = None, @@ -250,13 +260,62 @@ def __init__( ): if tape_size_mm is None: tape_size_mm = self.DEFAULT_TAPE_SIZE_MM - if tape_size_mm not in self.SUPPORTED_TAPE_SIZES_MM: + + self._device = device + + # --- Set options based on printer type --- + if self._device is not None: + if self._device.id_product == SUPPORTED_DEVICE_ID.LABELMANAGER_PC: + self.labeler_print_head_width_pixels = 128 + self.labeler_supported_tape_sizes = [19, 12, 9, 6] + self.labeler_label_vertical_margins = { + 6: (11, 51), + 9: (1, 62), + 12: (0, 63), + 19: (0, 0), + } + + elif self._device.id_product == SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II: + self.labeler_print_head_width_pixels = 128 + self.labeler_supported_tape_sizes = [24, 19, 12, 9, 6] + self.labeler_label_vertical_margins = { + 6: (11, 51), + 9: (1, 62), + 12: (0, 63), + 19: (0, 0), + 24: (0, 0), + } + + else: + # Defaults (For most printers) + self.labeler_print_head_width_pixels = 64 + self.labeler_supported_tape_sizes = [12, 9, 6] + self.labeler_label_vertical_margins = { + 6: (11, 51), + 9: (1, 62), + 12: (0, 63), + } + else: + # Simulator (supports everything) + self.labeler_print_head_width_pixels = 128 + self.labeler_supported_tape_sizes = [24, 19, 12, 9, 6] + self.labeler_label_vertical_margins = { + 6: (11, 51), + 9: (1, 62), + 12: (0, 63), + 19: (0, 0), + 24: (0, 0), + } + + # --- End set options based on printer type --- + + # Check if selected tape size is supported by printer + if tape_size_mm not in self.labeler_supported_tape_sizes: raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self.SUPPORTED_TAPE_SIZES_MM}" + f"Supported sizes: {self.labeler_supported_tape_sizes}" ) self.tape_size_mm = tape_size_mm - self._device = device @property def height_px(self): @@ -277,6 +336,7 @@ def minimum_horizontal_margin_mm(self): @property def labeler_margin_px(self) -> tuple[float, float]: + # ToDo: Use preset margins here instead of this calculation vertical_margin_mm = max( 0, (self.tape_size_mm - self.LABELER_PRINT_HEAD_HEIGHT_MM) / 2 ) From 1872e408cef34ed64e790822543ac6438c91a80c Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Thu, 20 Jun 2024 18:01:32 +0000 Subject: [PATCH 04/34] Add DeviceConfig class for each printer Each printer/labeler now has a config class instance setting its specific properties. --- src/labelle/gui/gui.py | 2 +- src/labelle/lib/constants.py | 129 +++++++++++++++++----- src/labelle/lib/devices/device_config.py | 71 ++++++++++++ src/labelle/lib/devices/device_manager.py | 16 +++ src/labelle/lib/devices/dymo_labeler.py | 79 +++---------- 5 files changed, 206 insertions(+), 91 deletions(-) create mode 100644 src/labelle/lib/devices/device_config.py diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index e13fc052..d0f42ef2 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -63,7 +63,7 @@ def _init_elements(self) -> None: self._device_manager = DeviceManager() self._dymo_labeler = DymoLabeler() self._settings_toolbar.update_labeler_context( - supported_tape_sizes=self._dymo_labeler.labeler_supported_tape_sizes, + supported_tape_sizes=self._dymo_labeler._deviceConfig.supportedTapeSizes, installed_tape_size=self._dymo_labeler.tape_size_mm, minimum_horizontal_margin_mm=self._dymo_labeler.minimum_horizontal_margin_mm, ) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index f48af6fe..95a8da41 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -13,11 +13,12 @@ # either sysfs is unavailable or unusable by this script for some reason. # Please beware that DEV_NODE must be set to None when not used, else you will # be bitten by the NameError exception. -from enum import Enum +from enum import IntEnum from pathlib import Path import labelle.resources.fonts import labelle.resources.icons +from labelle.lib.devices.device_config import DeviceConfig try: from pyqrcode import QRCode @@ -37,7 +38,7 @@ # Supported USB device ID enumeration -class SUPPORTED_DEVICE_ID(Enum): +class SUPPORTED_DEVICE_ID(IntEnum): LABELMANAGER_PC = 0x0011 LABELPOINT_350 = 0x0015 LABELMANAGER_PC_II = 0x001C @@ -52,36 +53,110 @@ class SUPPORTED_DEVICE_ID(Enum): MOBILE_LABELER = 0x1009 -# fmt: off -# Very bad I know, but it keeps a loop of removing the line breaks and errroring +# Create supported products list +SUPPORTED_PRODUCTS = [] -SUPPORTED_PRODUCTS = { - SUPPORTED_DEVICE_ID.LABELMANAGER_PC: +# ---- Supported USB Devices configuration ---- +SUPPORTED_PRODUCTS.append( + DeviceConfig( "DYMO LabelMANAGER PC", - SUPPORTED_DEVICE_ID.LABELPOINT_350: + [int(SUPPORTED_DEVICE_ID.LABELMANAGER_PC)], + # ToDo: Validate config! + 128, + [6, 9, 12, 19], + {6: (44, 85), 9: (31, 94), 12: (38, 117), 19: (2, 127)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( "LabelPoint 350", - SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II: + [int(SUPPORTED_DEVICE_ID.LABELPOINT_350)], + # ToDo: Validate config! + 64, + [6, 9, 12], + {6: (44, 85), 9: (31, 94), 12: (38, 117)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( "DYMO LabelMANAGER PC II", - SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_NO_MODE_SWITCH: - "LabelManager PnP (no mode switch)", - SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_MODE_SWITCH: - "LabelManager PnP (mode switch)", - SUPPORTED_DEVICE_ID.LABELMANAGER_420P_NO_MODE_SWITCH: - f"LabelManager 420P (no mode switch) {UNCONFIRMED_MESSAGE}", - SUPPORTED_DEVICE_ID.LABELMANAGER_420P_MODE_SWITCH: - f"LabelManager 420P (mode switch) {UNCONFIRMED_MESSAGE}", - SUPPORTED_DEVICE_ID.LABELMANAGER_280P_NO_MODE_SWITCH: - "LabelManager 280 (no mode switch)", - SUPPORTED_DEVICE_ID.LABELMANAGER_280P_MODE_SWITCH: - "LabelManager 280 (no mode switch)", - SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_NO_MODE_SWITCH: - f"LabelManager Wireless PnP (no mode switch) {UNCONFIRMED_MESSAGE}", - SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_MODE_SWITCH: - f"LabelManager Wireless PnP (mode switch) {UNCONFIRMED_MESSAGE}", - SUPPORTED_DEVICE_ID.MOBILE_LABELER: + [int(SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II)], + 128, + [6, 9, 12, 19, 24], + {6: (44, 85), 9: (31, 94), 12: (38, 117), 19: (2, 127), 24: (2, 127)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( + "LabelManager PnP", + [ + int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_NO_MODE_SWITCH), + int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_MODE_SWITCH), + ], + # ToDo: Validate config! + 64, + [6, 9, 12], + {6: (44, 85), 9: (31, 94), 12: (38, 117)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( + f"LabelManager 420P {UNCONFIRMED_MESSAGE}", + [ + int(SUPPORTED_DEVICE_ID.LABELMANAGER_420P_NO_MODE_SWITCH), + int(SUPPORTED_DEVICE_ID.LABELMANAGER_420P_MODE_SWITCH), + ], + # ToDo: Validate config! + 64, + [6, 9, 12], + {6: (44, 85), 9: (31, 94), 12: (38, 117)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( + "LabelManager 280", + [ + int(SUPPORTED_DEVICE_ID.LABELMANAGER_280P_MODE_SWITCH), + int(SUPPORTED_DEVICE_ID.LABELMANAGER_280P_NO_MODE_SWITCH), + ], + # ToDo: Validate config! + 64, + [6, 9, 12], + {6: (44, 85), 9: (31, 94), 12: (38, 117)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( + f"LabelManager Wireless PnP {UNCONFIRMED_MESSAGE}", + [ + int(SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_NO_MODE_SWITCH), + int(SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_MODE_SWITCH), + ], + # ToDo: Validate config! + 64, + [6, 9, 12], + {6: (44, 85), 9: (31, 94), 12: (38, 117)}, + ) +) +SUPPORTED_PRODUCTS.append( + DeviceConfig( f"MobileLabeler {UNCONFIRMED_MESSAGE}", -} -# fmt: on + [int(SUPPORTED_DEVICE_ID.MOBILE_LABELER)], + # ToDo: Validate config! + 64, + [6, 9, 12], + {6: (44, 85), 9: (31, 94), 12: (38, 117)}, + ) +) + +# Simulator configuration +SIMULATOR_CONFIG = DeviceConfig( + "Simulator", + [0], + 128, + [6, 9, 12, 19, 24], + {6: (44, 85), 9: (31, 94), 12: (38, 117), 19: (2, 127), 24: (2, 127)}, +) DEV_VENDOR = 0x0922 diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py new file mode 100644 index 00000000..9a9cd5be --- /dev/null +++ b/src/labelle/lib/devices/device_config.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +import logging +from typing import Tuple + +LOG = logging.getLogger(__name__) + + +class DeviceConfig: + """Configuration object for the capabilities of a label printer.""" + + name: str + """Name of this device""" + + deviceIDs: list[int] + """List of USB Device ID's this device can identify as""" + + printHeadSizePixels: int + """Size of the print head in pixels (use calibration routine to determine)""" + + supportedTapeSizes: list[int] + """List of supported tape sizes in mm""" + + marginsPerTape: dict[int, Tuple[int, int]] + """ + Dictonary of print margins per tape size + Entry: TapeSizeMM : (topMarginInPx, bottomMarginInPx) + Use the calibration routine to determine these for each tape size + """ + + def __init__( + self, + name: str, + deviceIDs: list[int], + printHeadSizePixels: int, + supportedTapeSizes: list[int], + marginsPerTape: dict[int, Tuple[int, int]], + ): + """Initialize a Labeler config object.""" + self.name = name + self.deviceIDs = deviceIDs + self.printHeadSizePixels = printHeadSizePixels + self.supportedTapeSizes = supportedTapeSizes + self.marginsPerTape = marginsPerTape + + def matches_device_id(self, idValue: int) -> bool: + """Check if the a device ID matches this config.""" + if idValue in self.deviceIDs: + return True + else: + return False + + def get_tape_print_margins(self, tapeSizeMM: int) -> tuple[int, int]: + """Get print margins for a specific tape size. + + :param tapeSizeMM: Tape size in mm + :return: Margins tuple, None if not supported + """ + if tapeSizeMM in self.supportedTapeSizes: + if tapeSizeMM in self.marginsPerTape: + # Return specified margins + return self.marginsPerTape[tapeSizeMM] + else: + # No specific margins set, return default + return (0, 0) + else: + # Tape size not supported + raise ValueError( + f"Unsupported tape size {tapeSizeMM}mm. " + f"Supported sizes: {self.supportedTapeSizes}mm" + ) diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 0e198c7d..0be70032 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -9,6 +9,7 @@ SUPPORTED_PRODUCTS, UNCONFIRMED_MESSAGE, ) +from labelle.lib.devices.device_config import DeviceConfig from labelle.lib.devices.usb_device import UsbDevice LOG = logging.getLogger(__name__) @@ -87,3 +88,18 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}" LOG.debug(msg) return dev + + +def get_device_config_by_id(idValue: int) -> DeviceConfig: + """Get a labeler device config with USB ID. + + :param idValue: USB ID value + :return: Device config, None if not found + """ + # + for device in SUPPORTED_PRODUCTS: + if device.matches_device_id(idValue) is True: + return device + + # No device config found + return None diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index ff06bfcc..fdea5a49 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -10,13 +10,14 @@ import array import logging import math -from typing import Any import usb from PIL import Image from usb.core import NoBackendError, USBError -from labelle.lib.constants import ESC, SUPPORTED_DEVICE_ID, SYN +from labelle.lib.constants import ESC, SIMULATOR_CONFIG, SYN +from labelle.lib.devices.device_config import DeviceConfig +from labelle.lib.devices.device_manager import get_device_config_by_id from labelle.lib.devices.usb_device import UsbDevice, UsbDeviceError from labelle.lib.utils import mm_to_px @@ -237,83 +238,36 @@ def _raw_print_label(self, lines: list[list[int]]): class DymoLabeler: _device: UsbDevice | None + _deviceConfig: DeviceConfig | None tape_size_mm: int LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 LABELER_PRINT_HEAD_HEIGHT_MM = 8.2 DEFAULT_TAPE_SIZE_MM = 12 - # Default supported tape sizes - labeler_supported_tape_sizes: list - - # Default printer head size in dots - labeler_print_head_width_pixels: int - - # Default printer margins (for each tape size) - # { tapeSizeMm : (firstVisiblePixel, lastVisiblePixel) } - labeler_label_vertical_margins: dict[int, Any] - def __init__( self, tape_size_mm: int | None = None, device: UsbDevice | None = None, ): - if tape_size_mm is None: - tape_size_mm = self.DEFAULT_TAPE_SIZE_MM - self._device = device - # --- Set options based on printer type --- if self._device is not None: - if self._device.id_product == SUPPORTED_DEVICE_ID.LABELMANAGER_PC: - self.labeler_print_head_width_pixels = 128 - self.labeler_supported_tape_sizes = [19, 12, 9, 6] - self.labeler_label_vertical_margins = { - 6: (11, 51), - 9: (1, 62), - 12: (0, 63), - 19: (0, 0), - } - - elif self._device.id_product == SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II: - self.labeler_print_head_width_pixels = 128 - self.labeler_supported_tape_sizes = [24, 19, 12, 9, 6] - self.labeler_label_vertical_margins = { - 6: (11, 51), - 9: (1, 62), - 12: (0, 63), - 19: (0, 0), - 24: (0, 0), - } - - else: - # Defaults (For most printers) - self.labeler_print_head_width_pixels = 64 - self.labeler_supported_tape_sizes = [12, 9, 6] - self.labeler_label_vertical_margins = { - 6: (11, 51), - 9: (1, 62), - 12: (0, 63), - } + # Retrieve device config + self._deviceConfig = get_device_config_by_id(self._device.id_product) else: - # Simulator (supports everything) - self.labeler_print_head_width_pixels = 128 - self.labeler_supported_tape_sizes = [24, 19, 12, 9, 6] - self.labeler_label_vertical_margins = { - 6: (11, 51), - 9: (1, 62), - 12: (0, 63), - 19: (0, 0), - 24: (0, 0), - } - - # --- End set options based on printer type --- - - # Check if selected tape size is supported by printer - if tape_size_mm not in self.labeler_supported_tape_sizes: + # Use simulator config + self._deviceConfig = SIMULATOR_CONFIG + + if self._deviceConfig is None: + raise ValueError(f"Unsupported device type {device.id_product}") + + if tape_size_mm is None: + tape_size_mm = self.DEFAULT_TAPE_SIZE_MM + if tape_size_mm not in self._deviceConfig.supportedTapeSizes: raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self.labeler_supported_tape_sizes}" + f"Supported sizes: {self._deviceConfig.supportedTapeSizes}mm" ) self.tape_size_mm = tape_size_mm @@ -336,7 +290,6 @@ def minimum_horizontal_margin_mm(self): @property def labeler_margin_px(self) -> tuple[float, float]: - # ToDo: Use preset margins here instead of this calculation vertical_margin_mm = max( 0, (self.tape_size_mm - self.LABELER_PRINT_HEAD_HEIGHT_MM) / 2 ) From 1b279b3c52debb3954fd1b0f59bab2d8dc93e12a Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Thu, 20 Jun 2024 18:22:03 +0000 Subject: [PATCH 05/34] Please mypy... --- src/labelle/lib/constants.py | 2 +- src/labelle/lib/devices/device_manager.py | 2 +- src/labelle/lib/devices/dymo_labeler.py | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index 95a8da41..a39d4408 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -13,7 +13,7 @@ # either sysfs is unavailable or unusable by this script for some reason. # Please beware that DEV_NODE must be set to None when not used, else you will # be bitten by the NameError exception. -from enum import IntEnum +from enum import Enum, IntEnum from pathlib import Path import labelle.resources.fonts diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 0be70032..4ec93398 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -90,7 +90,7 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice return dev -def get_device_config_by_id(idValue: int) -> DeviceConfig: +def get_device_config_by_id(idValue: int) -> DeviceConfig | None: """Get a labeler device config with USB ID. :param idValue: USB ID value diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index fdea5a49..ae8b6064 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -238,7 +238,7 @@ def _raw_print_label(self, lines: list[list[int]]): class DymoLabeler: _device: UsbDevice | None - _deviceConfig: DeviceConfig | None + _deviceConfig: DeviceConfig tape_size_mm: int LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 @@ -254,14 +254,15 @@ def __init__( if self._device is not None: # Retrieve device config - self._deviceConfig = get_device_config_by_id(self._device.id_product) + foundConfig = get_device_config_by_id(self._device.id_product) + if foundConfig is not None: + self._deviceConfig = foundConfig + else: + raise ValueError(f"Unsupported device type {self._device.id_product}") else: # Use simulator config self._deviceConfig = SIMULATOR_CONFIG - if self._deviceConfig is None: - raise ValueError(f"Unsupported device type {device.id_product}") - if tape_size_mm is None: tape_size_mm = self.DEFAULT_TAPE_SIZE_MM if tape_size_mm not in self._deviceConfig.supportedTapeSizes: From 2353512c9ded9eb7bfbdd3e8d06f256032fa1127 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:04:51 +0000 Subject: [PATCH 06/34] Fix usb device recognition --- src/labelle/lib/devices/device_manager.py | 8 +++++--- src/labelle/lib/devices/usb_device.py | 13 +++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 4ec93398..a96e2c6b 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -5,7 +5,6 @@ from usb.core import NoBackendError, USBError from labelle.lib.constants import ( - SUPPORTED_DEVICE_ID, SUPPORTED_PRODUCTS, UNCONFIRMED_MESSAGE, ) @@ -82,8 +81,11 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice LOG.debug(dev.device_info) dev = devices[0] if dev.is_supported: - msg = f"Recognized device as \ - {SUPPORTED_PRODUCTS[SUPPORTED_DEVICE_ID(dev.id_product)]}" + foundDeviceConfig: DeviceConfig | None = get_device_config_by_id( + dev.id_product + ) + if foundDeviceConfig is not None: + msg = f"Recognized device as {foundDeviceConfig.name}" else: msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}" LOG.debug(msg) diff --git a/src/labelle/lib/devices/usb_device.py b/src/labelle/lib/devices/usb_device.py index 2770091c..113d5a7d 100644 --- a/src/labelle/lib/devices/usb_device.py +++ b/src/labelle/lib/devices/usb_device.py @@ -87,10 +87,15 @@ def _is_supported_vendor(dev: usb.core.Device) -> bool: @property def is_supported(self) -> bool: - return ( - self._is_supported_vendor(self._dev) - and self.id_product in SUPPORTED_PRODUCTS - ) + # If vendor matches + if self._is_supported_vendor(self._dev) is True: + # if one item in our supported products matches the device id + for device in SUPPORTED_PRODUCTS: + if device.matches_device_id(self.id_product) is True: + return True + + # no matches found + return False @staticmethod def supported_devices() -> set[UsbDevice]: From dcc6234b56275ecd519f099192ed6bcf243f70ae Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:10:15 +0000 Subject: [PATCH 07/34] Calculate margins instead of using lookup table --- src/labelle/lib/constants.py | 27 ++++++--- src/labelle/lib/devices/device_config.py | 75 +++++++++++++++++++----- src/labelle/lib/devices/dymo_labeler.py | 15 +++-- 3 files changed, 86 insertions(+), 31 deletions(-) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index a39d4408..d916ebe0 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -62,9 +62,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): "DYMO LabelMANAGER PC", [int(SUPPORTED_DEVICE_ID.LABELMANAGER_PC)], # ToDo: Validate config! + # Printhead 128 Pixels, distributed over 18mm of active area 128, + 18, [6, 9, 12, 19], - {6: (44, 85), 9: (31, 94), 12: (38, 117), 19: (2, 127)}, ) ) SUPPORTED_PRODUCTS.append( @@ -72,18 +73,20 @@ class SUPPORTED_DEVICE_ID(IntEnum): "LabelPoint 350", [int(SUPPORTED_DEVICE_ID.LABELPOINT_350)], # ToDo: Validate config! + # Printhead 64 Pixels, distributed over 9mm of active area 64, + 9, [6, 9, 12], - {6: (44, 85), 9: (31, 94), 12: (38, 117)}, ) ) SUPPORTED_PRODUCTS.append( DeviceConfig( "DYMO LabelMANAGER PC II", [int(SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II)], + # Printhead 128 Pixels, distributed over 18mm of active area 128, + 18, [6, 9, 12, 19, 24], - {6: (44, 85), 9: (31, 94), 12: (38, 117), 19: (2, 127), 24: (2, 127)}, ) ) SUPPORTED_PRODUCTS.append( @@ -94,9 +97,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_MODE_SWITCH), ], # ToDo: Validate config! + # Printhead 64 Pixels, distributed over 9mm of active area 64, + 9, [6, 9, 12], - {6: (44, 85), 9: (31, 94), 12: (38, 117)}, ) ) SUPPORTED_PRODUCTS.append( @@ -107,9 +111,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): int(SUPPORTED_DEVICE_ID.LABELMANAGER_420P_MODE_SWITCH), ], # ToDo: Validate config! + # Printhead 64 Pixels, distributed over 9mm of active area 64, + 9, [6, 9, 12], - {6: (44, 85), 9: (31, 94), 12: (38, 117)}, ) ) SUPPORTED_PRODUCTS.append( @@ -120,9 +125,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): int(SUPPORTED_DEVICE_ID.LABELMANAGER_280P_NO_MODE_SWITCH), ], # ToDo: Validate config! + # Printhead 64 Pixels, distributed over 9mm of active area 64, + 9, [6, 9, 12], - {6: (44, 85), 9: (31, 94), 12: (38, 117)}, ) ) SUPPORTED_PRODUCTS.append( @@ -133,9 +139,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): int(SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_MODE_SWITCH), ], # ToDo: Validate config! + # Printhead 64 Pixels, distributed over 9mm of active area 64, + 9, [6, 9, 12], - {6: (44, 85), 9: (31, 94), 12: (38, 117)}, ) ) SUPPORTED_PRODUCTS.append( @@ -143,9 +150,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): f"MobileLabeler {UNCONFIRMED_MESSAGE}", [int(SUPPORTED_DEVICE_ID.MOBILE_LABELER)], # ToDo: Validate config! + # Printhead 64 Pixels, distributed over 9mm of active area 64, + 9, [6, 9, 12], - {6: (44, 85), 9: (31, 94), 12: (38, 117)}, ) ) @@ -153,9 +161,10 @@ class SUPPORTED_DEVICE_ID(IntEnum): SIMULATOR_CONFIG = DeviceConfig( "Simulator", [0], + # Fake printhead 128 Pixels, distributed over 18mm of active area 128, + 18, [6, 9, 12, 19, 24], - {6: (44, 85), 9: (31, 94), 12: (38, 117), 19: (2, 127), 24: (2, 127)}, ) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index 9a9cd5be..dd356ef6 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -1,7 +1,7 @@ from __future__ import annotations import logging -from typing import Tuple +import math LOG = logging.getLogger(__name__) @@ -18,30 +18,35 @@ class DeviceConfig: printHeadSizePixels: int """Size of the print head in pixels (use calibration routine to determine)""" + printHeadActiveAreaMillimeters: float + """Size of the active area of the print head in millimters + (use calibration routine to determine)""" + supportedTapeSizes: list[int] """List of supported tape sizes in mm""" - marginsPerTape: dict[int, Tuple[int, int]] - """ - Dictonary of print margins per tape size - Entry: TapeSizeMM : (topMarginInPx, bottomMarginInPx) - Use the calibration routine to determine these for each tape size - """ + # Fixed to 1 mm until proven otherwise ;) + TAPE_ALIGNMENT_INACCURACY_MM: float = 1 + """The inaccuracy of the tape position relative to the printhead""" + # Inaccuracy of the tape position is mostly caused by + # the tape moving slightly from side to side in the cartridge. + # Improper cartrigde placemement is also an factor, + # but negligible due to a physical spring in the lid. def __init__( self, name: str, deviceIDs: list[int], printHeadSizePixels: int, + printHeadActiveAreaMillimeters: int, supportedTapeSizes: list[int], - marginsPerTape: dict[int, Tuple[int, int]], ): """Initialize a Labeler config object.""" self.name = name self.deviceIDs = deviceIDs self.printHeadSizePixels = printHeadSizePixels + self.printHeadActiveAreaMillimeters = printHeadActiveAreaMillimeters self.supportedTapeSizes = supportedTapeSizes - self.marginsPerTape = marginsPerTape def matches_device_id(self, idValue: int) -> bool: """Check if the a device ID matches this config.""" @@ -50,19 +55,57 @@ def matches_device_id(self, idValue: int) -> bool: else: return False - def get_tape_print_margins(self, tapeSizeMM: int) -> tuple[int, int]: + def get_tape_print_size_and_margins_px( + self, tapeSizeMM: int + ) -> tuple[int, int, int]: """Get print margins for a specific tape size. :param tapeSizeMM: Tape size in mm - :return: Margins tuple, None if not supported + :return: Margins tuple in pixels [ActivePixels,TopMarginPx,BottomMarginPx] """ if tapeSizeMM in self.supportedTapeSizes: - if tapeSizeMM in self.marginsPerTape: - # Return specified margins - return self.marginsPerTape[tapeSizeMM] + # Calculate the pixels per mm for this printer + # Example: printhead of 128 Pixels, distributed over 18 mm of active area. + # Makes 7.11 pixels/mm + printPPmm: float = ( + self.printHeadSizePixels / self.printHeadActiveAreaMillimeters + ) + + # Calculate usable tape width (*2 for top and bottom) + usableTapeWidth: float = tapeSizeMM - ( + self.TAPE_ALIGNMENT_INACCURACY_MM * 2 + ) + + # Calculate the numer of active pixels for the tape + activeTapePixels: float = 0 + if usableTapeWidth >= self.printHeadActiveAreaMillimeters: + # Tape is larger than active area of printhead. Use all pixels + activeTapePixels = self.printHeadSizePixels else: - # No specific margins set, return default - return (0, 0) + # Calculate the amount of active pixels we are able to use + # (taking the placement inaccuracy into account) + activeTapePixels = printPPmm * usableTapeWidth + + # Round down to nearest whole number as we can't use half a pixels ;) + activeTapePixels = math.floor(activeTapePixels) + + # To calculate the margins we need to know some hardware info + # Printer has special "support studs" that + # let 19 & 24mm tapes go to the bottom (if supported) + # but 12mm based casettes are raised so they are centered to the printhead. + # Smaller tapes than 12mm are in 12mm casettes are centered + # in the cartridge and in turn also centered to the printhead + # Which gives us the advantage that we can + # just calculate the top and bottom margin + + # Calculate the top margin + topMargin = round(((self.printHeadSizePixels - activeTapePixels) / 2), 0) + + # Bottom margin is equal due to centering of the tape + bottomMargin = topMargin + + # Return active pixels / margins set + return (int(activeTapePixels), int(topMargin), int(bottomMargin)) else: # Tape size not supported raise ValueError( diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index ae8b6064..27587e22 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -242,7 +242,6 @@ class DymoLabeler: tape_size_mm: int LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 - LABELER_PRINT_HEAD_HEIGHT_MM = 8.2 DEFAULT_TAPE_SIZE_MM = 12 def __init__( @@ -274,7 +273,10 @@ def __init__( @property def height_px(self): - return DymoLabelerFunctions.height_px(self.tape_size_mm) + """Get the tape size in pixels.""" + return self._deviceConfig.get_tape_print_size_and_margins_px(self.tape_size_mm)[ + 0 + ] @property def _functions(self) -> DymoLabelerFunctions: @@ -291,13 +293,14 @@ def minimum_horizontal_margin_mm(self): @property def labeler_margin_px(self) -> tuple[float, float]: - vertical_margin_mm = max( - 0, (self.tape_size_mm - self.LABELER_PRINT_HEAD_HEIGHT_MM) / 2 - ) + # Get vertical (top) margin + vertical_margin_px = self._deviceConfig.get_tape_print_size_and_margins_px( + self.tape_size_mm + )[1] return ( mm_to_px(self.minimum_horizontal_margin_mm), - mm_to_px(vertical_margin_mm), + vertical_margin_px, ) @property From 040f2c20aea67fb788e245e9b0d20b64775b1e21 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:43:05 +0000 Subject: [PATCH 08/34] Force sending an "exact printhead width" bitmap to the printer --- src/labelle/lib/devices/device_config.py | 4 ++++ src/labelle/lib/render_engines/margins.py | 19 ++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index dd356ef6..8235d6ac 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -104,6 +104,10 @@ def get_tape_print_size_and_margins_px( # Bottom margin is equal due to centering of the tape bottomMargin = topMargin + # Make sure the total is the exact amount of pixels of the printhead + # Aka compensate for margin rounding/division errors + activeTapePixels = self.printHeadSizePixels - (topMargin + bottomMargin) + # Return active pixels / margins set return (int(activeTapePixels), int(topMargin), int(bottomMargin)) else: diff --git a/src/labelle/lib/render_engines/margins.py b/src/labelle/lib/render_engines/margins.py index 15cfe01e..a634920d 100644 --- a/src/labelle/lib/render_engines/margins.py +++ b/src/labelle/lib/render_engines/margins.py @@ -105,25 +105,22 @@ def render_with_meta( # There's also some vertical margin between printed area and the label # edge - vertical_offset_px: float = 0 if self.mode == "print": # print head is already in offset from label's edge under the cutter horizontal_offset_px -= self.labeler_horizontal_margin_px - # no need to add vertical margins to bitmap - bitmap_height = float(payload_bitmap.height) - elif self.mode == "preview": - # add vertical margins to bitmap - bitmap_height = ( - float(payload_bitmap.height) + self.labeler_vertical_margin_px * 2.0 - ) - vertical_offset_px = self.labeler_vertical_margin_px + + # add vertical margins to bitmap + bitmap_height = ( + float(payload_bitmap.height) + self.labeler_vertical_margin_px * 2.0 + ) bitmap = Image.new("1", (math.ceil(label_width_px), math.ceil(bitmap_height))) bitmap.paste( - payload_bitmap, box=(round(horizontal_offset_px), round(vertical_offset_px)) + payload_bitmap, + box=(round(horizontal_offset_px), round(self.labeler_vertical_margin_px)), ) meta = { "horizontal_offset_px": horizontal_offset_px, - "vertical_offset_px": vertical_offset_px, + "vertical_offset_px": self.labeler_vertical_margin_px, } return bitmap, meta From f907b8ba51126129757bc4df75b599c471b64e44 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:12:40 +0000 Subject: [PATCH 09/34] Fix config not updated when device set --- src/labelle/lib/devices/dymo_labeler.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 27587e22..83f30e7a 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -249,18 +249,7 @@ def __init__( tape_size_mm: int | None = None, device: UsbDevice | None = None, ): - self._device = device - - if self._device is not None: - # Retrieve device config - foundConfig = get_device_config_by_id(self._device.id_product) - if foundConfig is not None: - self._deviceConfig = foundConfig - else: - raise ValueError(f"Unsupported device type {self._device.id_product}") - else: - # Use simulator config - self._deviceConfig = SIMULATOR_CONFIG + self.device = device if tape_size_mm is None: tape_size_mm = self.DEFAULT_TAPE_SIZE_MM @@ -312,6 +301,17 @@ def device(self, device: UsbDevice | None): try: if device: device.setup() + + # Retrieve device config + foundConfig = get_device_config_by_id(device.id_product) + if foundConfig is not None: + self._deviceConfig = foundConfig + else: + raise ValueError(f"Unsupported device type {device.id_product}") + else: + # Use simulator config + self._deviceConfig = SIMULATOR_CONFIG + except UsbDeviceError as e: device = None LOG.error(e) From d64c97753e04761f09b11b66bde5f5f626a86473 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:21:19 +0000 Subject: [PATCH 10/34] Tested on LabelManager PnP --- src/labelle/lib/constants.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index d916ebe0..2a39984c 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -96,7 +96,6 @@ class SUPPORTED_DEVICE_ID(IntEnum): int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_NO_MODE_SWITCH), int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_MODE_SWITCH), ], - # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area 64, 9, From a7def5ebb0b446f0eadecf637cc8438dd3a89376 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:02:37 +0000 Subject: [PATCH 11/34] replace all camels with snakes --- src/labelle/gui/gui.py | 2 +- src/labelle/lib/devices/device_config.py | 69 ++++++++++++----------- src/labelle/lib/devices/device_manager.py | 6 +- src/labelle/lib/devices/dymo_labeler.py | 22 ++++---- 4 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index d0f42ef2..c5cf0884 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -63,7 +63,7 @@ def _init_elements(self) -> None: self._device_manager = DeviceManager() self._dymo_labeler = DymoLabeler() self._settings_toolbar.update_labeler_context( - supported_tape_sizes=self._dymo_labeler._deviceConfig.supportedTapeSizes, + supported_tape_sizes=self._dymo_labeler._device_config.supported_tape_sizes, installed_tape_size=self._dymo_labeler.tape_size_mm, minimum_horizontal_margin_mm=self._dymo_labeler.minimum_horizontal_margin_mm, ) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index 8235d6ac..ae5a9187 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -12,17 +12,17 @@ class DeviceConfig: name: str """Name of this device""" - deviceIDs: list[int] + device_ids: list[int] """List of USB Device ID's this device can identify as""" - printHeadSizePixels: int + print_head_size_px: int """Size of the print head in pixels (use calibration routine to determine)""" - printHeadActiveAreaMillimeters: float + print_head_active_area_mm: float """Size of the active area of the print head in millimters (use calibration routine to determine)""" - supportedTapeSizes: list[int] + supported_tape_sizes: list[int] """List of supported tape sizes in mm""" # Fixed to 1 mm until proven otherwise ;) @@ -36,58 +36,59 @@ class DeviceConfig: def __init__( self, name: str, - deviceIDs: list[int], - printHeadSizePixels: int, - printHeadActiveAreaMillimeters: int, - supportedTapeSizes: list[int], + device_ids: list[int], + print_head_size_pixels: int, + print_head_active_area_mm: int, + supported_tape_sizes: list[int], ): """Initialize a Labeler config object.""" self.name = name - self.deviceIDs = deviceIDs - self.printHeadSizePixels = printHeadSizePixels - self.printHeadActiveAreaMillimeters = printHeadActiveAreaMillimeters - self.supportedTapeSizes = supportedTapeSizes + self.device_ids = device_ids + self.print_head_size_px = print_head_size_pixels + self.print_head_active_area_mm = print_head_active_area_mm + self.supported_tape_sizes = supported_tape_sizes - def matches_device_id(self, idValue: int) -> bool: + def matches_device_id(self, device_id: int) -> bool: """Check if the a device ID matches this config.""" - if idValue in self.deviceIDs: + if device_id in self.device_ids: return True else: return False def get_tape_print_size_and_margins_px( - self, tapeSizeMM: int + self, tape_size_mm: int ) -> tuple[int, int, int]: """Get print margins for a specific tape size. - :param tapeSizeMM: Tape size in mm - :return: Margins tuple in pixels [ActivePixels,TopMarginPx,BottomMarginPx] + :param tape_size_mm: Tape size in mm + :return: Margins tuple in pixels + (Active pixels on tape, Top margin pixels, bottom margin pixels) """ - if tapeSizeMM in self.supportedTapeSizes: + if tape_size_mm in self.supported_tape_sizes: # Calculate the pixels per mm for this printer # Example: printhead of 128 Pixels, distributed over 18 mm of active area. # Makes 7.11 pixels/mm - printPPmm: float = ( - self.printHeadSizePixels / self.printHeadActiveAreaMillimeters + print_pixels_per_mm: float = ( + self.print_head_size_px / self.print_head_active_area_mm ) # Calculate usable tape width (*2 for top and bottom) - usableTapeWidth: float = tapeSizeMM - ( + usable_tape_width_mm: float = tape_size_mm - ( self.TAPE_ALIGNMENT_INACCURACY_MM * 2 ) # Calculate the numer of active pixels for the tape - activeTapePixels: float = 0 - if usableTapeWidth >= self.printHeadActiveAreaMillimeters: + usable_tape_width_pixels: float = 0 + if usable_tape_width_mm >= self.print_head_active_area_mm: # Tape is larger than active area of printhead. Use all pixels - activeTapePixels = self.printHeadSizePixels + usable_tape_width_pixels = self.print_head_size_px else: # Calculate the amount of active pixels we are able to use # (taking the placement inaccuracy into account) - activeTapePixels = printPPmm * usableTapeWidth + usable_tape_width_pixels = print_pixels_per_mm * usable_tape_width_mm # Round down to nearest whole number as we can't use half a pixels ;) - activeTapePixels = math.floor(activeTapePixels) + usable_tape_width_pixels = math.floor(usable_tape_width_pixels) # To calculate the margins we need to know some hardware info # Printer has special "support studs" that @@ -99,20 +100,24 @@ def get_tape_print_size_and_margins_px( # just calculate the top and bottom margin # Calculate the top margin - topMargin = round(((self.printHeadSizePixels - activeTapePixels) / 2), 0) + margin_top = round( + ((self.print_head_size_px - usable_tape_width_pixels) / 2), 0 + ) # Bottom margin is equal due to centering of the tape - bottomMargin = topMargin + margin_bottom = margin_top # Make sure the total is the exact amount of pixels of the printhead # Aka compensate for margin rounding/division errors - activeTapePixels = self.printHeadSizePixels - (topMargin + bottomMargin) + usable_tape_width_pixels = self.print_head_size_px - ( + margin_top + margin_bottom + ) # Return active pixels / margins set - return (int(activeTapePixels), int(topMargin), int(bottomMargin)) + return (int(usable_tape_width_pixels), int(margin_top), int(margin_bottom)) else: # Tape size not supported raise ValueError( - f"Unsupported tape size {tapeSizeMM}mm. " - f"Supported sizes: {self.supportedTapeSizes}mm" + f"Unsupported tape size {tape_size_mm}mm. " + f"Supported sizes: {self.supported_tape_sizes}mm" ) diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index a96e2c6b..1127768c 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -81,11 +81,11 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice LOG.debug(dev.device_info) dev = devices[0] if dev.is_supported: - foundDeviceConfig: DeviceConfig | None = get_device_config_by_id( + found_device_config: DeviceConfig | None = get_device_config_by_id( dev.id_product ) - if foundDeviceConfig is not None: - msg = f"Recognized device as {foundDeviceConfig.name}" + if found_device_config is not None: + msg = f"Recognized device as {found_device_config.name}" else: msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}" LOG.debug(msg) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 83f30e7a..31129e16 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -238,7 +238,7 @@ def _raw_print_label(self, lines: list[list[int]]): class DymoLabeler: _device: UsbDevice | None - _deviceConfig: DeviceConfig + _device_config: DeviceConfig tape_size_mm: int LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 @@ -253,19 +253,19 @@ def __init__( if tape_size_mm is None: tape_size_mm = self.DEFAULT_TAPE_SIZE_MM - if tape_size_mm not in self._deviceConfig.supportedTapeSizes: + if tape_size_mm not in self._device_config.supported_tape_sizes: raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self._deviceConfig.supportedTapeSizes}mm" + f"Supported sizes: {self._device_config.supported_tape_sizes}mm" ) self.tape_size_mm = tape_size_mm @property def height_px(self): """Get the tape size in pixels.""" - return self._deviceConfig.get_tape_print_size_and_margins_px(self.tape_size_mm)[ - 0 - ] + return self._device_config.get_tape_print_size_and_margins_px( + self.tape_size_mm + )[0] @property def _functions(self) -> DymoLabelerFunctions: @@ -283,7 +283,7 @@ def minimum_horizontal_margin_mm(self): @property def labeler_margin_px(self) -> tuple[float, float]: # Get vertical (top) margin - vertical_margin_px = self._deviceConfig.get_tape_print_size_and_margins_px( + vertical_margin_px = self._device_config.get_tape_print_size_and_margins_px( self.tape_size_mm )[1] @@ -303,14 +303,14 @@ def device(self, device: UsbDevice | None): device.setup() # Retrieve device config - foundConfig = get_device_config_by_id(device.id_product) - if foundConfig is not None: - self._deviceConfig = foundConfig + found_config = get_device_config_by_id(device.id_product) + if found_config is not None: + self._device_config = found_config else: raise ValueError(f"Unsupported device type {device.id_product}") else: # Use simulator config - self._deviceConfig = SIMULATOR_CONFIG + self._device_config = SIMULATOR_CONFIG except UsbDeviceError as e: device = None From d8dcb3db93a60c0fe91b039ee13bb1ba3ce6159b Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 09:43:33 +0000 Subject: [PATCH 12/34] Cleanup supported device list syntax --- src/labelle/gui/gui.py | 2 +- src/labelle/lib/constants.py | 158 ++++++++--------------- src/labelle/lib/devices/device_config.py | 32 ++--- src/labelle/lib/devices/dymo_labeler.py | 4 +- 4 files changed, 75 insertions(+), 121 deletions(-) diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index c5cf0884..f773d04f 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -63,7 +63,7 @@ def _init_elements(self) -> None: self._device_manager = DeviceManager() self._dymo_labeler = DymoLabeler() self._settings_toolbar.update_labeler_context( - supported_tape_sizes=self._dymo_labeler._device_config.supported_tape_sizes, + supported_tape_sizes=self._dymo_labeler._device_config.supported_tape_sizes_mm, installed_tape_size=self._dymo_labeler.tape_size_mm, minimum_horizontal_margin_mm=self._dymo_labeler.minimum_horizontal_margin_mm, ) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index 2a39984c..f06ce12e 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -13,7 +13,7 @@ # either sysfs is unavailable or unusable by this script for some reason. # Please beware that DEV_NODE must be set to None when not used, else you will # be bitten by the NameError exception. -from enum import Enum, IntEnum +from enum import Enum from pathlib import Path import labelle.resources.fonts @@ -36,134 +36,88 @@ "report your experiences in https://github.com/labelle-org/labelle/issues/4" ) - -# Supported USB device ID enumeration -class SUPPORTED_DEVICE_ID(IntEnum): - LABELMANAGER_PC = 0x0011 - LABELPOINT_350 = 0x0015 - LABELMANAGER_PC_II = 0x001C - LABELMANAGER_PNP_NO_MODE_SWITCH = 0x1001 - LABELMANAGER_PNP_MODE_SWITCH = 0x1002 - LABELMANAGER_420P_NO_MODE_SWITCH = 0x1003 - LABELMANAGER_420P_MODE_SWITCH = 0x1004 - LABELMANAGER_280P_NO_MODE_SWITCH = 0x1005 - LABELMANAGER_280P_MODE_SWITCH = 0x1006 - LABELMANAGER_WIRELESS_PNP_NO_MODE_SWITCH = 0x1007 - LABELMANAGER_WIRELESS_PNP_MODE_SWITCH = 0x1008 - MOBILE_LABELER = 0x1009 - - -# Create supported products list -SUPPORTED_PRODUCTS = [] - # ---- Supported USB Devices configuration ---- -SUPPORTED_PRODUCTS.append( +SUPPORTED_PRODUCTS = [ DeviceConfig( - "DYMO LabelMANAGER PC", - [int(SUPPORTED_DEVICE_ID.LABELMANAGER_PC)], + name="DYMO LabelMANAGER PC", + device_ids=[0x0011], # ToDo: Validate config! # Printhead 128 Pixels, distributed over 18mm of active area - 128, - 18, - [6, 9, 12, 19], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=128, + print_head_active_area_width_mm=18, + supported_tape_sizes_mm=[6, 9, 12, 19], + ), DeviceConfig( - "LabelPoint 350", - [int(SUPPORTED_DEVICE_ID.LABELPOINT_350)], + name="LabelPoint 350", + device_ids=[0x0015], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - 64, - 9, - [6, 9, 12], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=64, + print_head_active_area_width_mm=9, + supported_tape_sizes_mm=[6, 9, 12], + ), DeviceConfig( - "DYMO LabelMANAGER PC II", - [int(SUPPORTED_DEVICE_ID.LABELMANAGER_PC_II)], + name="DYMO LabelMANAGER PC II", + device_ids=[0x001C], # Printhead 128 Pixels, distributed over 18mm of active area - 128, - 18, - [6, 9, 12, 19, 24], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=128, + print_head_active_area_width_mm=18, + supported_tape_sizes_mm=[6, 9, 12, 19, 24], + ), DeviceConfig( - "LabelManager PnP", - [ - int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_NO_MODE_SWITCH), - int(SUPPORTED_DEVICE_ID.LABELMANAGER_PNP_MODE_SWITCH), - ], + name="LabelManager PnP", + device_ids=[0x1001, 0x1002], # Printhead 64 Pixels, distributed over 9mm of active area - 64, - 9, - [6, 9, 12], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=64, + print_head_active_area_width_mm=9, + supported_tape_sizes_mm=[6, 9, 12], + ), DeviceConfig( - f"LabelManager 420P {UNCONFIRMED_MESSAGE}", - [ - int(SUPPORTED_DEVICE_ID.LABELMANAGER_420P_NO_MODE_SWITCH), - int(SUPPORTED_DEVICE_ID.LABELMANAGER_420P_MODE_SWITCH), - ], + name=f"LabelManager 420P {UNCONFIRMED_MESSAGE}", + device_ids=[0x1003, 0x1004], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - 64, - 9, - [6, 9, 12], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=64, + print_head_active_area_width_mm=9, + supported_tape_sizes_mm=[6, 9, 12], + ), DeviceConfig( - "LabelManager 280", - [ - int(SUPPORTED_DEVICE_ID.LABELMANAGER_280P_MODE_SWITCH), - int(SUPPORTED_DEVICE_ID.LABELMANAGER_280P_NO_MODE_SWITCH), - ], + name="LabelManager 280", + device_ids=[0x1006, 0x1005], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - 64, - 9, - [6, 9, 12], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=64, + print_head_active_area_width_mm=9, + supported_tape_sizes_mm=[6, 9, 12], + ), DeviceConfig( - f"LabelManager Wireless PnP {UNCONFIRMED_MESSAGE}", - [ - int(SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_NO_MODE_SWITCH), - int(SUPPORTED_DEVICE_ID.LABELMANAGER_WIRELESS_PNP_MODE_SWITCH), - ], + name=f"LabelManager Wireless PnP {UNCONFIRMED_MESSAGE}", + device_ids=[0x1007, 0x1008], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - 64, - 9, - [6, 9, 12], - ) -) -SUPPORTED_PRODUCTS.append( + print_head_width_px=64, + print_head_active_area_width_mm=9, + supported_tape_sizes_mm=[6, 9, 12], + ), DeviceConfig( - f"MobileLabeler {UNCONFIRMED_MESSAGE}", - [int(SUPPORTED_DEVICE_ID.MOBILE_LABELER)], + name=f"MobileLabeler {UNCONFIRMED_MESSAGE}", + device_ids=[0x1009], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - 64, - 9, - [6, 9, 12], - ) -) + print_head_width_px=64, + print_head_active_area_width_mm=9, + supported_tape_sizes_mm=[6, 9, 12], + ), +] # Simulator configuration SIMULATOR_CONFIG = DeviceConfig( - "Simulator", - [0], + name="Simulator", + device_ids=[0], # Fake printhead 128 Pixels, distributed over 18mm of active area - 128, - 18, - [6, 9, 12, 19, 24], + print_head_width_px=128, + print_head_active_area_width_mm=18, + supported_tape_sizes_mm=[6, 9, 12, 19, 24], ) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index ae5a9187..89034a75 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -15,14 +15,14 @@ class DeviceConfig: device_ids: list[int] """List of USB Device ID's this device can identify as""" - print_head_size_px: int + print_head_width_px: int """Size of the print head in pixels (use calibration routine to determine)""" - print_head_active_area_mm: float + print_head_active_area_width_mm: float """Size of the active area of the print head in millimters (use calibration routine to determine)""" - supported_tape_sizes: list[int] + supported_tape_sizes_mm: list[int] """List of supported tape sizes in mm""" # Fixed to 1 mm until proven otherwise ;) @@ -37,16 +37,16 @@ def __init__( self, name: str, device_ids: list[int], - print_head_size_pixels: int, - print_head_active_area_mm: int, - supported_tape_sizes: list[int], + print_head_width_px: int, + print_head_active_area_width_mm: int, + supported_tape_sizes_mm: list[int], ): """Initialize a Labeler config object.""" self.name = name self.device_ids = device_ids - self.print_head_size_px = print_head_size_pixels - self.print_head_active_area_mm = print_head_active_area_mm - self.supported_tape_sizes = supported_tape_sizes + self.print_head_width_px = print_head_width_px + self.print_head_active_area_width_mm = print_head_active_area_width_mm + self.supported_tape_sizes_mm = supported_tape_sizes_mm def matches_device_id(self, device_id: int) -> bool: """Check if the a device ID matches this config.""" @@ -64,12 +64,12 @@ def get_tape_print_size_and_margins_px( :return: Margins tuple in pixels (Active pixels on tape, Top margin pixels, bottom margin pixels) """ - if tape_size_mm in self.supported_tape_sizes: + if tape_size_mm in self.supported_tape_sizes_mm: # Calculate the pixels per mm for this printer # Example: printhead of 128 Pixels, distributed over 18 mm of active area. # Makes 7.11 pixels/mm print_pixels_per_mm: float = ( - self.print_head_size_px / self.print_head_active_area_mm + self.print_head_width_px / self.print_head_active_area_width_mm ) # Calculate usable tape width (*2 for top and bottom) @@ -79,9 +79,9 @@ def get_tape_print_size_and_margins_px( # Calculate the numer of active pixels for the tape usable_tape_width_pixels: float = 0 - if usable_tape_width_mm >= self.print_head_active_area_mm: + if usable_tape_width_mm >= self.print_head_active_area_width_mm: # Tape is larger than active area of printhead. Use all pixels - usable_tape_width_pixels = self.print_head_size_px + usable_tape_width_pixels = self.print_head_width_px else: # Calculate the amount of active pixels we are able to use # (taking the placement inaccuracy into account) @@ -101,7 +101,7 @@ def get_tape_print_size_and_margins_px( # Calculate the top margin margin_top = round( - ((self.print_head_size_px - usable_tape_width_pixels) / 2), 0 + ((self.print_head_width_px - usable_tape_width_pixels) / 2), 0 ) # Bottom margin is equal due to centering of the tape @@ -109,7 +109,7 @@ def get_tape_print_size_and_margins_px( # Make sure the total is the exact amount of pixels of the printhead # Aka compensate for margin rounding/division errors - usable_tape_width_pixels = self.print_head_size_px - ( + usable_tape_width_pixels = self.print_head_width_px - ( margin_top + margin_bottom ) @@ -119,5 +119,5 @@ def get_tape_print_size_and_margins_px( # Tape size not supported raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self.supported_tape_sizes}mm" + f"Supported sizes: {self.supported_tape_sizes_mm}mm" ) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 31129e16..5b023ff0 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -253,10 +253,10 @@ def __init__( if tape_size_mm is None: tape_size_mm = self.DEFAULT_TAPE_SIZE_MM - if tape_size_mm not in self._device_config.supported_tape_sizes: + if tape_size_mm not in self._device_config.supported_tape_sizes_mm: raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self._device_config.supported_tape_sizes}mm" + f"Supported sizes: {self._device_config.supported_tape_sizes_mm}mm" ) self.tape_size_mm = tape_size_mm From 4fc8b688a6f29c37e0eff13905193a79a1e90cfe Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 09:50:28 +0000 Subject: [PATCH 13/34] Convert device config to dataclass --- src/labelle/lib/devices/device_config.py | 33 +++++++----------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index 89034a75..f1489d44 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -2,10 +2,12 @@ import logging import math +from dataclasses import dataclass LOG = logging.getLogger(__name__) +@dataclass class DeviceConfig: """Configuration object for the capabilities of a label printer.""" @@ -25,28 +27,13 @@ class DeviceConfig: supported_tape_sizes_mm: list[int] """List of supported tape sizes in mm""" - # Fixed to 1 mm until proven otherwise ;) - TAPE_ALIGNMENT_INACCURACY_MM: float = 1 - """The inaccuracy of the tape position relative to the printhead""" - # Inaccuracy of the tape position is mostly caused by - # the tape moving slightly from side to side in the cartridge. - # Improper cartrigde placemement is also an factor, - # but negligible due to a physical spring in the lid. - - def __init__( - self, - name: str, - device_ids: list[int], - print_head_width_px: int, - print_head_active_area_width_mm: int, - supported_tape_sizes_mm: list[int], - ): - """Initialize a Labeler config object.""" - self.name = name - self.device_ids = device_ids - self.print_head_width_px = print_head_width_px - self.print_head_active_area_width_mm = print_head_active_area_width_mm - self.supported_tape_sizes_mm = supported_tape_sizes_mm + tape_alignment_inaccuracy_mm: float = 1.0 + """The inaccuracy of the tape position relative to the printhead + Inaccuracy of the tape position is mostly caused by + the tape moving slightly from side to side in the cartridge. + Improper cartrigde placemement is also an factor, + but negligible due to a physical spring in the lid. + """ def matches_device_id(self, device_id: int) -> bool: """Check if the a device ID matches this config.""" @@ -74,7 +61,7 @@ def get_tape_print_size_and_margins_px( # Calculate usable tape width (*2 for top and bottom) usable_tape_width_mm: float = tape_size_mm - ( - self.TAPE_ALIGNMENT_INACCURACY_MM * 2 + self.tape_alignment_inaccuracy_mm * 2 ) # Calculate the numer of active pixels for the tape From 1d36049015f7333b101c17565ee1dd5dc4fea950 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 09:59:22 +0000 Subject: [PATCH 14/34] Convert labeler device-config to property --- src/labelle/gui/gui.py | 2 +- src/labelle/lib/devices/dymo_labeler.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index f773d04f..3fedd927 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -63,7 +63,7 @@ def _init_elements(self) -> None: self._device_manager = DeviceManager() self._dymo_labeler = DymoLabeler() self._settings_toolbar.update_labeler_context( - supported_tape_sizes=self._dymo_labeler._device_config.supported_tape_sizes_mm, + supported_tape_sizes=self._dymo_labeler.device_config.supported_tape_sizes_mm, installed_tape_size=self._dymo_labeler.tape_size_mm, minimum_horizontal_margin_mm=self._dymo_labeler.minimum_horizontal_margin_mm, ) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 5b023ff0..4d680795 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -292,6 +292,10 @@ def labeler_margin_px(self) -> tuple[float, float]: vertical_margin_px, ) + @property + def device_config(self) -> DeviceConfig: + return self._device_config + @property def device(self) -> UsbDevice | None: return self._device From d8b3b6dba9bd1062a6c87932d35791501b1c7aa8 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 11:20:02 +0000 Subject: [PATCH 15/34] Move get_tape_print_size_and_margins_px to labeler class tape_print_properties Used named tuple Renamed width/height to be consistent --- src/labelle/lib/constants.py | 36 +++++----- src/labelle/lib/devices/device_config.py | 72 +------------------- src/labelle/lib/devices/dymo_labeler.py | 86 +++++++++++++++++++++--- 3 files changed, 96 insertions(+), 98 deletions(-) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index f06ce12e..881d6585 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -43,8 +43,8 @@ device_ids=[0x0011], # ToDo: Validate config! # Printhead 128 Pixels, distributed over 18mm of active area - print_head_width_px=128, - print_head_active_area_width_mm=18, + print_head_px=128, + print_head_mm=18, supported_tape_sizes_mm=[6, 9, 12, 19], ), DeviceConfig( @@ -52,24 +52,24 @@ device_ids=[0x0015], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - print_head_width_px=64, - print_head_active_area_width_mm=9, + print_head_px=64, + print_head_mm=9, supported_tape_sizes_mm=[6, 9, 12], ), DeviceConfig( name="DYMO LabelMANAGER PC II", device_ids=[0x001C], # Printhead 128 Pixels, distributed over 18mm of active area - print_head_width_px=128, - print_head_active_area_width_mm=18, + print_head_px=128, + print_head_mm=18, supported_tape_sizes_mm=[6, 9, 12, 19, 24], ), DeviceConfig( name="LabelManager PnP", device_ids=[0x1001, 0x1002], # Printhead 64 Pixels, distributed over 9mm of active area - print_head_width_px=64, - print_head_active_area_width_mm=9, + print_head_px=64, + print_head_mm=9, supported_tape_sizes_mm=[6, 9, 12], ), DeviceConfig( @@ -77,8 +77,8 @@ device_ids=[0x1003, 0x1004], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - print_head_width_px=64, - print_head_active_area_width_mm=9, + print_head_px=64, + print_head_mm=9, supported_tape_sizes_mm=[6, 9, 12], ), DeviceConfig( @@ -86,8 +86,8 @@ device_ids=[0x1006, 0x1005], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - print_head_width_px=64, - print_head_active_area_width_mm=9, + print_head_px=64, + print_head_mm=9, supported_tape_sizes_mm=[6, 9, 12], ), DeviceConfig( @@ -95,8 +95,8 @@ device_ids=[0x1007, 0x1008], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - print_head_width_px=64, - print_head_active_area_width_mm=9, + print_head_px=64, + print_head_mm=9, supported_tape_sizes_mm=[6, 9, 12], ), DeviceConfig( @@ -104,8 +104,8 @@ device_ids=[0x1009], # ToDo: Validate config! # Printhead 64 Pixels, distributed over 9mm of active area - print_head_width_px=64, - print_head_active_area_width_mm=9, + print_head_px=64, + print_head_mm=9, supported_tape_sizes_mm=[6, 9, 12], ), ] @@ -115,8 +115,8 @@ name="Simulator", device_ids=[0], # Fake printhead 128 Pixels, distributed over 18mm of active area - print_head_width_px=128, - print_head_active_area_width_mm=18, + print_head_px=128, + print_head_mm=18, supported_tape_sizes_mm=[6, 9, 12, 19, 24], ) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index f1489d44..e4894806 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -import math from dataclasses import dataclass LOG = logging.getLogger(__name__) @@ -17,10 +16,10 @@ class DeviceConfig: device_ids: list[int] """List of USB Device ID's this device can identify as""" - print_head_width_px: int + print_head_px: int """Size of the print head in pixels (use calibration routine to determine)""" - print_head_active_area_width_mm: float + print_head_mm: float """Size of the active area of the print head in millimters (use calibration routine to determine)""" @@ -41,70 +40,3 @@ def matches_device_id(self, device_id: int) -> bool: return True else: return False - - def get_tape_print_size_and_margins_px( - self, tape_size_mm: int - ) -> tuple[int, int, int]: - """Get print margins for a specific tape size. - - :param tape_size_mm: Tape size in mm - :return: Margins tuple in pixels - (Active pixels on tape, Top margin pixels, bottom margin pixels) - """ - if tape_size_mm in self.supported_tape_sizes_mm: - # Calculate the pixels per mm for this printer - # Example: printhead of 128 Pixels, distributed over 18 mm of active area. - # Makes 7.11 pixels/mm - print_pixels_per_mm: float = ( - self.print_head_width_px / self.print_head_active_area_width_mm - ) - - # Calculate usable tape width (*2 for top and bottom) - usable_tape_width_mm: float = tape_size_mm - ( - self.tape_alignment_inaccuracy_mm * 2 - ) - - # Calculate the numer of active pixels for the tape - usable_tape_width_pixels: float = 0 - if usable_tape_width_mm >= self.print_head_active_area_width_mm: - # Tape is larger than active area of printhead. Use all pixels - usable_tape_width_pixels = self.print_head_width_px - else: - # Calculate the amount of active pixels we are able to use - # (taking the placement inaccuracy into account) - usable_tape_width_pixels = print_pixels_per_mm * usable_tape_width_mm - - # Round down to nearest whole number as we can't use half a pixels ;) - usable_tape_width_pixels = math.floor(usable_tape_width_pixels) - - # To calculate the margins we need to know some hardware info - # Printer has special "support studs" that - # let 19 & 24mm tapes go to the bottom (if supported) - # but 12mm based casettes are raised so they are centered to the printhead. - # Smaller tapes than 12mm are in 12mm casettes are centered - # in the cartridge and in turn also centered to the printhead - # Which gives us the advantage that we can - # just calculate the top and bottom margin - - # Calculate the top margin - margin_top = round( - ((self.print_head_width_px - usable_tape_width_pixels) / 2), 0 - ) - - # Bottom margin is equal due to centering of the tape - margin_bottom = margin_top - - # Make sure the total is the exact amount of pixels of the printhead - # Aka compensate for margin rounding/division errors - usable_tape_width_pixels = self.print_head_width_px - ( - margin_top + margin_bottom - ) - - # Return active pixels / margins set - return (int(usable_tape_width_pixels), int(margin_top), int(margin_bottom)) - else: - # Tape size not supported - raise ValueError( - f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self.supported_tape_sizes_mm}mm" - ) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 4d680795..6a8e0dae 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -10,6 +10,7 @@ import array import logging import math +from collections import namedtuple import usb from PIL import Image @@ -244,6 +245,14 @@ class DymoLabeler: LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 DEFAULT_TAPE_SIZE_MM = 12 + TapePrintProperties = namedtuple( + "TapePrintProperties", + ["usable_tape_height_px", "top_margin_px", "bottom_margin_px"], + ) + """Tape print properties tuple type. + Contains margins and size in pixels for printing. + """ + def __init__( self, tape_size_mm: int | None = None, @@ -262,10 +271,8 @@ def __init__( @property def height_px(self): - """Get the tape size in pixels.""" - return self._device_config.get_tape_print_size_and_margins_px( - self.tape_size_mm - )[0] + """Get the (usable) tape height in pixels.""" + return self.tape_print_properties.usable_tape_height_px @property def _functions(self) -> DymoLabelerFunctions: @@ -282,16 +289,75 @@ def minimum_horizontal_margin_mm(self): @property def labeler_margin_px(self) -> tuple[float, float]: - # Get vertical (top) margin - vertical_margin_px = self._device_config.get_tape_print_size_and_margins_px( - self.tape_size_mm - )[1] - return ( mm_to_px(self.minimum_horizontal_margin_mm), - vertical_margin_px, + self.tape_print_properties.top_margin_px, ) + @property + def tape_print_properties(self) -> TapePrintProperties: + if self.tape_size_mm in self.device_config.supported_tape_sizes_mm: + # Calculate the pixels per mm for this printer + # Example: printhead of 128 Pixels, distributed over 18 mm of active area. + # Makes 7.11 pixels/mm + print_pixels_per_mm: float = ( + self.device_config.print_head_px / self.device_config.print_head_mm + ) + + # Calculate usable tape height (*2 for top and bottom) + usable_tape_height_mm: float = self.tape_size_mm - ( + 2 * self.device_config.tape_alignment_inaccuracy_mm + ) + + # Calculate the numer of active pixels for the tape + usable_tape_height_pixels: float = 0 + if usable_tape_height_mm >= self.device_config.print_head_mm: + # Tape is larger than active area of printhead. Use all pixels + usable_tape_height_pixels = self.device_config.print_head_px + else: + # Calculate the amount of active pixels we are able to use + # (taking the placement inaccuracy into account) + usable_tape_height_pixels = print_pixels_per_mm * usable_tape_height_mm + + # Round down to nearest whole number as we can't use half a pixels ;) + usable_tape_height_pixels = math.floor(usable_tape_height_pixels) + + # To calculate the margins we need to know some hardware info + # Printer has special "support studs" that + # let 19 & 24mm tapes go to the bottom (if supported) + # but 12mm based casettes are raised so they are centered to the printhead. + # Smaller tapes than 12mm are in 12mm casettes are centered + # in the cartridge and in turn also centered to the printhead + # Which gives us the advantage that we can + # just calculate the top and bottom margin + + # Calculate the top margin + margin_top = round( + ((self.device_config.print_head_px - usable_tape_height_pixels) / 2), 0 + ) + + # Bottom margin is equal due to centering of the tape + margin_bottom = margin_top + + # Make sure the total is the exact amount of pixels of the printhead + # Aka compensate for margin rounding/division errors + usable_tape_height_pixels = self.device_config.print_head_px - ( + margin_top + margin_bottom + ) + + # Return active pixels / margins set + return self.TapePrintProperties( + usable_tape_height_px=int(usable_tape_height_pixels), + top_margin_px=int(margin_top), + bottom_margin_px=int(margin_bottom), + ) + else: + # Tape size not supported + raise ValueError( + f"Unsupported tape size {self.tape_size_mm}mm. " + f"Supported sizes: {self.device_config.supported_tape_sizes_mm}mm" + ) + @property def device_config(self) -> DeviceConfig: return self._device_config From 13fa9b944d6fdc695a6cb3da83d0ebf5465138c4 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:41:24 +0000 Subject: [PATCH 16/34] Revert GUI supported_tape_sizes type --- src/labelle/gui/q_settings_toolbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labelle/gui/q_settings_toolbar.py b/src/labelle/gui/q_settings_toolbar.py index f633451a..57b45e6a 100644 --- a/src/labelle/gui/q_settings_toolbar.py +++ b/src/labelle/gui/q_settings_toolbar.py @@ -57,7 +57,7 @@ def _init_elements(self) -> None: def update_labeler_context( self, - supported_tape_sizes: list[int], + supported_tape_sizes: tuple[int, ...], installed_tape_size: int, minimum_horizontal_margin_mm: float, ) -> None: From 424ec34115bf6bebccb62aaa73c483ddbfe77d3c Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:53:17 +0000 Subject: [PATCH 17/34] Remove fixed default tapesize (take highest supported as default) Move LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM to device config --- src/labelle/lib/devices/device_config.py | 4 ++++ src/labelle/lib/devices/dymo_labeler.py | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index e4894806..f5c4f09a 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -34,6 +34,10 @@ class DeviceConfig: but negligible due to a physical spring in the lid. """ + LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM: float = 8.1 + """This is the distance between the printhead and the cutter (knife). + """ + def matches_device_id(self, device_id: int) -> bool: """Check if the a device ID matches this config.""" if device_id in self.device_ids: diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 6a8e0dae..f45cf5d9 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -242,9 +242,6 @@ class DymoLabeler: _device_config: DeviceConfig tape_size_mm: int - LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM = 8.1 - DEFAULT_TAPE_SIZE_MM = 12 - TapePrintProperties = namedtuple( "TapePrintProperties", ["usable_tape_height_px", "top_margin_px", "bottom_margin_px"], @@ -261,7 +258,10 @@ def __init__( self.device = device if tape_size_mm is None: - tape_size_mm = self.DEFAULT_TAPE_SIZE_MM + # Select highest supported tape size as default, if not set + tape_size_mm = max(self._device_config.supported_tape_sizes_mm) + + # Check if selected tape size is supported if tape_size_mm not in self._device_config.supported_tape_sizes_mm: raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " @@ -285,7 +285,7 @@ def _functions(self) -> DymoLabelerFunctions: @property def minimum_horizontal_margin_mm(self): - return self.LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM + return self.device_config.LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM @property def labeler_margin_px(self) -> tuple[float, float]: From d342a04a15bfc0a8ebf24478076c489201dc8dce Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:57:47 +0000 Subject: [PATCH 18/34] Shorten matches_device_id function --- src/labelle/lib/devices/device_config.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index f5c4f09a..28f8840f 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -40,7 +40,4 @@ class DeviceConfig: def matches_device_id(self, device_id: int) -> bool: """Check if the a device ID matches this config.""" - if device_id in self.device_ids: - return True - else: - return False + return device_id in self.device_ids From 17869289cce34e50ddf48e56bacd901729a70522 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:03:15 +0000 Subject: [PATCH 19/34] Fix code convention in if statement --- src/labelle/lib/devices/dymo_labeler.py | 110 ++++++++++++------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index f45cf5d9..36d460a7 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -296,67 +296,67 @@ def labeler_margin_px(self) -> tuple[float, float]: @property def tape_print_properties(self) -> TapePrintProperties: - if self.tape_size_mm in self.device_config.supported_tape_sizes_mm: - # Calculate the pixels per mm for this printer - # Example: printhead of 128 Pixels, distributed over 18 mm of active area. - # Makes 7.11 pixels/mm - print_pixels_per_mm: float = ( - self.device_config.print_head_px / self.device_config.print_head_mm + # Check if selected tape size supported + if self.tape_size_mm not in self.device_config.supported_tape_sizes_mm: + raise ValueError( + f"Unsupported tape size {self.tape_size_mm}mm. " + f"Supported sizes: {self.device_config.supported_tape_sizes_mm}mm" ) - # Calculate usable tape height (*2 for top and bottom) - usable_tape_height_mm: float = self.tape_size_mm - ( - 2 * self.device_config.tape_alignment_inaccuracy_mm - ) + # Calculate the pixels per mm for this printer + # Example: printhead of 128 Pixels, distributed over 18 mm of active area. + # Makes 7.11 pixels/mm + print_pixels_per_mm: float = ( + self.device_config.print_head_px / self.device_config.print_head_mm + ) - # Calculate the numer of active pixels for the tape - usable_tape_height_pixels: float = 0 - if usable_tape_height_mm >= self.device_config.print_head_mm: - # Tape is larger than active area of printhead. Use all pixels - usable_tape_height_pixels = self.device_config.print_head_px - else: - # Calculate the amount of active pixels we are able to use - # (taking the placement inaccuracy into account) - usable_tape_height_pixels = print_pixels_per_mm * usable_tape_height_mm - - # Round down to nearest whole number as we can't use half a pixels ;) - usable_tape_height_pixels = math.floor(usable_tape_height_pixels) - - # To calculate the margins we need to know some hardware info - # Printer has special "support studs" that - # let 19 & 24mm tapes go to the bottom (if supported) - # but 12mm based casettes are raised so they are centered to the printhead. - # Smaller tapes than 12mm are in 12mm casettes are centered - # in the cartridge and in turn also centered to the printhead - # Which gives us the advantage that we can - # just calculate the top and bottom margin - - # Calculate the top margin - margin_top = round( - ((self.device_config.print_head_px - usable_tape_height_pixels) / 2), 0 - ) + # Calculate usable tape height (*2 for top and bottom) + usable_tape_height_mm: float = self.tape_size_mm - ( + 2 * self.device_config.tape_alignment_inaccuracy_mm + ) + + # Calculate the numer of active pixels for the tape + usable_tape_height_pixels: float = 0 + if usable_tape_height_mm >= self.device_config.print_head_mm: + # Tape is larger than active area of printhead. Use all pixels + usable_tape_height_pixels = self.device_config.print_head_px + else: + # Calculate the amount of active pixels we are able to use + # (taking the placement inaccuracy into account) + usable_tape_height_pixels = print_pixels_per_mm * usable_tape_height_mm + + # Round down to nearest whole number as we can't use half a pixels ;) + usable_tape_height_pixels = math.floor(usable_tape_height_pixels) + + # To calculate the margins we need to know some hardware info + # Printer has special "support studs" that + # let 19 & 24mm tapes go to the bottom (if supported) + # but 12mm based casettes are raised so they are centered to the printhead. + # Smaller tapes than 12mm are in 12mm casettes are centered + # in the cartridge and in turn also centered to the printhead + # Which gives us the advantage that we can + # just calculate the top and bottom margin + + # Calculate the top margin + margin_top = round( + ((self.device_config.print_head_px - usable_tape_height_pixels) / 2), 0 + ) - # Bottom margin is equal due to centering of the tape - margin_bottom = margin_top + # Bottom margin is equal due to centering of the tape + margin_bottom = margin_top - # Make sure the total is the exact amount of pixels of the printhead - # Aka compensate for margin rounding/division errors - usable_tape_height_pixels = self.device_config.print_head_px - ( - margin_top + margin_bottom - ) + # Make sure the total is the exact amount of pixels of the printhead + # Aka compensate for margin rounding/division errors + usable_tape_height_pixels = self.device_config.print_head_px - ( + margin_top + margin_bottom + ) - # Return active pixels / margins set - return self.TapePrintProperties( - usable_tape_height_px=int(usable_tape_height_pixels), - top_margin_px=int(margin_top), - bottom_margin_px=int(margin_bottom), - ) - else: - # Tape size not supported - raise ValueError( - f"Unsupported tape size {self.tape_size_mm}mm. " - f"Supported sizes: {self.device_config.supported_tape_sizes_mm}mm" - ) + # Return active pixels / margins set + return self.TapePrintProperties( + usable_tape_height_px=int(usable_tape_height_pixels), + top_margin_px=int(margin_top), + bottom_margin_px=int(margin_bottom), + ) @property def device_config(self) -> DeviceConfig: From 16d82e8f87ab111f6a56bf3123988a74cacc6620 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:04:32 +0000 Subject: [PATCH 20/34] Shorten if statement --- src/labelle/lib/devices/device_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 1127768c..36f2bb61 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -100,7 +100,7 @@ def get_device_config_by_id(idValue: int) -> DeviceConfig | None: """ # for device in SUPPORTED_PRODUCTS: - if device.matches_device_id(idValue) is True: + if device.matches_device_id(idValue): return device # No device config found From e239867b9cc6ce8dc1be7863dc1d452f5fa82229 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:07:25 +0000 Subject: [PATCH 21/34] Shorten if statement --- src/labelle/lib/devices/dymo_labeler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 36d460a7..28c9f4ae 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -373,11 +373,11 @@ def device(self, device: UsbDevice | None): device.setup() # Retrieve device config - found_config = get_device_config_by_id(device.id_product) - if found_config is not None: - self._device_config = found_config - else: + found_device_config = get_device_config_by_id(device.id_product) + + if found_device_config is None: raise ValueError(f"Unsupported device type {device.id_product}") + self._deviceConfig = found_device_config else: # Use simulator config self._device_config = SIMULATOR_CONFIG From b39021782079fa211abacd5278c0de36723a452c Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:16:27 +0000 Subject: [PATCH 22/34] Let get_device_config_by_id raise exception if not supported --- src/labelle/lib/devices/device_manager.py | 13 +++++-------- src/labelle/lib/devices/dymo_labeler.py | 8 ++------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 36f2bb61..4a072af6 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -81,18 +81,15 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice LOG.debug(dev.device_info) dev = devices[0] if dev.is_supported: - found_device_config: DeviceConfig | None = get_device_config_by_id( - dev.id_product - ) - if found_device_config is not None: - msg = f"Recognized device as {found_device_config.name}" + device_config = get_device_config_by_id(dev.id_product) + msg = f"Recognized device as {device_config.name}" else: msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}" LOG.debug(msg) return dev -def get_device_config_by_id(idValue: int) -> DeviceConfig | None: +def get_device_config_by_id(product_id: int) -> DeviceConfig: """Get a labeler device config with USB ID. :param idValue: USB ID value @@ -100,8 +97,8 @@ def get_device_config_by_id(idValue: int) -> DeviceConfig | None: """ # for device in SUPPORTED_PRODUCTS: - if device.matches_device_id(idValue): + if device.matches_device_id(product_id): return device # No device config found - return None + raise ValueError(f"Unsupported device type product id: {product_id}") diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 28c9f4ae..3c422533 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -372,12 +372,8 @@ def device(self, device: UsbDevice | None): if device: device.setup() - # Retrieve device config - found_device_config = get_device_config_by_id(device.id_product) - - if found_device_config is None: - raise ValueError(f"Unsupported device type {device.id_product}") - self._deviceConfig = found_device_config + # Retrieve device config based on product ID + self._deviceConfig = get_device_config_by_id(device.id_product) else: # Use simulator config self._device_config = SIMULATOR_CONFIG From 0bdf8fd7dfd3c36277bfdaf09a405bfa9f4b6eb6 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:19:18 +0000 Subject: [PATCH 23/34] Remove var / shorten line --- src/labelle/lib/devices/device_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/labelle/lib/devices/device_manager.py b/src/labelle/lib/devices/device_manager.py index 4a072af6..1a66a2fd 100644 --- a/src/labelle/lib/devices/device_manager.py +++ b/src/labelle/lib/devices/device_manager.py @@ -81,8 +81,7 @@ def find_and_select_device(self, patterns: list[str] | None = None) -> UsbDevice LOG.debug(dev.device_info) dev = devices[0] if dev.is_supported: - device_config = get_device_config_by_id(dev.id_product) - msg = f"Recognized device as {device_config.name}" + msg = f"Recognized device as {get_device_config_by_id(dev.id_product).name}" else: msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}" LOG.debug(msg) @@ -101,4 +100,4 @@ def get_device_config_by_id(product_id: int) -> DeviceConfig: return device # No device config found - raise ValueError(f"Unsupported device type product id: {product_id}") + raise ValueError(f"Unsupported device type product id: {hex(product_id)}") From a4ed930f3c7eaffe10001db641fdaf0a67769d90 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:21:35 +0000 Subject: [PATCH 24/34] fix comment --- src/labelle/lib/devices/device_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index 28f8840f..4de9fc68 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -28,6 +28,7 @@ class DeviceConfig: tape_alignment_inaccuracy_mm: float = 1.0 """The inaccuracy of the tape position relative to the printhead + Inaccuracy of the tape position is mostly caused by the tape moving slightly from side to side in the cartridge. Improper cartrigde placemement is also an factor, From fdf353837179483548aa275a0b7e1c73759e7a85 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:26:45 +0000 Subject: [PATCH 25/34] Clear up comment block --- src/labelle/lib/devices/dymo_labeler.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 3c422533..06a069d6 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -328,14 +328,12 @@ def tape_print_properties(self) -> TapePrintProperties: # Round down to nearest whole number as we can't use half a pixels ;) usable_tape_height_pixels = math.floor(usable_tape_height_pixels) - # To calculate the margins we need to know some hardware info - # Printer has special "support studs" that - # let 19 & 24mm tapes go to the bottom (if supported) - # but 12mm based casettes are raised so they are centered to the printhead. - # Smaller tapes than 12mm are in 12mm casettes are centered - # in the cartridge and in turn also centered to the printhead - # Which gives us the advantage that we can - # just calculate the top and bottom margin + # To calculate the margins we need to know some hardware info: + # The printer has special "support studs" that allow 19 & 24mm tapes + # to sink to the bottom of the printer. + # 12mm based casettes are raised slightly so they are center to the printhead. + # Tapes smaller than 12mm are centered in the cartridge and to the printhead. + # This gives us the advantage that we can calculate the top and bottom margin # Calculate the top margin margin_top = round( From 0fa7ad5ef0a82c5e6f70c234e1181232f7617ad5 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 15:18:19 +0000 Subject: [PATCH 26/34] Fix wrong var name --- src/labelle/lib/devices/dymo_labeler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 06a069d6..c8097fc8 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -371,7 +371,7 @@ def device(self, device: UsbDevice | None): device.setup() # Retrieve device config based on product ID - self._deviceConfig = get_device_config_by_id(device.id_product) + self._device_config = get_device_config_by_id(device.id_product) else: # Use simulator config self._device_config = SIMULATOR_CONFIG From 7246473e2914ffa7574c58155b7ca9147eba96a3 Mon Sep 17 00:00:00 2001 From: FaBjE <5355001+FaBjE@users.noreply.github.com> Date: Sun, 23 Jun 2024 15:25:38 +0000 Subject: [PATCH 27/34] Remove constant DPI values and calculations As the DPI or PPM (pixels per mm) is now a property of the printer. Move all calculations requiring this value to the labeler object. This will improve future support of different DPI printers --- src/labelle/cli/cli.py | 25 ++++++------- src/labelle/gui/gui.py | 2 +- src/labelle/gui/q_labels_list.py | 10 +++--- src/labelle/gui/q_settings_toolbar.py | 2 +- src/labelle/lib/constants.py | 4 --- src/labelle/lib/devices/dymo_labeler.py | 36 +++++++++++++------ .../lib/render_engines/print_preview.py | 13 ++++--- src/labelle/lib/utils.py | 12 ------- 8 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/labelle/cli/cli.py b/src/labelle/cli/cli.py index 0ab34132..5979eeb5 100755 --- a/src/labelle/cli/cli.py +++ b/src/labelle/cli/cli.py @@ -18,7 +18,6 @@ from labelle.lib.constants import ( DEFAULT_BARCODE_TYPE, DEFAULT_MARGIN_PX, - PIXELS_PER_MM, USE_QR, BarcodeType, Direction, @@ -54,12 +53,12 @@ LOG = logging.getLogger(__name__) -def mm_to_payload_px(mm: float, margin: float) -> float: +def mm_to_payload_px(labeler: DymoLabeler, mm: float, margin: float) -> float: """Convert a length in mm to a number of pixels of payload. - The print resolution is 7 pixels/mm, and margin is subtracted from each side. + Margin is subtracted from each side. """ - return max(0, (mm * PIXELS_PER_MM) - margin * 2) + return max(0, (mm * labeler.pixels_per_mm) - margin * 2) def version_callback(value: bool) -> None: @@ -503,13 +502,6 @@ def default( min_label_mm_len = fixed_length max_label_mm_len = fixed_length - min_payload_len_px = mm_to_payload_px(min_label_mm_len, margin_px) - max_payload_len_px = ( - mm_to_payload_px(max_label_mm_len, margin_px) - if max_label_mm_len is not None - else None - ) - if output == Output.PRINTER: device_manager = get_device_manager() device = device_manager.find_and_select_device(patterns=device_pattern) @@ -518,13 +510,21 @@ def default( device = None dymo_labeler = DymoLabeler(tape_size_mm=tape_size_mm, device=device) + + min_payload_len_px = mm_to_payload_px(dymo_labeler, min_label_mm_len, margin_px) + max_payload_len_px = ( + mm_to_payload_px(dymo_labeler, max_label_mm_len, margin_px) + if max_label_mm_len is not None + else None + ) + if not render_engines: raise typer.BadParameter("No elements to print") render_engine = HorizontallyCombinedRenderEngine(render_engines) render_context = RenderContext( background_color="white", foreground_color="black", - height_px=dymo_labeler.height_px, + height_px=dymo_labeler.label_height_px, preview_show_margins=False, ) @@ -544,6 +544,7 @@ def default( else: render = PrintPreviewRenderEngine( render_engine=render_engine, + dymo_labeler=dymo_labeler, justify=justify, visible_horizontal_margin_px=margin_px, labeler_margin_px=dymo_labeler.labeler_margin_px, diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index 3fedd927..730ef1b5 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -105,7 +105,7 @@ def _on_settings_changed(self, settings: Settings) -> None: self._render_context = RenderContext( foreground_color=settings.foreground_color, background_color=settings.background_color, - height_px=self._dymo_labeler.height_px, + height_px=self._dymo_labeler.label_height_px, preview_show_margins=settings.preview_show_margins, ) self._label_list.update_params( diff --git a/src/labelle/gui/q_labels_list.py b/src/labelle/gui/q_labels_list.py index 60cb5a85..bbaa133e 100644 --- a/src/labelle/gui/q_labels_list.py +++ b/src/labelle/gui/q_labels_list.py @@ -27,7 +27,6 @@ RenderEngine, RenderEngineException, ) -from labelle.lib.utils import mm_to_px LOG = logging.getLogger(__name__) @@ -159,11 +158,12 @@ def render_preview(self) -> None: assert self.render_context is not None render_engine = PrintPreviewRenderEngine( render_engine=self._payload_render_engine, + dymo_labeler=self.dymo_labeler, justify=self.justify, - visible_horizontal_margin_px=mm_to_px(self.h_margin_mm), + visible_horizontal_margin_px=self.dymo_labeler.mm_to_px(self.h_margin_mm), labeler_margin_px=self.dymo_labeler.labeler_margin_px, max_width_px=None, - min_width_px=mm_to_px(self.min_label_width_mm), + min_width_px=self.dymo_labeler.mm_to_px(self.min_label_width_mm), ) try: bitmap = render_engine.render(self.render_context) @@ -179,10 +179,10 @@ def render_print(self) -> None: render_engine = PrintPayloadRenderEngine( render_engine=self._payload_render_engine, justify=self.justify, - visible_horizontal_margin_px=mm_to_px(self.h_margin_mm), + visible_horizontal_margin_px=self.dymo_labeler.mm_to_px(self.h_margin_mm), labeler_margin_px=self.dymo_labeler.labeler_margin_px, max_width_px=None, - min_width_px=mm_to_px(self.min_label_width_mm), + min_width_px=self.dymo_labeler.mm_to_px(self.min_label_width_mm), ) try: bitmap, _ = render_engine.render_with_meta(self.render_context) diff --git a/src/labelle/gui/q_settings_toolbar.py b/src/labelle/gui/q_settings_toolbar.py index 57b45e6a..f633451a 100644 --- a/src/labelle/gui/q_settings_toolbar.py +++ b/src/labelle/gui/q_settings_toolbar.py @@ -57,7 +57,7 @@ def _init_elements(self) -> None: def update_labeler_context( self, - supported_tape_sizes: tuple[int, ...], + supported_tape_sizes: list[int], installed_tape_size: int, minimum_horizontal_margin_mm: float, ) -> None: diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index 881d6585..646a39f1 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -137,10 +137,6 @@ DEFAULT_MARGIN_PX = 56 VERTICAL_PREVIEW_MARGIN_PX = 13 -DPI = 180 -MM_PER_INCH = 25.4 -PIXELS_PER_MM = DPI / MM_PER_INCH - ICON_DIR = Path(labelle.resources.icons.__file__).parent diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index c8097fc8..f712a79d 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -20,7 +20,6 @@ from labelle.lib.devices.device_config import DeviceConfig from labelle.lib.devices.device_manager import get_device_config_by_id from labelle.lib.devices.usb_device import UsbDevice, UsbDeviceError -from labelle.lib.utils import mm_to_px LOG = logging.getLogger(__name__) POSSIBLE_USB_ERRORS = (UsbDeviceError, NoBackendError, USBError) @@ -257,6 +256,9 @@ def __init__( ): self.device = device + if self._device_config is None: + raise ValueError("No device config") + if tape_size_mm is None: # Select highest supported tape size as default, if not set tape_size_mm = max(self._device_config.supported_tape_sizes_mm) @@ -270,7 +272,7 @@ def __init__( self.tape_size_mm = tape_size_mm @property - def height_px(self): + def label_height_px(self): """Get the (usable) tape height in pixels.""" return self.tape_print_properties.usable_tape_height_px @@ -285,12 +287,14 @@ def _functions(self) -> DymoLabelerFunctions: @property def minimum_horizontal_margin_mm(self): + # Return distance between printhead and cutter + # as we don't want to cut though our printed label return self.device_config.LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM @property def labeler_margin_px(self) -> tuple[float, float]: return ( - mm_to_px(self.minimum_horizontal_margin_mm), + self.mm_to_px(self.minimum_horizontal_margin_mm), self.tape_print_properties.top_margin_px, ) @@ -303,13 +307,6 @@ def tape_print_properties(self) -> TapePrintProperties: f"Supported sizes: {self.device_config.supported_tape_sizes_mm}mm" ) - # Calculate the pixels per mm for this printer - # Example: printhead of 128 Pixels, distributed over 18 mm of active area. - # Makes 7.11 pixels/mm - print_pixels_per_mm: float = ( - self.device_config.print_head_px / self.device_config.print_head_mm - ) - # Calculate usable tape height (*2 for top and bottom) usable_tape_height_mm: float = self.tape_size_mm - ( 2 * self.device_config.tape_alignment_inaccuracy_mm @@ -323,7 +320,7 @@ def tape_print_properties(self) -> TapePrintProperties: else: # Calculate the amount of active pixels we are able to use # (taking the placement inaccuracy into account) - usable_tape_height_pixels = print_pixels_per_mm * usable_tape_height_mm + usable_tape_height_pixels = self.pixels_per_mm * usable_tape_height_mm # Round down to nearest whole number as we can't use half a pixels ;) usable_tape_height_pixels = math.floor(usable_tape_height_pixels) @@ -385,6 +382,23 @@ def device(self, device: UsbDevice | None): def is_ready(self) -> bool: return self.device is not None + @property + def pixels_per_mm(self) -> float: + # Calculate the pixels per mm for this printer + # Example: printhead of 128 Pixels, distributed over 18 mm of active area. + # Makes 7.11 pixels/mm + return self.device_config.print_head_px / self.device_config.print_head_mm + + def px_to_mm(self, px) -> float: + """Convert pixels to millimeters for the current printer.""" + mm = px / self.pixels_per_mm + # Round up to nearest 0.1mm + return math.ceil(mm * 10) / 10 + + def mm_to_px(self, mm) -> float: + """Convert millimeters to pixels for the current printer.""" + return mm * self.pixels_per_mm + def print( self, bitmap: Image.Image, diff --git a/src/labelle/lib/render_engines/print_preview.py b/src/labelle/lib/render_engines/print_preview.py index a7406517..4cbc32cc 100644 --- a/src/labelle/lib/render_engines/print_preview.py +++ b/src/labelle/lib/render_engines/print_preview.py @@ -4,10 +4,10 @@ from PIL import Image, ImageColor, ImageDraw, ImageOps from labelle.lib.constants import Direction +from labelle.lib.devices.dymo_labeler import DymoLabeler from labelle.lib.render_engines.margins import MarginsRenderEngine from labelle.lib.render_engines.render_context import RenderContext from labelle.lib.render_engines.render_engine import RenderEngine -from labelle.lib.utils import px_to_mm class PrintPreviewRenderEngine(RenderEngine): @@ -15,10 +15,12 @@ class PrintPreviewRenderEngine(RenderEngine): Y_MARGIN_PX = 30 DX = X_MARGIN_PX * 0.3 DY = Y_MARGIN_PX * 0.3 + dymo_labeler: DymoLabeler def __init__( self, render_engine: RenderEngine, + dymo_labeler: DymoLabeler, justify: Direction = Direction.CENTER, visible_horizontal_margin_px: float = 0, labeler_margin_px: tuple[float, float] = (0, 0), @@ -26,6 +28,7 @@ def __init__( min_width_px: float = 0, ): super().__init__() + self.dymo_labeler = dymo_labeler self.render_engine = MarginsRenderEngine( render_engine=render_engine, mode="preview", @@ -166,28 +169,28 @@ def _show_margins(self, label_bitmap, preview_bitmap, meta, context): # payload width { "xy": (self.X_MARGIN_PX + label_width / 2, preview_width_mark_y), - "text": f"{px_to_mm(label_width - x_margin * 2)} mm", + "text": f"{self.dymo_labeler.px_to_mm(label_width - x_margin * 2)} mm", "anchor": "mm", "align": "center", }, # label width { "xy": (self.X_MARGIN_PX + label_width / 2, label_width_mark_y), - "text": f"{px_to_mm(label_width)} mm", + "text": f"{self.dymo_labeler.px_to_mm(label_width)} mm", "anchor": "mm", "align": "center", }, # payload height { "xy": (preview_width_mark_x, self.DY + label_height / 2 - self.DY), - "text": f"{px_to_mm(label_height - y_margin * 2)} mm", + "text": f"{self.dymo_labeler.px_to_mm(label_height - y_margin * 2)} mm", "anchor": "mm", "align": "center", }, # label height { "xy": (label_width_mark_x, self.DY + label_height / 2 + self.DY), - "text": f"{px_to_mm(label_height)} mm", + "text": f"{self.dymo_labeler.px_to_mm(label_height)} mm", "anchor": "mm", "align": "center", }, diff --git a/src/labelle/lib/utils.py b/src/labelle/lib/utils.py index a465004f..56fb7281 100755 --- a/src/labelle/lib/utils.py +++ b/src/labelle/lib/utils.py @@ -7,13 +7,11 @@ # === END LICENSE STATEMENT === import contextlib import logging -import math import sys from typing import Generator, List, Tuple from PIL import ImageDraw -from labelle.lib.constants import PIXELS_PER_MM from labelle.lib.logger import print_exception LOG = logging.getLogger(__name__) @@ -34,16 +32,6 @@ def draw_image(bitmap) -> Generator[ImageDraw.ImageDraw, None, None]: del drawobj -def px_to_mm(px) -> float: - mm = px / PIXELS_PER_MM - # Round up to nearest 0.1mm - return math.ceil(mm * 10) / 10 - - -def mm_to_px(mm) -> float: - return mm * PIXELS_PER_MM - - @contextlib.contextmanager def system_run() -> Generator[None, None, None]: try: From 182d77e1947fc4ec7168f9f84658df67a83579a4 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Fri, 2 Aug 2024 16:55:34 +0200 Subject: [PATCH 28/34] Update config for LabelManager 420P --- src/labelle/lib/constants.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/labelle/lib/constants.py b/src/labelle/lib/constants.py index 646a39f1..762ba89f 100755 --- a/src/labelle/lib/constants.py +++ b/src/labelle/lib/constants.py @@ -73,13 +73,13 @@ supported_tape_sizes_mm=[6, 9, 12], ), DeviceConfig( - name=f"LabelManager 420P {UNCONFIRMED_MESSAGE}", + # Ref: + name="LabelManager 420P", device_ids=[0x1003, 0x1004], - # ToDo: Validate config! - # Printhead 64 Pixels, distributed over 9mm of active area - print_head_px=64, - print_head_mm=9, - supported_tape_sizes_mm=[6, 9, 12], + # Printhead 128 Pixels, distributed over 18mm of active area + print_head_px=128, + print_head_mm=18, + supported_tape_sizes_mm=[6, 9, 12, 19], ), DeviceConfig( name="LabelManager 280", From ee78160873ee619a50aa92775b23cba9e92ebc31 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Thu, 4 Jul 2024 10:38:32 +0200 Subject: [PATCH 29/34] Fix NamedTuple --- src/labelle/lib/devices/dymo_labeler.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index f712a79d..b31426ca 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -10,7 +10,7 @@ import array import logging import math -from collections import namedtuple +from typing import NamedTuple import usb from PIL import Image @@ -236,19 +236,17 @@ def _raw_print_label(self, lines: list[list[int]]): LOG.debug(f"Post-send response: {status}") +class TapePrintProperties(NamedTuple): + usable_tape_height_px: int + top_margin_px: int + bottom_margin_px: int + + class DymoLabeler: _device: UsbDevice | None _device_config: DeviceConfig tape_size_mm: int - TapePrintProperties = namedtuple( - "TapePrintProperties", - ["usable_tape_height_px", "top_margin_px", "bottom_margin_px"], - ) - """Tape print properties tuple type. - Contains margins and size in pixels for printing. - """ - def __init__( self, tape_size_mm: int | None = None, @@ -347,7 +345,7 @@ def tape_print_properties(self) -> TapePrintProperties: ) # Return active pixels / margins set - return self.TapePrintProperties( + return TapePrintProperties( usable_tape_height_px=int(usable_tape_height_pixels), top_margin_px=int(margin_top), bottom_margin_px=int(margin_bottom), From c21d6be23610f40f483599c38aa45d8ece79b5f4 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Thu, 4 Jul 2024 10:42:35 +0200 Subject: [PATCH 30/34] Remove property from label_height_px --- src/labelle/cli/cli.py | 2 +- src/labelle/gui/gui.py | 2 +- src/labelle/lib/devices/dymo_labeler.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/labelle/cli/cli.py b/src/labelle/cli/cli.py index e5cd55e6..ad301a30 100755 --- a/src/labelle/cli/cli.py +++ b/src/labelle/cli/cli.py @@ -524,7 +524,7 @@ def default( render_context = RenderContext( background_color="white", foreground_color="black", - height_px=dymo_labeler.label_height_px, + height_px=dymo_labeler.get_label_height_px(), preview_show_margins=False, ) diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index 730ef1b5..dc18889b 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -105,7 +105,7 @@ def _on_settings_changed(self, settings: Settings) -> None: self._render_context = RenderContext( foreground_color=settings.foreground_color, background_color=settings.background_color, - height_px=self._dymo_labeler.label_height_px, + height_px=self._dymo_labeler.get_label_height_px(), preview_show_margins=settings.preview_show_margins, ) self._label_list.update_params( diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index b31426ca..2cf4d609 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -269,8 +269,7 @@ def __init__( ) self.tape_size_mm = tape_size_mm - @property - def label_height_px(self): + def get_label_height_px(self): """Get the (usable) tape height in pixels.""" return self.tape_print_properties.usable_tape_height_px From a5c07789146f9e357145dc130cfae4d6254e5be7 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Thu, 4 Jul 2024 11:07:20 +0200 Subject: [PATCH 31/34] Make DymoLabeler._device_config public Using a property here is indirect and pointless --- src/labelle/lib/devices/dymo_labeler.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 2cf4d609..b12ed546 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -244,7 +244,7 @@ class TapePrintProperties(NamedTuple): class DymoLabeler: _device: UsbDevice | None - _device_config: DeviceConfig + device_config: DeviceConfig tape_size_mm: int def __init__( @@ -254,18 +254,18 @@ def __init__( ): self.device = device - if self._device_config is None: + if self.device_config is None: raise ValueError("No device config") if tape_size_mm is None: # Select highest supported tape size as default, if not set - tape_size_mm = max(self._device_config.supported_tape_sizes_mm) + tape_size_mm = max(self.device_config.supported_tape_sizes_mm) # Check if selected tape size is supported - if tape_size_mm not in self._device_config.supported_tape_sizes_mm: + if tape_size_mm not in self.device_config.supported_tape_sizes_mm: raise ValueError( f"Unsupported tape size {tape_size_mm}mm. " - f"Supported sizes: {self._device_config.supported_tape_sizes_mm}mm" + f"Supported sizes: {self.device_config.supported_tape_sizes_mm}mm" ) self.tape_size_mm = tape_size_mm @@ -350,10 +350,6 @@ def tape_print_properties(self) -> TapePrintProperties: bottom_margin_px=int(margin_bottom), ) - @property - def device_config(self) -> DeviceConfig: - return self._device_config - @property def device(self) -> UsbDevice | None: return self._device @@ -365,10 +361,10 @@ def device(self, device: UsbDevice | None): device.setup() # Retrieve device config based on product ID - self._device_config = get_device_config_by_id(device.id_product) + self.device_config = get_device_config_by_id(device.id_product) else: # Use simulator config - self._device_config = SIMULATOR_CONFIG + self.device_config = SIMULATOR_CONFIG except UsbDeviceError as e: device = None From 277b10226b1c714f6bd8ed38003821f627e45c12 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Thu, 4 Jul 2024 14:29:03 +0200 Subject: [PATCH 32/34] Set the device explicitly --- src/labelle/gui/gui.py | 2 +- src/labelle/lib/devices/dymo_labeler.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/labelle/gui/gui.py b/src/labelle/gui/gui.py index dc18889b..8bc6ce1c 100644 --- a/src/labelle/gui/gui.py +++ b/src/labelle/gui/gui.py @@ -137,7 +137,7 @@ def _on_print_label(self) -> None: crash_msg_box(self, "Printing Failed!", err) def _on_device_selected(self) -> None: - self._dymo_labeler.device = self._device_selector.selected_device + self._dymo_labeler.set_device(self._device_selector.selected_device) self._settings_toolbar.on_settings_changed() diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index b12ed546..39b2918e 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -252,7 +252,7 @@ def __init__( tape_size_mm: int | None = None, device: UsbDevice | None = None, ): - self.device = device + self.set_device(device) if self.device_config is None: raise ValueError("No device config") @@ -352,12 +352,13 @@ def tape_print_properties(self) -> TapePrintProperties: @property def device(self) -> UsbDevice | None: + # Using a property here shields the device from being mutated + # outside of set_device. return self._device - @device.setter - def device(self, device: UsbDevice | None): + def set_device(self, device: UsbDevice | None): try: - if device: + if device is not None: device.setup() # Retrieve device config based on product ID From 833b92bf6ad63a0028e27bf74c49e464127dcc1a Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Thu, 4 Jul 2024 11:14:26 +0200 Subject: [PATCH 33/34] Remove property from pixels_per_mm --- src/labelle/cli/cli.py | 2 +- src/labelle/lib/devices/dymo_labeler.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/labelle/cli/cli.py b/src/labelle/cli/cli.py index ad301a30..7f16b0ca 100755 --- a/src/labelle/cli/cli.py +++ b/src/labelle/cli/cli.py @@ -58,7 +58,7 @@ def mm_to_payload_px(labeler: DymoLabeler, mm: float, margin: float) -> float: Margin is subtracted from each side. """ - return max(0, (mm * labeler.pixels_per_mm) - margin * 2) + return max(0, (mm * labeler.pixels_per_mm()) - margin * 2) def version_callback(value: bool) -> None: diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 39b2918e..2e8cfd4d 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -317,7 +317,7 @@ def tape_print_properties(self) -> TapePrintProperties: else: # Calculate the amount of active pixels we are able to use # (taking the placement inaccuracy into account) - usable_tape_height_pixels = self.pixels_per_mm * usable_tape_height_mm + usable_tape_height_pixels = self.pixels_per_mm() * usable_tape_height_mm # Round down to nearest whole number as we can't use half a pixels ;) usable_tape_height_pixels = math.floor(usable_tape_height_pixels) @@ -376,7 +376,6 @@ def set_device(self, device: UsbDevice | None): def is_ready(self) -> bool: return self.device is not None - @property def pixels_per_mm(self) -> float: # Calculate the pixels per mm for this printer # Example: printhead of 128 Pixels, distributed over 18 mm of active area. @@ -385,13 +384,13 @@ def pixels_per_mm(self) -> float: def px_to_mm(self, px) -> float: """Convert pixels to millimeters for the current printer.""" - mm = px / self.pixels_per_mm + mm = px / self.pixels_per_mm() # Round up to nearest 0.1mm return math.ceil(mm * 10) / 10 def mm_to_px(self, mm) -> float: """Convert millimeters to pixels for the current printer.""" - return mm * self.pixels_per_mm + return mm * self.pixels_per_mm() def print( self, From fd62b5345b00a811355d76161c70de07fa2822ad Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Thu, 4 Jul 2024 12:41:57 +0200 Subject: [PATCH 34/34] Store printhead-cutter distance in px instead of mm --- src/labelle/lib/devices/device_config.py | 5 ++--- src/labelle/lib/devices/dymo_labeler.py | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/labelle/lib/devices/device_config.py b/src/labelle/lib/devices/device_config.py index 4de9fc68..28b7ee54 100644 --- a/src/labelle/lib/devices/device_config.py +++ b/src/labelle/lib/devices/device_config.py @@ -35,9 +35,8 @@ class DeviceConfig: but negligible due to a physical spring in the lid. """ - LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM: float = 8.1 - """This is the distance between the printhead and the cutter (knife). - """ + distance_between_print_head_and_cutter_px: int = 57 + """Amount of unprintable tape remaining in the machine after cutting with knife.""" def matches_device_id(self, device_id: int) -> bool: """Check if the a device ID matches this config.""" diff --git a/src/labelle/lib/devices/dymo_labeler.py b/src/labelle/lib/devices/dymo_labeler.py index 2e8cfd4d..4b6d6445 100755 --- a/src/labelle/lib/devices/dymo_labeler.py +++ b/src/labelle/lib/devices/dymo_labeler.py @@ -286,12 +286,14 @@ def _functions(self) -> DymoLabelerFunctions: def minimum_horizontal_margin_mm(self): # Return distance between printhead and cutter # as we don't want to cut though our printed label - return self.device_config.LABELER_DISTANCE_BETWEEN_PRINT_HEAD_AND_CUTTER_MM + return self.px_to_mm( + self.device_config.distance_between_print_head_and_cutter_px + ) @property def labeler_margin_px(self) -> tuple[float, float]: return ( - self.mm_to_px(self.minimum_horizontal_margin_mm), + self.device_config.distance_between_print_head_and_cutter_px, self.tape_print_properties.top_margin_px, )