Update label printer GUI with improved preview layout - 50% larger canvas, proportional barcodes, 25 char limit, 0.5cm left margin
This commit is contained in:
BIN
label_barcode_tmp.png
Normal file
BIN
label_barcode_tmp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 787 B |
@@ -24,50 +24,145 @@ import cups
|
|||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
from print_label import create_label_image, print_label_standalone
|
from print_label import create_label_image, print_label_standalone
|
||||||
|
from kivy.clock import Clock
|
||||||
|
|
||||||
# Set window size
|
# Set window size
|
||||||
Window.size = (1600, 900)
|
Window.size = (1600, 900)
|
||||||
|
|
||||||
|
|
||||||
class LabelPreviewWidget(ScatterLayout):
|
class LabelPreviewWidget(BoxLayout):
|
||||||
"""Widget for displaying the label preview"""
|
"""Widget for displaying the warehouse identification label preview"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.label_image = None
|
self.orientation = 'vertical'
|
||||||
self.temp_preview_path = None
|
self.temp_file_path = None
|
||||||
|
|
||||||
def update_preview(self, text):
|
def update_preview(self, sap_nr, cantitate, lot_number):
|
||||||
"""Update the preview with new label image"""
|
"""Update preview with label and barcode information"""
|
||||||
if text:
|
try:
|
||||||
try:
|
# Create label with article info
|
||||||
self.label_image = create_label_image(text)
|
label_text = f"{sap_nr}|{cantitate}|{lot_number}"
|
||||||
self.display_preview()
|
self.generate_and_display_preview(sap_nr, cantitate, lot_number, label_text)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error creating preview: {e}")
|
print(f"Error updating preview: {e}")
|
||||||
|
|
||||||
def display_preview(self):
|
def generate_and_display_preview(self, sap_nr, cantitate, lot_number, barcode_text):
|
||||||
"""Display the preview image"""
|
"""Generate preview showing alternating text and barcode rows"""
|
||||||
if self.label_image:
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
# Save to temporary file
|
import barcode
|
||||||
import tempfile
|
from barcode.writer import ImageWriter
|
||||||
if self.temp_preview_path and os.path.exists(self.temp_preview_path):
|
|
||||||
|
# Canvas dimensions (11.5cm x 8cm) * 1.5 = 17.25cm x 12cm
|
||||||
|
# At 96 DPI: 11.5cm ≈ 435px, 8cm ≈ 303px
|
||||||
|
# Increased by 50%: 652px x 454px
|
||||||
|
canvas_width = 650
|
||||||
|
canvas_height = 450
|
||||||
|
|
||||||
|
# Left margin: 0.5cm ≈ 19px * 1.5 = 28px
|
||||||
|
left_margin = 28
|
||||||
|
usable_width = canvas_width - left_margin
|
||||||
|
|
||||||
|
# Create canvas
|
||||||
|
canvas = Image.new('RGB', (canvas_width, canvas_height), 'white')
|
||||||
|
draw = ImageDraw.Draw(canvas)
|
||||||
|
|
||||||
|
# 6 rows total
|
||||||
|
row_height = canvas_height // 6
|
||||||
|
|
||||||
|
# Font for text (larger for bigger canvas)
|
||||||
|
try:
|
||||||
|
label_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
|
||||||
|
value_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16)
|
||||||
|
except:
|
||||||
|
label_font = ImageFont.load_default()
|
||||||
|
value_font = ImageFont.load_default()
|
||||||
|
|
||||||
|
# Row definitions: (label_text, barcode_value, row_index)
|
||||||
|
rows_data = [
|
||||||
|
("SAP-Nr. Article", sap_nr, 0),
|
||||||
|
(None, sap_nr, 1), # Barcode row
|
||||||
|
("Cantitate", cantitate, 2),
|
||||||
|
(None, cantitate, 3), # Barcode row
|
||||||
|
("ID rola cablu", lot_number, 4),
|
||||||
|
(None, lot_number, 5), # Barcode row
|
||||||
|
]
|
||||||
|
|
||||||
|
for label_text, value, row_idx in rows_data:
|
||||||
|
row_y = row_idx * row_height
|
||||||
|
|
||||||
|
# Draw row border
|
||||||
|
draw.rectangle(
|
||||||
|
[(0, row_y), (canvas_width, row_y + row_height)],
|
||||||
|
outline='black',
|
||||||
|
width=1
|
||||||
|
)
|
||||||
|
|
||||||
|
if label_text:
|
||||||
|
# Text row: show label and value
|
||||||
|
draw.text(
|
||||||
|
(left_margin + 8, row_y + 8),
|
||||||
|
f"{label_text}: {value if value else '(empty)'}",
|
||||||
|
fill='black',
|
||||||
|
font=label_font
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Barcode row
|
||||||
|
if value and value.strip():
|
||||||
|
try:
|
||||||
|
# Limit barcode text to 25 characters
|
||||||
|
barcode_value = str(value)[:25]
|
||||||
|
|
||||||
|
CODE128 = barcode.get_barcode_class('code128')
|
||||||
|
writer_options = {
|
||||||
|
"write_text": False,
|
||||||
|
"module_width": 0.5,
|
||||||
|
"module_height": 12, # Smaller height for better fit
|
||||||
|
"quiet_zone": 2,
|
||||||
|
"font_size": 0
|
||||||
|
}
|
||||||
|
code = CODE128(barcode_value, writer=ImageWriter())
|
||||||
|
filename = code.save('label_barcode_tmp', options=writer_options)
|
||||||
|
barcode_img = Image.open(filename)
|
||||||
|
|
||||||
|
# Use standard barcode size, don't resize to fit width
|
||||||
|
canvas.paste(barcode_img, (left_margin + 4, row_y + 12))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Barcode error: {e}")
|
||||||
|
draw.text(
|
||||||
|
(left_margin + 8, row_y + row_height // 2 - 10),
|
||||||
|
"Barcode: " + str(value)[:25],
|
||||||
|
fill='black',
|
||||||
|
font=value_font
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
draw.text(
|
||||||
|
(left_margin + 8, row_y + row_height // 2 - 10),
|
||||||
|
"(no data)",
|
||||||
|
fill='gray',
|
||||||
|
font=value_font
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save and display
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
|
||||||
|
canvas.save(tmp.name)
|
||||||
|
|
||||||
|
# Clean up old file
|
||||||
|
if self.temp_file_path and os.path.exists(self.temp_file_path):
|
||||||
try:
|
try:
|
||||||
os.remove(self.temp_preview_path)
|
os.remove(self.temp_file_path)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
|
self.temp_file_path = tmp.name
|
||||||
self.label_image.save(tmp.name)
|
|
||||||
self.temp_preview_path = tmp.name
|
|
||||||
|
|
||||||
# Clear and recreate children
|
# Update display
|
||||||
self.clear_widgets()
|
self.clear_widgets()
|
||||||
|
img_widget = KivyImage(source=tmp.name, size_hint=(1, 1))
|
||||||
# Add image
|
self.add_widget(img_widget)
|
||||||
img = KivyImage(source=self.temp_preview_path, size_hint=(1, 1))
|
|
||||||
self.add_widget(img)
|
|
||||||
|
|
||||||
|
|
||||||
class LabelPrinterApp(App):
|
class LabelPrinterApp(App):
|
||||||
@@ -145,7 +240,6 @@ class LabelPrinterApp(App):
|
|||||||
size_hint_y=None,
|
size_hint_y=None,
|
||||||
height=50,
|
height=50,
|
||||||
font_size='16sp',
|
font_size='16sp',
|
||||||
input_filter='int',
|
|
||||||
background_color=(0.95, 0.95, 0.95, 1)
|
background_color=(0.95, 0.95, 0.95, 1)
|
||||||
)
|
)
|
||||||
self.qty_input.bind(text=self.on_input_change)
|
self.qty_input.bind(text=self.on_input_change)
|
||||||
@@ -203,7 +297,7 @@ class LabelPrinterApp(App):
|
|||||||
container = BoxLayout(orientation='vertical', size_hint_x=0.6, spacing=10)
|
container = BoxLayout(orientation='vertical', size_hint_x=0.6, spacing=10)
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
title = Label(text='[b]Label Preview (11.5cm x 8cm)[/b]', markup=True,
|
title = Label(text='[b]Label Preview[/b]', markup=True,
|
||||||
size_hint_y=0.08, font_size='18sp')
|
size_hint_y=0.08, font_size='18sp')
|
||||||
container.add_widget(title)
|
container.add_widget(title)
|
||||||
|
|
||||||
@@ -217,241 +311,11 @@ class LabelPrinterApp(App):
|
|||||||
"""Update preview when input changes"""
|
"""Update preview when input changes"""
|
||||||
# Get all input values
|
# Get all input values
|
||||||
sap_nr = self.sap_input.text
|
sap_nr = self.sap_input.text
|
||||||
quantity = self.qty_input.text
|
cantitate = self.qty_input.text # This is actually the barcode/cantitate field
|
||||||
cable_id = self.cable_id_input.text
|
cable_id = self.cable_id_input.text
|
||||||
|
|
||||||
# Create label text combining all fields
|
# Update preview
|
||||||
if sap_nr or quantity or cable_id:
|
self.preview_widget.update_preview(sap_nr, cantitate, cable_id)
|
||||||
label_text = f"{sap_nr}|{quantity}|{cable_id}"
|
|
||||||
self.preview_widget.update_preview(label_text)
|
|
||||||
|
|
||||||
def print_label(self, instance):
|
|
||||||
"""Handle print button press"""
|
|
||||||
sap_nr = self.sap_input.text.strip()
|
|
||||||
quantity = self.qty_input.text.strip()
|
|
||||||
cable_id = self.cable_id_input.text.strip()
|
|
||||||
printer = self.printer_spinner.text
|
|
||||||
|
|
||||||
# Validate input
|
|
||||||
if not sap_nr and not quantity and not cable_id:
|
|
||||||
self.show_popup("Error", "Please enter at least one field")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Create combined label text
|
|
||||||
label_text = f"{sap_nr}|{quantity}|{cable_id}"
|
|
||||||
|
|
||||||
# Show loading popup
|
|
||||||
popup = Popup(
|
|
||||||
title='Printing',
|
|
||||||
content=BoxLayout(
|
|
||||||
orientation='vertical',
|
|
||||||
padding=10,
|
|
||||||
spacing=10
|
|
||||||
),
|
|
||||||
size_hint=(0.6, 0.3)
|
|
||||||
)
|
|
||||||
|
|
||||||
popup.content.add_widget(Label(text='Printing label...\nPlease wait'))
|
|
||||||
popup.open()
|
|
||||||
|
|
||||||
# Print in background thread
|
|
||||||
def print_thread():
|
|
||||||
try:
|
|
||||||
success = print_label_standalone(label_text, printer, preview=0)
|
|
||||||
if success:
|
|
||||||
popup.dismiss()
|
|
||||||
self.show_popup("Success", "Label printed successfully!")
|
|
||||||
else:
|
|
||||||
popup.dismiss()
|
|
||||||
self.show_popup("Error", "Failed to print label")
|
|
||||||
except Exception as e:
|
|
||||||
popup.dismiss()
|
|
||||||
self.show_popup("Error", f"Print error: {str(e)}")
|
|
||||||
|
|
||||||
thread = threading.Thread(target=print_thread)
|
|
||||||
thread.daemon = True
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
def show_popup(self, title, message):
|
|
||||||
"""Show a popup message"""
|
|
||||||
popup = Popup(
|
|
||||||
title=title,
|
|
||||||
content=BoxLayout(
|
|
||||||
orientation='vertical',
|
|
||||||
padding=10,
|
|
||||||
spacing=10
|
|
||||||
),
|
|
||||||
size_hint=(0.6, 0.3)
|
|
||||||
)
|
|
||||||
|
|
||||||
popup.content.add_widget(Label(text=message))
|
|
||||||
|
|
||||||
close_button = Button(text='OK', size_hint_y=0.3)
|
|
||||||
close_button.bind(on_press=popup.dismiss)
|
|
||||||
popup.content.add_widget(close_button)
|
|
||||||
|
|
||||||
popup.open()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app = LabelPrinterApp()
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
|
|
||||||
class LabelPrinterApp(App):
|
|
||||||
"""Main Kivy application for label printing"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
self.available_printers = self.get_available_printers()
|
|
||||||
self.preview_widget = None
|
|
||||||
|
|
||||||
def get_available_printers(self):
|
|
||||||
"""Get list of available printers from CUPS"""
|
|
||||||
try:
|
|
||||||
conn = cups.Connection()
|
|
||||||
printers = conn.getPrinters()
|
|
||||||
return list(printers.keys()) if printers else ["PDF"]
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error getting printers: {e}")
|
|
||||||
return ["PDF"]
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
"""Build the main UI"""
|
|
||||||
self.title = "Label Printer Interface"
|
|
||||||
|
|
||||||
# Main layout - horizontal split between input and preview
|
|
||||||
main_layout = BoxLayout(orientation='horizontal', spacing=10, padding=10)
|
|
||||||
|
|
||||||
# Left column - Input form
|
|
||||||
left_column = self.create_input_column()
|
|
||||||
|
|
||||||
# Right column - Preview
|
|
||||||
right_column = self.create_preview_column()
|
|
||||||
|
|
||||||
main_layout.add_widget(left_column)
|
|
||||||
main_layout.add_widget(right_column)
|
|
||||||
|
|
||||||
return main_layout
|
|
||||||
|
|
||||||
def create_input_column(self):
|
|
||||||
"""Create the left column with input fields"""
|
|
||||||
container = BoxLayout(orientation='vertical', size_hint_x=0.4, spacing=10)
|
|
||||||
|
|
||||||
# Title
|
|
||||||
title = Label(text='[b]Label Information[/b]', markup=True, size_hint_y=0.08,
|
|
||||||
font_size='18sp')
|
|
||||||
container.add_widget(title)
|
|
||||||
|
|
||||||
# Scroll view for form
|
|
||||||
scroll = ScrollView(size_hint_y=0.85)
|
|
||||||
form_layout = GridLayout(cols=1, spacing=10, size_hint_y=None, padding=10)
|
|
||||||
form_layout.bind(minimum_height=form_layout.setter('height'))
|
|
||||||
|
|
||||||
# SAP-Nr. Articol
|
|
||||||
sap_label = Label(text='SAP-Nr. Articol:', size_hint_y=None, height=40,
|
|
||||||
font_size='14sp')
|
|
||||||
form_layout.add_widget(sap_label)
|
|
||||||
|
|
||||||
self.sap_input = TextInput(
|
|
||||||
multiline=False,
|
|
||||||
size_hint_y=None,
|
|
||||||
height=50,
|
|
||||||
font_size='16sp',
|
|
||||||
background_color=(0.95, 0.95, 0.95, 1)
|
|
||||||
)
|
|
||||||
self.sap_input.bind(text=self.on_input_change)
|
|
||||||
form_layout.add_widget(self.sap_input)
|
|
||||||
|
|
||||||
# Cantitate
|
|
||||||
qty_label = Label(text='Cantitate:', size_hint_y=None, height=40,
|
|
||||||
font_size='14sp')
|
|
||||||
form_layout.add_widget(qty_label)
|
|
||||||
|
|
||||||
self.qty_input = TextInput(
|
|
||||||
multiline=False,
|
|
||||||
size_hint_y=None,
|
|
||||||
height=50,
|
|
||||||
font_size='16sp',
|
|
||||||
input_filter='int',
|
|
||||||
background_color=(0.95, 0.95, 0.95, 1)
|
|
||||||
)
|
|
||||||
self.qty_input.bind(text=self.on_input_change)
|
|
||||||
form_layout.add_widget(self.qty_input)
|
|
||||||
|
|
||||||
# ID rola cablu
|
|
||||||
cable_id_label = Label(text='ID rola cablu:', size_hint_y=None, height=40,
|
|
||||||
font_size='14sp')
|
|
||||||
form_layout.add_widget(cable_id_label)
|
|
||||||
|
|
||||||
self.cable_id_input = TextInput(
|
|
||||||
multiline=False,
|
|
||||||
size_hint_y=None,
|
|
||||||
height=50,
|
|
||||||
font_size='16sp',
|
|
||||||
background_color=(0.95, 0.95, 0.95, 1)
|
|
||||||
)
|
|
||||||
self.cable_id_input.bind(text=self.on_input_change)
|
|
||||||
form_layout.add_widget(self.cable_id_input)
|
|
||||||
|
|
||||||
# Printer selection
|
|
||||||
printer_label = Label(text='Select Printer:', size_hint_y=None, height=40,
|
|
||||||
font_size='14sp')
|
|
||||||
form_layout.add_widget(printer_label)
|
|
||||||
|
|
||||||
printer_spinner = Spinner(
|
|
||||||
text=self.available_printers[0] if self.available_printers else "No Printers",
|
|
||||||
values=self.available_printers,
|
|
||||||
size_hint_y=None,
|
|
||||||
height=50,
|
|
||||||
font_size='14sp'
|
|
||||||
)
|
|
||||||
self.printer_spinner = printer_spinner
|
|
||||||
form_layout.add_widget(printer_spinner)
|
|
||||||
|
|
||||||
scroll.add_widget(form_layout)
|
|
||||||
container.add_widget(scroll)
|
|
||||||
|
|
||||||
# Print button
|
|
||||||
print_button = Button(
|
|
||||||
text='PRINT LABEL',
|
|
||||||
size_hint_y=0.15,
|
|
||||||
font_size='16sp',
|
|
||||||
background_color=(0.2, 0.6, 0.2, 1),
|
|
||||||
background_normal='',
|
|
||||||
bold=True
|
|
||||||
)
|
|
||||||
print_button.bind(on_press=self.print_label)
|
|
||||||
container.add_widget(print_button)
|
|
||||||
|
|
||||||
return container
|
|
||||||
|
|
||||||
def create_preview_column(self):
|
|
||||||
"""Create the right column with preview"""
|
|
||||||
container = BoxLayout(orientation='vertical', size_hint_x=0.6, spacing=10)
|
|
||||||
|
|
||||||
# Title
|
|
||||||
title = Label(text='[b]Label Preview (11.5cm x 8cm)[/b]', markup=True,
|
|
||||||
size_hint_y=0.08, font_size='18sp')
|
|
||||||
container.add_widget(title)
|
|
||||||
|
|
||||||
# Preview canvas
|
|
||||||
self.preview_widget = LabelPreviewWidget(size_hint_y=0.92)
|
|
||||||
container.add_widget(self.preview_widget)
|
|
||||||
|
|
||||||
return container
|
|
||||||
|
|
||||||
def on_input_change(self, instance, value):
|
|
||||||
"""Update preview when input changes"""
|
|
||||||
# Get all input values
|
|
||||||
sap_nr = self.sap_input.text
|
|
||||||
quantity = self.qty_input.text
|
|
||||||
cable_id = self.cable_id_input.text
|
|
||||||
|
|
||||||
# Create label text combining all fields
|
|
||||||
if sap_nr or quantity or cable_id:
|
|
||||||
label_text = f"{sap_nr}|{quantity}|{cable_id}"
|
|
||||||
self.preview_widget.update_preview(label_text)
|
|
||||||
|
|
||||||
def print_label(self, instance):
|
def print_label(self, instance):
|
||||||
"""Handle print button press"""
|
"""Handle print button press"""
|
||||||
|
|||||||
Reference in New Issue
Block a user