Import latest version from label_printer repository as starting point

This commit is contained in:
NAME
2026-02-12 19:25:05 +02:00
parent fdb143e726
commit 0743c44051
8 changed files with 273 additions and 28 deletions

View File

@@ -19,6 +19,9 @@ from kivy.graphics import Color, Rectangle
import os
import threading
import platform
import time
import datetime
import glob
from print_label import print_label_standalone, get_available_printers
from kivy.clock import Clock
@@ -35,11 +38,175 @@ class LabelPrinterApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.available_printers = self.get_available_printers()
# Build printer display names and mapping to full names
full_printers = get_available_printers()
self.printer_display_map = {} # display_name -> full_name
self.available_printers = []
for full_name in full_printers:
display_name = self._shorten_printer_name(full_name)
# Ensure unique display names
if display_name in self.printer_display_map:
display_name = full_name[:20]
self.printer_display_map[display_name] = full_name
self.available_printers.append(display_name)
# Clean old PDF backup files on startup
self.cleanup_old_pdfs()
# Clean old log files on startup
self.cleanup_old_logs()
def get_available_printers(self):
"""Get list of available printers (cross-platform)"""
return get_available_printers()
def _shorten_printer_name(self, name, max_len=20):
"""Shorten printer name for display (max 20 chars).
For network printers like \\\\server\\printer, show just the printer part."""
if name.startswith('\\\\'):
# Network printer: \\server\printer -> extract printer name
parts = name.strip('\\').split('\\')
if len(parts) >= 2:
short = parts[-1] # Just the printer name
else:
short = name
else:
short = name
# Truncate to max_len
if len(short) > max_len:
short = short[:max_len]
return short
def _get_full_printer_name(self, display_name):
"""Resolve display name back to full printer name for printing."""
return self.printer_display_map.get(display_name, display_name)
def cleanup_old_pdfs(self, days=5):
"""
Delete PDF files older than specified days from pdf_backup folder.
Args:
days (int): Delete files older than this many days (default: 5)
"""
pdf_backup_dir = 'pdf_backup'
# Create folder if it doesn't exist
if not os.path.exists(pdf_backup_dir):
os.makedirs(pdf_backup_dir, exist_ok=True)
return
try:
current_time = time.time()
cutoff_time = current_time - (days * 24 * 3600) # Convert days to seconds
for filename in os.listdir(pdf_backup_dir):
file_path = os.path.join(pdf_backup_dir, filename)
# Only process PDF files
if not filename.endswith('.pdf'):
continue
# Check if file is older than cutoff
if os.path.isfile(file_path):
file_mtime = os.path.getmtime(file_path)
if file_mtime < cutoff_time:
try:
os.remove(file_path)
print(f"Deleted old PDF: {filename}")
except Exception as e:
print(f"Failed to delete {filename}: {e}")
except Exception as e:
print(f"Error during PDF cleanup: {e}")
def cleanup_old_logs(self, days=5):
"""
Delete log files older than specified days from logs folder.
Args:
days (int): Delete files older than this many days (default: 5)
"""
logs_dir = 'logs'
# Create folder if it doesn't exist
if not os.path.exists(logs_dir):
os.makedirs(logs_dir, exist_ok=True)
return
try:
current_time = time.time()
cutoff_time = current_time - (days * 24 * 3600) # Convert days to seconds
for filename in os.listdir(logs_dir):
file_path = os.path.join(logs_dir, filename)
# Process both .log and .csv files
if not (filename.endswith('.log') or filename.endswith('.csv')):
continue
# Check if file is older than cutoff
if os.path.isfile(file_path):
file_mtime = os.path.getmtime(file_path)
if file_mtime < cutoff_time:
try:
os.remove(file_path)
print(f"Deleted old log: {filename}")
except Exception as e:
print(f"Failed to delete {filename}: {e}")
except Exception as e:
print(f"Error during log cleanup: {e}")
def log_print_action(self, sap_nr, quantity, cable_id, printer, pdf_filename, success):
"""
Log the print action to a CSV log file (table format).
Args:
sap_nr (str): SAP article number
quantity (str): Quantity value
cable_id (str): Cable ID
printer (str): Printer name
pdf_filename (str): Path to the generated PDF file
success (bool): Whether the print was successful
"""
logs_dir = 'logs'
# Create logs folder if it doesn't exist
os.makedirs(logs_dir, exist_ok=True)
try:
# Create log filename with date
log_date = datetime.datetime.now().strftime("%Y%m%d")
log_filename = os.path.join(logs_dir, f"print_log_{log_date}.csv")
# Create log entry values
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
status = "SUCCESS" if success else "FAILED"
# Escape CSV values (handle commas and quotes)
def escape_csv(value):
"""Escape CSV special characters"""
value = str(value)
if ',' in value or '"' in value or '\n' in value:
value = '"' + value.replace('"', '""') + '"'
return value
# Create CSV line
log_line = (
f"{timestamp},{status},"
f"{escape_csv(sap_nr)},"
f"{escape_csv(quantity)},"
f"{escape_csv(cable_id)},"
f"{escape_csv(printer)},"
f"{escape_csv(pdf_filename)}\n"
)
# Check if file exists to add header on first entry
file_exists = os.path.isfile(log_filename)
# Write to log file
with open(log_filename, 'a', encoding='utf-8') as f:
# Add header if file is new
if not file_exists:
f.write("Timestamp,Status,SAP-Nr,Quantity,Cable ID,Printer,PDF File\n")
# Append log entry
f.write(log_line)
print(f"Log entry saved to: {log_filename}")
except Exception as e:
print(f"Error saving log entry: {e}")
def build(self):
"""Build the simplified single-column UI"""
@@ -138,7 +305,8 @@ class LabelPrinterApp(App):
values=self.available_printers,
size_hint_y=None,
height=45,
font_size='12sp'
font_size='12sp',
sync_height=True,
)
self.printer_spinner = printer_spinner
form_layout.add_widget(printer_spinner)
@@ -180,7 +348,8 @@ class LabelPrinterApp(App):
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
# Resolve display name to full printer name
printer = self._get_full_printer_name(self.printer_spinner.text)
# Validate input
if not sap_nr and not quantity and not cable_id:
@@ -206,18 +375,37 @@ class LabelPrinterApp(App):
# Print in background thread (using PDF by default)
def print_thread():
pdf_filename = None
success = False
try:
success = print_label_standalone(label_text, printer, preview=0, use_pdf=True)
# Get the PDF filename that was created
# Files are saved to pdf_backup/ with timestamp
pdf_files = glob.glob('pdf_backup/final_label_*.pdf')
if pdf_files:
# Get the most recently created PDF file
pdf_filename = max(pdf_files, key=os.path.getctime)
if success:
# Log the successful print action
self.log_print_action(sap_nr, quantity, cable_id, printer, pdf_filename or "unknown", True)
# Use Clock.schedule_once to update UI from main thread
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
Clock.schedule_once(lambda dt: self.show_popup("Success", "Label printed successfully!"), 0.1)
# Clear inputs after successful print
# Clear inputs after successful print (but keep printer selection)
Clock.schedule_once(lambda dt: self.clear_inputs(), 0.2)
else:
# Log the failed print action
self.log_print_action(sap_nr, quantity, cable_id, printer, pdf_filename or "unknown", False)
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
Clock.schedule_once(lambda dt: self.show_popup("Error", "Failed to print label"), 0.1)
except Exception as e:
# Log the error
self.log_print_action(sap_nr, quantity, cable_id, printer, pdf_filename or "unknown", False)
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
Clock.schedule_once(lambda dt: self.show_popup("Error", f"Print error: {str(e)}"), 0.1)
@@ -226,10 +414,11 @@ class LabelPrinterApp(App):
thread.start()
def clear_inputs(self):
"""Clear all input fields"""
"""Clear only the input fields, preserving printer selection"""
self.sap_input.text = ''
self.qty_input.text = ''
self.cable_id_input.text = ''
# Printer selection is NOT cleared - it persists until user changes it
def show_popup(self, title, message):
"""Show a popup message"""