Skip to content
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

Develop cleanup 1 #66

Merged
merged 6 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/labelle/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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,
)

Expand Down
4 changes: 2 additions & 2 deletions src/labelle/gui/gui.py
Original file line number Diff line number Diff line change
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.label_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
5 changes: 2 additions & 3 deletions src/labelle/lib/devices/device_config.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this commit needed, can you please elaborate?
I assume that when adding a new device, one measures the printhead cutter distance using a ruler. How can one measure the distance in pixels?
I think this is a physical value, thus it should be in a real distance unit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that when adding a new device, one measures the printhead cutter distance using a ruler. How can one measure the distance in pixels?

Hmm, my assumption was that we should measure the distance using a test pattern. I hadn't thought about using a ruler.

In the end we must advance the tape by some whole number of pixels, so at least in my mind pixels are the fundamental unit of length.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that when adding a new device, one measures the printhead cutter distance using a ruler. How can one measure the distance in pixels?

Hmm, my assumption was that we should measure the distance using a test pattern. I hadn't thought about using a ruler.

In the end we must advance the tape by some whole number of pixels, so at least in my mind pixels are the fundamental unit of length.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that makes sense now! Measuring the test pattern indeed makes it reasonable to prefer pixels over millimeters.

Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
61 changes: 28 additions & 33 deletions src/labelle/lib/devices/dymo_labeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import array
import logging
import math
from collections import namedtuple
from typing import NamedTuple

import usb
from PIL import Image
Expand Down Expand Up @@ -236,43 +236,40 @@ 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
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,
device: UsbDevice | None = None,
):
self.device = device
self.set_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

@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

Expand All @@ -289,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,
)

Expand All @@ -320,7 +319,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)
Expand All @@ -347,31 +346,28 @@ 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),
)

@property
def device_config(self) -> DeviceConfig:
return self._device_config

@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
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
Expand All @@ -382,7 +378,6 @@ 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.
Expand All @@ -391,13 +386,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,
Expand Down