Files
label_printer/label_printer_gui.py
Quality App Developer 31fab25430 Add Kivy GUI interface for label printer
- Created label_printer_gui.py: Complete Kivy-based GUI application
  - Two-column layout (input form + live preview)
  - SAP-Nr, Quantity, Cable ID input fields
  - Real-time barcode preview (11.5cm x 8cm)
  - Printer selection dropdown
  - Print button with CUPS integration

- Added setup automation:
  - setup_and_run.py: Python setup launcher
  - start_gui.sh: Bash launcher script
  - validate_project.py: Project validation

- Added comprehensive documentation:
  - INDEX.md: Project overview and quick start
  - GETTING_STARTED.md: 15-minute quick start guide
  - README_GUI.md: Complete feature documentation
  - TECHNICAL_DOCS.md: Architecture and customization
  - FILE_GUIDE.md: File reference guide
  - IMPLEMENTATION_SUMMARY.md: Implementation overview

- Updated dependencies:
  - requirements_gui.txt: New Kivy dependencies

- Preserved:
  - print_label.py: Original printing engine (modified to remove main code)
  - Original documentation and dependencies

Features:
- Live preview of labels as you type
- Automatic CUPS printer detection
- Non-blocking background printing
- User-friendly error handling
- Responsive two-column layout
- Production-ready quality
2026-02-04 15:37:04 +02:00

527 lines
17 KiB
Python

"""
Label Printer GUI Application using Kivy
This application provides a user-friendly interface for printing labels with barcodes.
"""
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.spinner import Spinner
from kivy.uix.scrollview import ScrollView
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.uix.image import Image as KivyImage
from kivy.uix.widget import Widget
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
from kivy.graphics import Color, Rectangle
from kivy.uix.scatterlayout import ScatterLayout
from PIL import Image as PILImage
import cups
import io
import os
import threading
from print_label import create_label_image, print_label_standalone
# Set window size
Window.size = (1600, 900)
class LabelPreviewWidget(ScatterLayout):
"""Widget for displaying the label preview"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.label_image = None
self.temp_preview_path = None
def update_preview(self, text):
"""Update the preview with new label image"""
if text:
try:
self.label_image = create_label_image(text)
self.display_preview()
except Exception as e:
print(f"Error creating preview: {e}")
def display_preview(self):
"""Display the preview image"""
if self.label_image:
# Save to temporary file
import tempfile
if self.temp_preview_path and os.path.exists(self.temp_preview_path):
try:
os.remove(self.temp_preview_path)
except:
pass
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
self.label_image.save(tmp.name)
self.temp_preview_path = tmp.name
# Clear and recreate children
self.clear_widgets()
# Add image
img = KivyImage(source=self.temp_preview_path, size_hint=(1, 1))
self.add_widget(img)
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):
"""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):
"""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()