Files
Label-design/app.py
2025-06-19 11:11:56 +03:00

199 lines
7.1 KiB
Python

import tkinter as tk
from tkinter import simpledialog, messagebox, ttk
from PIL import Image, ImageTk, ImageDraw, ImageFont
import barcode
from barcode.writer import ImageWriter
import cups
import os
def get_printers():
conn = cups.Connection()
return list(conn.getPrinters().keys())
def create_label_image(text):
# Label dimensions for 9x5 cm at 300 DPI
label_width = 1063 # 9 cm
label_height = 591 # 5 cm
# Outer frame (95% of label, centered)
outer_frame_width = int(label_width * 0.95)
outer_frame_height = int(label_height * 0.95)
outer_frame_x = (label_width - outer_frame_width) // 2
outer_frame_y = (label_height - outer_frame_height) // 2
# Barcode frame (top, inside outer frame)
barcode_frame_width = int(outer_frame_width * 0.90)
barcode_frame_height = int(outer_frame_height * 0.60)
barcode_frame_x = outer_frame_x + (outer_frame_width - barcode_frame_width) // 2
barcode_frame_y = outer_frame_y
# Text frame (immediately below barcode frame)
text_frame_width = int(outer_frame_width * 0.90)
text_frame_height = int(outer_frame_height * 0.35)
text_frame_x = outer_frame_x + (outer_frame_width - text_frame_width) // 2
gap_between_frames = 5 # or 0 for no gap
text_frame_y = barcode_frame_y + barcode_frame_height + gap_between_frames
# Generate barcode image (no text), at higher resolution
CODE128 = barcode.get_barcode_class('code128')
writer_options = {
"write_text": False,
"module_width": 0.5, # default is 0.2, increase for higher res
"module_height": barcode_frame_height, # match frame height
"quiet_zone": 3.5, # default, can adjust if needed
"font_size": 0 # no text
}
code = CODE128(text, writer=ImageWriter())
filename = code.save('label_barcode', options=writer_options)
barcode_img = Image.open(filename)
# Now resize barcode to exactly fit barcode frame (stretch, do not keep aspect ratio)
barcode_resized = barcode_img.resize((barcode_frame_width, barcode_frame_height), Image.LANCZOS)
# Create label image
label_img = Image.new('RGB', (label_width, label_height), 'white')
# Paste barcode centered in barcode frame
label_img.paste(barcode_resized, (barcode_frame_x, barcode_frame_y))
# Draw text in text frame, maximize font size to fit frame (keep sharpness)
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
max_font_size = text_frame_height
min_font_size = 10
best_font_size = min_font_size
for font_size in range(min_font_size, max_font_size + 1):
try:
font = ImageFont.truetype(font_path, font_size)
except IOError:
font = ImageFont.load_default()
break
dummy_img = Image.new('RGB', (1, 1))
dummy_draw = ImageDraw.Draw(dummy_img)
text_bbox = dummy_draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
if text_width > text_frame_width or text_height > text_frame_height:
break
best_font_size = font_size
# Use the best font size found
try:
font = ImageFont.truetype(font_path, best_font_size)
except IOError:
font = ImageFont.load_default()
draw = ImageDraw.Draw(label_img)
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
text_x = text_frame_x + (text_frame_width - text_width) // 2
text_y = text_frame_y + (text_frame_height - text_height) // 2
draw.text((text_x, text_y), text, font=font, fill='black')
os.remove(filename)
return label_img
def print_label(printer_name, text):
label_img = create_label_image(text)
label_img.save('final_label.png')
conn = cups.Connection()
conn.printFile(printer_name, 'final_label.png', "Label Print", {})
os.remove('final_label.png')
def show_preview(image_path, on_print):
preview = tk.Toplevel()
preview.title("Label Preview")
preview.geometry("1063x591")
preview.resizable(False, False)
img = Image.open(image_path)
img_tk = ImageTk.PhotoImage(img)
label = tk.Label(preview, image=img_tk)
label.image = img_tk # Keep reference
label.pack()
def print_and_close():
on_print()
preview.destroy()
# Auto-close and print after 4 seconds (4000 ms)
preview.after(4000, print_and_close)
preview.grab_set()
preview.wait_window()
def select_printer(printers):
dialog = tk.Toplevel()
dialog.title("Select Printer")
tk.Label(dialog, text="Select a printer:").pack(padx=10, pady=5)
printer_var = tk.StringVar()
combo = ttk.Combobox(dialog, textvariable=printer_var, values=printers, state="readonly")
combo.pack(padx=10, pady=5)
combo.current(0)
selected = {'printer': None}
def on_ok():
selected['printer'] = printer_var.get()
dialog.destroy()
btn = tk.Button(dialog, text="OK", command=on_ok)
btn.pack(pady=10)
dialog.grab_set()
dialog.wait_window()
return selected['printer']
def ask_label_text_with_preview():
dialog = tk.Toplevel()
dialog.title("Label Text")
tk.Label(dialog, text="Enter text for the label:").pack(padx=10, pady=(10, 2))
text_var = tk.StringVar()
entry = tk.Entry(dialog, textvariable=text_var, width=40)
entry.pack(padx=10, pady=2)
preview_var = tk.BooleanVar(value=True)
chk = tk.Checkbutton(dialog, text="Show print preview", variable=preview_var)
chk.pack(padx=10, pady=2)
result = {'text': None, 'preview': True}
def on_ok():
result['text'] = text_var.get()
result['preview'] = preview_var.get()
dialog.destroy()
btn = tk.Button(dialog, text="OK", command=on_ok)
btn.pack(pady=10)
entry.focus()
dialog.grab_set()
dialog.wait_window()
return result['text'], result['preview']
def show_auto_close_info(title, message, timeout=3000):
info = tk.Toplevel()
info.title(title)
info.geometry("300x100")
info.resizable(False, False)
tk.Label(info, text=message, font=("Arial", 12)).pack(expand=True, padx=10, pady=10)
info.after(timeout, info.destroy)
info.grab_set()
info.wait_window()
def main():
root = tk.Tk()
root.withdraw()
printers = get_printers()
if not printers:
messagebox.showerror("Error", "No printers found.")
return
printer = select_printer(printers)
if not printer:
messagebox.showerror("Error", "No printer selected.")
return
text, do_preview = ask_label_text_with_preview()
if not text:
messagebox.showerror("Error", "No text entered.")
return
label_img = create_label_image(text)
label_img.save('final_label.png')
if do_preview:
show_preview('final_label.png', lambda: print_label(printer, text))
show_auto_close_info("Done", "Label sent to printer.", timeout=2000)
else:
print_label(printer, text)
show_auto_close_info("Done", "Label sent to printer.", timeout=5000)
if __name__ == "__main__":
main()