Skip to content

Merge develop into main #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d73626d
Add "DYMO LabelMANAGER PC II" device ID
FaBjE Jun 15, 2024
06f61ea
Add 24mm tape type
FaBjE Jun 16, 2024
3aecdc7
device specific init WIP
FaBjE Jun 18, 2024
1872e40
Add DeviceConfig class for each printer
FaBjE Jun 20, 2024
1b279b3
Please mypy...
FaBjE Jun 20, 2024
2353512
Fix usb device recognition
FaBjE Jun 21, 2024
dcc6234
Calculate margins instead of using lookup table
FaBjE Jun 21, 2024
040f2c2
Force sending an "exact printhead width" bitmap to the printer
FaBjE Jun 21, 2024
f907b8b
Fix config not updated when device set
FaBjE Jun 21, 2024
d64c977
Tested on LabelManager PnP
FaBjE Jun 21, 2024
a7def5e
replace all camels with snakes
FaBjE Jun 22, 2024
d8dcb3d
Cleanup supported device list syntax
FaBjE Jun 23, 2024
4fc8b68
Convert device config to dataclass
FaBjE Jun 23, 2024
1d36049
Convert labeler device-config to property
FaBjE Jun 23, 2024
d8b3b6d
Move get_tape_print_size_and_margins_px to labeler class tape_print_p…
FaBjE Jun 23, 2024
13fa9b9
Revert GUI supported_tape_sizes type
FaBjE Jun 23, 2024
424ec34
Remove fixed default tapesize (take highest supported as default)
FaBjE Jun 23, 2024
d342a04
Shorten matches_device_id function
FaBjE Jun 23, 2024
1786928
Fix code convention in if statement
FaBjE Jun 23, 2024
16d82e8
Shorten if statement
FaBjE Jun 23, 2024
e239867
Shorten if statement
FaBjE Jun 23, 2024
b390217
Let get_device_config_by_id raise exception if not supported
FaBjE Jun 23, 2024
0bdf8fd
Remove var / shorten line
FaBjE Jun 23, 2024
a4ed930
fix comment
FaBjE Jun 23, 2024
fdf3538
Clear up comment block
FaBjE Jun 23, 2024
0fa7ad5
Fix wrong var name
FaBjE Jun 23, 2024
7246473
Remove constant DPI values and calculations
FaBjE Jun 23, 2024
11f1cdb
Merge pull request #54 from FaBjE/feature/deviceDependentMargins
maresb Jul 4, 2024
182d77e
Update config for LabelManager 420P
maresb Aug 2, 2024
520e8ca
Merge pull request #63 from maresb/fix-420p
maresb Aug 2, 2024
f7e9e59
Merge branch 'main' into update-develop-with-mai
maresb Aug 3, 2024
65460fb
Merge pull request #64 from maresb/update-develop-with-mai
maresb Aug 3, 2024
ee78160
Fix NamedTuple
maresb Jul 4, 2024
c21d6be
Remove property from label_height_px
maresb Jul 4, 2024
a5c0778
Make DymoLabeler._device_config public
maresb Jul 4, 2024
277b102
Set the device explicitly
maresb Jul 4, 2024
833b92b
Remove property from pixels_per_mm
maresb Jul 4, 2024
fd62b53
Store printhead-cutter distance in px instead of mm
maresb Jul 4, 2024
5c98f8b
Merge pull request #66 from maresb/develop-cleanup-1
maresb Sep 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions src/labelle/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from labelle.lib.constants import (
DEFAULT_BARCODE_TYPE,
DEFAULT_MARGIN_PX,
PIXELS_PER_MM,
USE_QR,
BarcodeType,
Direction,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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.get_label_height_px(),
preview_show_margins=False,
)

Expand All @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions src/labelle/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.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,
)
Expand Down Expand Up @@ -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.get_label_height_px(),
preview_show_margins=settings.preview_show_margins,
)
self._label_list.update_params(
Expand Down Expand Up @@ -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()


Expand Down
10 changes: 5 additions & 5 deletions src/labelle/gui/q_labels_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
RenderEngine,
RenderEngineException,
)
from labelle.lib.utils import mm_to_px

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/labelle/gui/q_settings_toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
104 changes: 87 additions & 17 deletions src/labelle/lib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import labelle.resources.fonts
import labelle.resources.icons
from labelle.lib.devices.device_config import DeviceConfig

try:
from pyqrcode import QRCode
Expand All @@ -34,19 +35,92 @@
"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_PRODUCTS = {
0x0011: "DYMO LabelMANAGER PC",
0x0015: "LabelPoint 350",
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 USB Devices configuration ----
SUPPORTED_PRODUCTS = [
DeviceConfig(
name="DYMO LabelMANAGER PC",
device_ids=[0x0011],
# ToDo: Validate config!
# 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="LabelPoint 350",
device_ids=[0x0015],
# 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],
),
DeviceConfig(
name="DYMO LabelMANAGER PC II",
device_ids=[0x001C],
# 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, 24],
),
DeviceConfig(
name="LabelManager PnP",
device_ids=[0x1001, 0x1002],
# Printhead 64 Pixels, distributed over 9mm of active area
print_head_px=64,
print_head_mm=9,
supported_tape_sizes_mm=[6, 9, 12],
),
DeviceConfig(
# Ref: <https://github.com/labelle-org/labelle/issues/62>
name="LabelManager 420P",
device_ids=[0x1003, 0x1004],
# 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",
device_ids=[0x1006, 0x1005],
# 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],
),
DeviceConfig(
name=f"LabelManager Wireless PnP {UNCONFIRMED_MESSAGE}",
device_ids=[0x1007, 0x1008],
# 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],
),
DeviceConfig(
name=f"MobileLabeler {UNCONFIRMED_MESSAGE}",
device_ids=[0x1009],
# 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],
),
]

# Simulator configuration
SIMULATOR_CONFIG = DeviceConfig(
name="Simulator",
device_ids=[0],
# Fake 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, 24],
)


DEV_VENDOR = 0x0922

PRINTER_INTERFACE_CLASS = 0x07
Expand All @@ -63,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


Expand Down
43 changes: 43 additions & 0 deletions src/labelle/lib/devices/device_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from __future__ import annotations

import logging
from dataclasses import dataclass

LOG = logging.getLogger(__name__)


@dataclass
class DeviceConfig:
"""Configuration object for the capabilities of a label printer."""

name: str
"""Name of this device"""

device_ids: list[int]
"""List of USB Device ID's this device can identify as"""

print_head_px: int
"""Size of the print head in pixels (use calibration routine to determine)"""

print_head_mm: float
"""Size of the active area of the print head in millimters
(use calibration routine to determine)"""

supported_tape_sizes_mm: list[int]
"""List of supported tape sizes in 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.
"""

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."""
return device_id in self.device_ids
18 changes: 17 additions & 1 deletion src/labelle/lib/devices/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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__)
Expand Down Expand Up @@ -80,8 +81,23 @@ 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 {get_device_config_by_id(dev.id_product).name}"
else:
msg = f"Unrecognized device: {hex(dev.id_product)}. {UNCONFIRMED_MESSAGE}"
LOG.debug(msg)
return dev


def get_device_config_by_id(product_id: 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(product_id):
return device

# No device config found
raise ValueError(f"Unsupported device type product id: {hex(product_id)}")
Loading