added support for printing simple labels

This commit is contained in:
Hubert Bryłkowski 2023-07-09 17:32:08 +02:00 committed by Piotr Gaczkowski
parent cdffc5fe68
commit 851012c2b6
6 changed files with 151 additions and 14 deletions

View file

@ -1,6 +1,14 @@
import os import os
from telegram.ext import CommandHandler, ApplicationBuilder from telegram import Update
from telegram.ext import (
CommandHandler,
ApplicationBuilder,
ConversationHandler,
CallbackContext,
filters,
MessageHandler,
)
from labeler.app.labeler import Application from labeler.app.labeler import Application
from labeler.infra.e550w_printer.printer import E550W from labeler.infra.e550w_printer.printer import E550W
@ -8,19 +16,67 @@ from labeler.infra.renderer import PILRenderer
class LabelingBot: class LabelingBot:
def __init__(self, app): def __init__(self, app: Application):
self.app = app self.app = app
async def media_info(self, update, context): async def media_info(self, update, context):
media = self.app.get_installed_media() media = self.app.get_installed_media()
await update.message.reply_text(f"Installed media: {media.description}") await update.message.reply_text(f"Installed media: {media.description}")
async def label_length(self, update, context):
await update.message.reply_text(
"Hello! Please tell me the length of the label, enter 0 for auto:"
)
return LABEL_LENGTH
async def label_text(self, update: Update, context: CallbackContext) -> int:
user_input = update.message.text
context.user_data["length"] = int(user_input)
await update.message.reply_text("Now, please tell me the text of the label:")
return LABEL_TEXT
async def simple_label(self, update: Update, context: CallbackContext) -> int:
user_input = update.message.text
context.user_data["label"] = user_input
try:
label = self.app.print_label(
text=context.user_data["label"], length=context.user_data["length"]
)
except Exception as e:
await update.message.reply_text(f"There was an exception: {e}")
return ConversationHandler.END
await update.message.reply_photo(
label.bytes, f'Your label is: {context.user_data["label"]}'
)
return ConversationHandler.END
async def cancel(self, update: Update, context: CallbackContext) -> int:
await update.message.reply_text("Cancelled.")
return ConversationHandler.END
if __name__ == "__main__": if __name__ == "__main__":
application = Application(PILRenderer(), E550W(os.environ.get("PRINTER_IP"))) application = Application(PILRenderer(), E550W(os.environ.get("PRINTER_IP")))
bot = LabelingBot(application) bot = LabelingBot(application)
LABEL_LENGTH, LABEL_TEXT = range(2)
conv_handler = ConversationHandler(
entry_points=[CommandHandler("simple_label", bot.label_length)],
states={
LABEL_LENGTH: [
MessageHandler(filters.Text() & ~filters.Command(), bot.label_text)
],
LABEL_TEXT: [
MessageHandler(filters.Text() & ~filters.Command(), bot.simple_label)
],
},
fallbacks=[CommandHandler("cancel", bot.cancel)],
)
app = ApplicationBuilder().token(os.environ["TELEGRAM_TOKEN"]).build() app = ApplicationBuilder().token(os.environ["TELEGRAM_TOKEN"]).build()
app.add_handler(CommandHandler("media_info", bot.media_info)) app.add_handler(CommandHandler("media_info", bot.media_info))
app.add_handler(conv_handler)
app.run_polling() app.run_polling()

View file

@ -1,4 +1,11 @@
from labeler.domain.objects import Label, LabelRequest, LabelDefinition, MediaDefinition from labeler.domain.objects import (
Label,
LabelRequest,
LabelDefinition,
MediaDefinition,
Dimension,
Image,
)
from labeler.interfaces import Renderer, Printer from labeler.interfaces import Renderer, Printer
@ -7,16 +14,16 @@ class Application:
self.renderer = renderer self.renderer = renderer
self.printer = printer self.printer = printer
def render_preview(self, label_request: LabelRequest): def render_preview(self, text: str, length: int = None) -> Label:
media = self.printer.get_installed_media() media = self.printer.get_installed_media()
if label_request.length is not None: if length != 0:
label_length = label_request.length - 2 * media.minimal_margin_horizontal label_length = Dimension(mm=length) - 2 * media.minimal_margin_horizontal
else: else:
label_length = media.printable_length label_length = None
label_definition = LabelDefinition( label_definition = LabelDefinition(
text=label_request.text, text=text,
length=label_length, length=label_length,
width=media.printable_width, width=media.printable_width,
dpi=media.dpi, dpi=media.dpi,
@ -24,5 +31,24 @@ class Application:
self.renderer.render_label(label_definition) self.renderer.render_label(label_definition)
def print_label(self, text: str, length: int = None) -> Image:
media = self.printer.get_installed_media()
if length != 0:
label_length = Dimension(mm=length) - 2 * media.minimal_margin_horizontal
else:
label_length = None
label_definition = LabelDefinition(
text=text,
length=label_length,
width=media.printable_width,
dpi=media.dpi,
)
label = self.renderer.render_label(label_definition)
self.printer.print_label(label)
return label
def get_installed_media(self) -> MediaDefinition: def get_installed_media(self) -> MediaDefinition:
return self.printer.get_installed_media() return self.printer.get_installed_media()

View file

@ -107,16 +107,17 @@ class MediaDefinition(BaseModel):
length: Dimension length: Dimension
minimal_margin_vertical: Dimension minimal_margin_vertical: Dimension
minimal_margin_horizontal: Dimension minimal_margin_horizontal: Dimension
minimum_length: Dimension = Field(default_factory=lambda: Dimension(mm=5))
dpi: int dpi: int
description: str description: str
@property @property
def printable_width(self) -> Dimension: def printable_width(self) -> Dimension:
return self.width - 2 * self.minimal_margin_horizontal return self.width - 2 * self.minimal_margin_vertical
@property @property
def printable_length(self) -> Dimension: def printable_length(self) -> Dimension:
return self.length - 2 * self.minimal_margin_vertical return self.length - 2 * self.minimal_margin_horizontal
class Label(BaseModel): class Label(BaseModel):

View file

@ -1,10 +1,14 @@
import io
import logging
from math import inf from math import inf
from brother_ql import BrotherQLRaster, create_label
from brother_ql.backends import guess_backend, backend_factory
from pysnmp.entity.engine import SnmpEngine from pysnmp.entity.engine import SnmpEngine
from pysnmp.hlapi import getCmd, CommunityData, UdpTransportTarget, ContextData from pysnmp.hlapi import getCmd, CommunityData, UdpTransportTarget, ContextData
from pysnmp.smi.rfc1902 import ObjectType, ObjectIdentity from pysnmp.smi.rfc1902 import ObjectType, ObjectIdentity
from labeler.domain.objects import MediaDefinition, Dimension from labeler.domain.objects import MediaDefinition, Dimension, Image
from labeler.infra.e550w_printer.media_definitions import ( from labeler.infra.e550w_printer.media_definitions import (
media_width, media_width,
tape_color, tape_color,
@ -16,6 +20,12 @@ from labeler.infra.e550w_printer.media_definitions import (
TYPE_BYTE, TYPE_BYTE,
) )
from labeler.interfaces import Printer from labeler.interfaces import Printer
from PIL import Image as PILImage
PRINTABLE_WIDTH = {
12: Dimension.from_points(150, 360),
24: Dimension.from_points(320, 360),
}
class E550W(Printer): class E550W(Printer):
@ -26,6 +36,45 @@ class E550W(Printer):
def get_installed_media(self) -> MediaDefinition: def get_installed_media(self) -> MediaDefinition:
return self.__get_printer_status() return self.__get_printer_status()
def print_label(self, label: Image):
im = PILImage.open(io.BytesIO(label.bytes))
qlr = BrotherQLRaster("PT-E550W")
create_label(
qlr,
im,
self.__media_width_to_type(label.height),
red=False,
threshold=70,
cut=True,
rotate=270,
compress=True,
dpi_600=True,
hq=True,
)
try:
try:
selected_backend = guess_backend(f"tcp://{self.ip_address}:9100")
except ValueError:
logging.error(
"Couln't guess the backend to use from the printer string descriptor"
)
BACKEND_CLASS = backend_factory(selected_backend)["backend_class"]
be = BACKEND_CLASS(f"tcp://{self.ip_address}:9100")
be.write(qlr.data)
be.dispose()
del be
except Exception as e:
logging.exception("Exception happened: %s", e)
def __media_width_to_type(self, height: int):
metric_width = Dimension.from_points(height, 360)
if metric_width == Dimension.from_points(150, 360):
return "pt512"
else:
raise ValueError(f"Unsupported media width: {metric_width}")
def __get_printer_status(self): def __get_printer_status(self):
raw_snmp_data = self.__get_snmp_status().asNumbers() raw_snmp_data = self.__get_snmp_status().asNumbers()
width = media_width(raw_snmp_data[WIDTH_BYTE]) width = media_width(raw_snmp_data[WIDTH_BYTE])
@ -36,9 +85,9 @@ class E550W(Printer):
return MediaDefinition( return MediaDefinition(
width=Dimension(mm=width), width=Dimension(mm=width),
length=Dimension(mm=inf), length=Dimension(mm=inf),
minimal_margin_vertical=Dimension(mm=1), minimal_margin_vertical=(Dimension(mm=width) - PRINTABLE_WIDTH[width]) / 2,
minimal_margin_horizontal=Dimension(mm=2), minimal_margin_horizontal=Dimension(mm=1),
dpi=600, dpi=360,
description=f"{tape_type} - {width}mm, {media_text_color} on {media_tape_color} background", description=f"{tape_type} - {width}mm, {media_text_color} on {media_tape_color} background",
) )

View file

@ -13,3 +13,7 @@ class Printer(abc.ABC):
@abc.abstractmethod @abc.abstractmethod
def get_installed_media(self) -> MediaDefinition: def get_installed_media(self) -> MediaDefinition:
pass pass
@abc.abstractmethod
def print_label(self, label: Image):
pass

View file

@ -6,6 +6,7 @@ as provide info about printer status and other useful information.
## Supported commands ## Supported commands
- `/media_info` - show info about currently installed media - `/media_info` - show info about currently installed media
- `/simple_label` - print a simple label
### usage example ### usage example
You need to things: You need to things: