Internal inventory labels: the QR carries L#### only, which keeps it at version 1 (large modules, reliable at 23 mm) and means scanning a label at the station types the ID and lands directly in the re-measure flow.
113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
"""
|
|
Lamp ID labels on the Brother QL-820NWB (DK-11221, 23x23 mm die-cut).
|
|
|
|
Each label carries the lamp ID as a QR code and as text. These are internal
|
|
inventory labels: scanning one with the station's barcode scanner types the
|
|
ID, which drops straight into the re-measure flow. The station prints two
|
|
per lamp: one for the box, one for the lamp itself.
|
|
|
|
Printer address comes from --printer or the HPCS_LABEL_PRINTER environment
|
|
variable, e.g. tcp://192.168.1.50 (the QL-820NWB listens on port 9100).
|
|
|
|
Usage:
|
|
uv run label_printer.py L0006 # print 2 labels
|
|
uv run label_printer.py L0006 --copies 1
|
|
uv run label_printer.py L0006 --preview out.png # render only, no printer
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
import qrcode
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
LABEL_ID = "23x23" # DK-11221
|
|
DOTS = (202, 202) # printable area of the 23x23 label
|
|
MODEL = "QL-820NWB"
|
|
TEXT_HEIGHT = 52 # bottom strip for the ID text
|
|
|
|
|
|
def _font(size):
|
|
for name in ("arialbd.ttf", "arial.ttf", "DejaVuSans-Bold.ttf"):
|
|
try:
|
|
return ImageFont.truetype(name, size)
|
|
except OSError:
|
|
continue
|
|
return ImageFont.load_default(size)
|
|
|
|
|
|
def render_label(lamp_id: str) -> Image.Image:
|
|
"""202x202 1-bit label: the bare lamp ID as QR on top, as text below."""
|
|
image = Image.new("1", DOTS, 1)
|
|
|
|
# Just the ID (internal use): a 5-character QR stays at version 1, so the
|
|
# modules print large and scan reliably at 23 mm. Scanning it at the
|
|
# station types the ID and lands in the re-measure flow.
|
|
qr = qrcode.QRCode(
|
|
error_correction=qrcode.constants.ERROR_CORRECT_M,
|
|
box_size=1,
|
|
border=2, # quiet zone; tight but fine for close-range scanning
|
|
)
|
|
qr.add_data(lamp_id.upper())
|
|
qr.make(fit=True)
|
|
modules = len(qr.get_matrix())
|
|
qr_size = DOTS[1] - TEXT_HEIGHT
|
|
qr.box_size = max(1, qr_size // modules)
|
|
qr_img = qr.make_image().get_image().convert("1")
|
|
image.paste(qr_img, ((DOTS[0] - qr_img.width) // 2, (qr_size - qr_img.height) // 2))
|
|
|
|
draw = ImageDraw.Draw(image)
|
|
size = 44
|
|
while size > 10:
|
|
font = _font(size)
|
|
box = draw.textbbox((0, 0), lamp_id, font=font)
|
|
if box[2] - box[0] <= DOTS[0] - 8:
|
|
break
|
|
size -= 2
|
|
draw.text(
|
|
((DOTS[0] - (box[2] - box[0])) // 2 - box[0], DOTS[1] - TEXT_HEIGHT + (TEXT_HEIGHT - (box[3] - box[1])) // 2 - box[1]),
|
|
lamp_id,
|
|
font=font,
|
|
fill=0,
|
|
)
|
|
return image
|
|
|
|
|
|
def print_labels(lamp_id: str, printer: str, copies: int = 2) -> None:
|
|
"""Render and send `copies` labels to the QL-820NWB."""
|
|
from brother_ql.backends.helpers import send
|
|
from brother_ql.conversion import convert
|
|
from brother_ql.raster import BrotherQLRaster
|
|
|
|
image = render_label(lamp_id)
|
|
raster = BrotherQLRaster(MODEL)
|
|
instructions = convert(qlr=raster, images=[image] * copies, label=LABEL_ID, cut=True)
|
|
send(instructions=instructions, printer_identifier=printer, backend_identifier="network", blocking=True)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Print lamp ID labels (QR + ID)")
|
|
parser.add_argument("lamp_id", help="e.g. L0006")
|
|
parser.add_argument("--printer", default=os.environ.get("HPCS_LABEL_PRINTER", ""),
|
|
help="tcp://<ip> of the QL-820NWB (default: HPCS_LABEL_PRINTER env)")
|
|
parser.add_argument("--copies", type=int, default=2)
|
|
parser.add_argument("--preview", metavar="PNG",
|
|
help="Write the rendered label to a PNG instead of printing")
|
|
args = parser.parse_args()
|
|
|
|
lamp_id = args.lamp_id.upper()
|
|
if args.preview:
|
|
render_label(lamp_id).save(args.preview)
|
|
print(f"Wrote {args.preview}")
|
|
return
|
|
if not args.printer:
|
|
print("ERROR: no printer configured. Pass --printer tcp://<ip> or set HPCS_LABEL_PRINTER.")
|
|
sys.exit(1)
|
|
print_labels(lamp_id, args.printer, args.copies)
|
|
print(f"Printed {args.copies} label(s) for {lamp_id} on {args.printer}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|