From 33df76f758dffd14f334f6ac09a15d9a3432c430 Mon Sep 17 00:00:00 2001 From: ske087 Date: Mon, 30 Jun 2025 15:45:36 +0300 Subject: [PATCH] print label function created --- app.py | 535 ++++++++++++++++++++++++++++++++++++++++----- final_label.png | Bin 0 -> 16752 bytes print_label.py | 217 ++++++++++++++++++ printer_config.txt | 1 + 4 files changed, 696 insertions(+), 57 deletions(-) create mode 100644 final_label.png create mode 100644 print_label.py create mode 100644 printer_config.txt diff --git a/app.py b/app.py index df4e915..3982f42 100644 --- a/app.py +++ b/app.py @@ -1,16 +1,421 @@ import tkinter as tk -from tkinter import simpledialog, messagebox, ttk +from tkinter import simpledialog, messagebox, ttk, font from PIL import Image, ImageTk, ImageDraw, ImageFont import barcode from barcode.writer import ImageWriter import cups import os +import time +# Valid operator codes (in a real app, this would be in a database) +VALID_OPERATORS = { + "OP001": "Operator 1", + "OP002": "Operator 2", + "OP003": "Operator 3", + "123456": "Admin" # Simple code for testing +} + +class LabelApp(tk.Tk): + def __init__(self): + super().__init__() + + # Configure fullscreen toggling + self.allow_fullscreen_toggle = True # Variable to control ESC key behavior + + # Configure the main window + self.title("Label Printing System") + self.configure(bg="#f0f0f0") + + # Create a container for all frames + self.container = tk.Frame(self) + self.container.pack(fill="both", expand=True) + + # Current logged in operator + self.current_operator = None + + # Initialize frames dictionary + self.frames = {} + + # Create frames + for F in (LoginFrame, DashboardFrame, SettingsFrame): + frame = F(self.container, self) + self.frames[F] = frame + frame.grid(row=0, column=0, sticky="nsew") + + # Configure container grid + self.container.grid_rowconfigure(0, weight=1) + self.container.grid_columnconfigure(0, weight=1) + + # Show login frame + self.show_frame(LoginFrame) + + # Bind Escape key to toggle fullscreen, respecting the variable + self.bind('', self.handle_escape) + + # Use after() to ensure fullscreen works - solves issues on some systems + self.attributes('-fullscreen', True) + self.after(100, self.ensure_fullscreen) + + def show_frame(self, frame_class): + """Bring the specified frame to the front""" + frame = self.frames[frame_class] + frame.tkraise() + + def login(self, operator_code): + """Attempt to login with the provided operator code""" + if operator_code in VALID_OPERATORS: + self.current_operator = { + "code": operator_code, + "name": VALID_OPERATORS[operator_code], + "login_time": time.strftime("%Y-%m-%d %H:%M:%S") + } + + # Disable fullscreen toggle for admin users + if operator_code == "123456": # Admin code + self.set_fullscreen_toggle(False) + else: + self.set_fullscreen_toggle(True) + + self.show_frame(DashboardFrame) + self.frames[DashboardFrame].update_operator_info() + return True + return False + + def logout(self): + """Log out and return to login screen""" + self.current_operator = None + self.show_frame(LoginFrame) + + def handle_escape(self, event): + """Handle ESC key press based on toggle variable""" + if self.allow_fullscreen_toggle: + self.toggle_fullscreen() + + def toggle_fullscreen(self): + """Toggle fullscreen mode""" + if self.attributes('-fullscreen'): + self.attributes('-fullscreen', False) + else: + self.attributes('-fullscreen', True) + + def set_fullscreen_toggle(self, enabled): + """Enable or disable the ESC key fullscreen toggle""" + self.allow_fullscreen_toggle = enabled + + def ensure_fullscreen(self): + """Make sure we're in fullscreen mode""" + if not self.attributes('-fullscreen'): + self.attributes('-fullscreen', True) + + def is_admin(self): + """Check if the current user is an admin""" + if self.current_operator and self.current_operator["code"] == "123456": + return True + return False + + +class LoginFrame(tk.Frame): + def __init__(self, parent, controller): + super().__init__(parent, bg="#f0f0f0") + self.controller = controller + + # Create login form + login_frame = tk.Frame(self, bg="#ffffff", padx=30, pady=30) + login_frame.place(relx=0.5, rely=0.5, anchor="center") + + # Title + title_font = font.Font(family="Arial", size=24, weight="bold") + tk.Label(login_frame, text="Label Printing System", font=title_font, bg="#ffffff").pack(pady=(0, 20)) + + # Operator code entry + tk.Label(login_frame, text="Scan Operator Code:", font=("Arial", 14), bg="#ffffff").pack(anchor="w", pady=(10, 5)) + self.code_var = tk.StringVar() + code_entry = tk.Entry(login_frame, textvariable=self.code_var, font=("Arial", 16), width=20, show="*") + code_entry.pack(pady=(0, 20), fill="x") + code_entry.focus_set() + + # Login button + login_btn = tk.Button(login_frame, text="Login", font=("Arial", 14), + bg="#4CAF50", fg="white", padx=20, pady=5, + command=self.login) + login_btn.pack(pady=(10, 5)) + + # Exit button + exit_btn = tk.Button(login_frame, text="Exit", font=("Arial", 14), + bg="#f44336", fg="white", padx=20, pady=5, + command=self.controller.destroy) + exit_btn.pack(pady=(5, 0)) + + # Bind Enter key to login + code_entry.bind("", lambda event: self.login()) + + def login(self): + operator_code = self.code_var.get() + if not operator_code: + messagebox.showerror("Error", "Please enter an operator code") + return + + if self.controller.login(operator_code): + self.code_var.set("") # Clear the entry + else: + messagebox.showerror("Error", "Invalid operator code") + self.code_var.set("") # Clear the entry + + +class DashboardFrame(tk.Frame): + def __init__(self, parent, controller): + super().__init__(parent, bg="#f0f0f0") + self.controller = controller + + # Create header frame + header_frame = tk.Frame(self, bg="#333333", height=60) + header_frame.pack(fill="x") + + # App title + tk.Label(header_frame, text="Label Printing Dashboard", font=("Arial", 16, "bold"), + fg="white", bg="#333333").pack(side="left", padx=20, pady=10) + + # Operator info + self.operator_label = tk.Label(header_frame, text="", font=("Arial", 12), + fg="white", bg="#333333") + self.operator_label.pack(side="right", padx=20, pady=10) + + # Settings button (only visible to admin) + self.settings_btn = tk.Button(header_frame, text="Settings", font=("Arial", 12), + bg="#2196F3", fg="white", padx=10, pady=2, + command=lambda: controller.show_frame(SettingsFrame)) + self.settings_btn.pack(side="right", padx=10, pady=10) + self.settings_btn.pack_forget() # Hide initially + + # Logout button + logout_btn = tk.Button(header_frame, text="Logout", font=("Arial", 12), + bg="#f44336", fg="white", padx=10, pady=2, + command=self.controller.logout) + logout_btn.pack(side="right", padx=10, pady=10) + + # Create main container to hold all three frames + main_container = tk.Frame(self, bg="#f0f0f0", padx=10, pady=10) + main_container.pack(fill="both", expand=True) + + # Configure grid for the main container (2 rows, 2 columns) + main_container.columnconfigure(0, weight=1) + main_container.columnconfigure(1, weight=1) + main_container.rowconfigure(0, weight=2) # Top row larger + main_container.rowconfigure(1, weight=1) # Bottom row smaller + + # 1. Create Label frame (top left) + label_frame = tk.LabelFrame(main_container, text="Create Label", font=("Arial", 14), + bg="#f0f0f0", padx=20, pady=20) + label_frame.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) + + # 2. Available Articles frame (top right) + articles_frame = tk.LabelFrame(main_container, text="Articole disponibile in viitor", + font=("Arial", 14), bg="#f0f0f0", padx=20, pady=20) + articles_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) + + # 3. Last 6 Labels frame (bottom, spans two columns) + history_frame = tk.LabelFrame(main_container, text="Ultimele 6 etichete printate", + font=("Arial", 14), bg="#f0f0f0", padx=20, pady=20) + history_frame.grid(row=1, column=0, columnspan=2, sticky="nsew", padx=5, pady=5) + + # Fill the Create Label frame with existing content + # Text input + tk.Label(label_frame, text="Enter text for label:", font=("Arial", 12), bg="#f0f0f0").pack(anchor="w", pady=(0, 5)) + self.text_var = tk.StringVar() + text_entry = tk.Entry(label_frame, textvariable=self.text_var, font=("Arial", 14), width=40) + text_entry.pack(fill="x", pady=(0, 20)) + text_entry.focus_set() + + # Preview checkbox + self.preview_var = tk.BooleanVar(value=True) + preview_chk = tk.Checkbutton(label_frame, text="Show print preview", variable=self.preview_var, + font=("Arial", 12), bg="#f0f0f0") + preview_chk.pack(anchor="w", pady=(0, 20)) + + # Buttons frame + buttons_frame = tk.Frame(label_frame, bg="#f0f0f0") + buttons_frame.pack(fill="x", pady=10) + + # Print button + print_btn = tk.Button(buttons_frame, text="Print Label", font=("Arial", 14), + bg="#4CAF50", fg="white", padx=20, pady=10, + command=self.print_label) + print_btn.pack(side="left", padx=(0, 10)) + + # Clear button + clear_btn = tk.Button(buttons_frame, text="Clear", font=("Arial", 14), + bg="#ff9800", fg="white", padx=20, pady=10, + command=lambda: self.text_var.set("")) + clear_btn.pack(side="left") + + # Placeholder message for the other frames (to be filled later) + tk.Label(articles_frame, text="Future articles will be displayed here", + font=("Arial", 12), bg="#f0f0f0").pack(expand=True) + + tk.Label(history_frame, text="Last 6 printed labels will be displayed here", + font=("Arial", 12), bg="#f0f0f0").pack(expand=True) + + # Status frame at the bottom + status_frame = tk.Frame(self, bg="#e0e0e0", height=30) + status_frame.pack(fill="x", side="bottom") + + self.status_label = tk.Label(status_frame, text="Ready", font=("Arial", 10), bg="#e0e0e0") + self.status_label.pack(side="left", padx=10, pady=5) + + def update_operator_info(self): + """Update the operator information displayed in the header""" + if self.controller.current_operator: + operator = self.controller.current_operator + self.operator_label.config(text=f"Operator: {operator['name']} ({operator['code']})") + + # Show settings button only for admin + if self.controller.is_admin(): + self.settings_btn.pack(side="right", padx=10, pady=10) + else: + self.settings_btn.pack_forget() + + def print_label(self): + """Print a label with the entered text""" + text = self.text_var.get().strip() + if not text: + messagebox.showerror("Error", "Please enter text for the label") + return + + # Get saved printer or available printers + saved_printer = get_saved_printer() + + if saved_printer: + printer = saved_printer + else: + # Get available printers + try: + printers = get_printers() + if not printers: + messagebox.showerror("Error", "No printers found") + return + except Exception as e: + messagebox.showerror("Error", f"Could not get printers: {str(e)}") + return + + # Select printer + printer = select_printer(printers) + if not printer: + return # User cancelled + + # Create and print label + try: + self.status_label.config(text="Creating label...") + self.update_idletasks() + + label_img = create_label_image(text) + label_img.save('final_label.png') + + if self.preview_var.get(): + self.status_label.config(text="Showing preview...") + self.update_idletasks() + show_preview('final_label.png', lambda: self.complete_print(printer, text)) + else: + self.complete_print(printer, text) + + except Exception as e: + messagebox.showerror("Error", f"Error creating label: {str(e)}") + self.status_label.config(text="Ready") + + def complete_print(self, printer, text): + """Complete the print job after preview (if shown)""" + try: + self.status_label.config(text="Printing...") + self.update_idletasks() + + print_label(printer, text) + + self.status_label.config(text="Label printed successfully") + show_auto_close_info("Success", "Label sent to printer", timeout=2000) + + # Clear text field after successful print + self.text_var.set("") + + except Exception as e: + messagebox.showerror("Error", f"Error printing label: {str(e)}") + finally: + self.status_label.config(text="Ready") + + +class SettingsFrame(tk.Frame): + def __init__(self, parent, controller): + super().__init__(parent, bg="#f0f0f0") + self.controller = controller + + # Create header frame + header_frame = tk.Frame(self, bg="#333333", height=60) + header_frame.pack(fill="x") + + # App title + tk.Label(header_frame, text="Settings", font=("Arial", 16, "bold"), + fg="white", bg="#333333").pack(side="left", padx=20, pady=10) + + # Back button + back_btn = tk.Button(header_frame, text="Back to Dashboard", font=("Arial", 12), + bg="#4CAF50", fg="white", padx=10, pady=2, + command=lambda: controller.show_frame(DashboardFrame)) + back_btn.pack(side="right", padx=10, pady=10) + + # Create main content frame + content_frame = tk.Frame(self, bg="#f0f0f0", padx=20, pady=20) + content_frame.pack(fill="both", expand=True) + + # Printer settings section + printer_frame = tk.LabelFrame(content_frame, text="Printer Settings", font=("Arial", 14), bg="#f0f0f0", padx=20, pady=20) + printer_frame.pack(fill="x", expand=False, padx=20, pady=20) + + # Printer selection + tk.Label(printer_frame, text="Select Default Printer:", font=("Arial", 12), bg="#f0f0f0").pack(anchor="w", pady=(0, 5)) + + # Get available printers + try: + self.printers = get_printers() + self.printer_var = tk.StringVar() + + # Load saved printer if available + saved_printer = load_printer_config() + if saved_printer and saved_printer in self.printers: + self.printer_var.set(saved_printer) + elif self.printers: + self.printer_var.set(self.printers[0]) + + combo = ttk.Combobox(printer_frame, textvariable=self.printer_var, values=self.printers, + state="readonly", font=("Arial", 12), width=40) + combo.pack(pady=(0, 20), fill="x") + + # Save button + save_btn = tk.Button(printer_frame, text="Save Printer Setting", font=("Arial", 12), + bg="#4CAF50", fg="white", padx=10, pady=5, + command=self.save_printer_config) + save_btn.pack(pady=10) + + except Exception as e: + tk.Label(printer_frame, text=f"Error loading printers: {str(e)}", font=("Arial", 12), + fg="red", bg="#f0f0f0").pack(pady=10) + + def save_printer_config(self): + """Save the selected printer to a configuration file""" + selected_printer = self.printer_var.get() + if selected_printer: + try: + with open('printer_config.txt', 'w') as f: + f.write(selected_printer) + messagebox.showinfo("Success", "Printer setting saved successfully") + except Exception as e: + messagebox.showerror("Error", f"Could not save printer setting: {str(e)}") + + +# Your existing functions remain largely the same def get_printers(): conn = cups.Connection() return list(conn.getPrinters().keys()) def create_label_image(text): + # Your existing create_label_image function (unchanged) # Label dimensions for 9x5 cm at 300 DPI label_width = 1063 # 9 cm label_height = 591 # 5 cm @@ -120,42 +525,64 @@ def show_preview(image_path, on_print): 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) + dialog.geometry("400x250") # Slightly larger for better visibility + dialog.configure(bg="#f0f0f0") # Match app background + dialog.resizable(False, False) + + # Create a distinctive border + frame = tk.Frame(dialog, bd=2, relief="ridge", bg="white", padx=20, pady=20) + frame.pack(fill="both", expand=True, padx=10, pady=10) + + tk.Label(frame, text="Select a printer:", font=("Arial", 14, "bold"), bg="white").pack(padx=10, pady=10) + + printer_var = tk.StringVar(value=printers[0] if printers else "") # Default to first printer + combo = ttk.Combobox(frame, textvariable=printer_var, values=printers, state="readonly", font=("Arial", 12)) + combo.pack(padx=10, pady=20, fill="x") + + button_frame = tk.Frame(frame, bg="white") + button_frame.pack(pady=10, fill="x") + 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() + + def on_cancel(): dialog.destroy() - btn = tk.Button(dialog, text="OK", command=on_ok) - btn.pack(pady=10) - entry.focus() + + # Auto-select timeout + def auto_select(): + if not selected['printer'] and printers: + selected['printer'] = printers[0] + dialog.destroy() + + ok_btn = tk.Button(button_frame, text="OK", command=on_ok, font=("Arial", 12), + bg="#4CAF50", fg="white", width=8) + ok_btn.pack(side="left", padx=(50, 10)) + + tk.Button(button_frame, text="Cancel", command=on_cancel, font=("Arial", 12), + bg="#f44336", fg="white", width=8).pack(side="left") + + # Ensure dialog is on top and focused + dialog.attributes('-topmost', True) + dialog.update() + dialog.attributes('-topmost', False) + + # Auto-select after 15 seconds if no choice made + dialog.after(15000, auto_select) + + # Set focus to the OK button + dialog.after(100, lambda: ok_btn.focus_set()) + dialog.grab_set() dialog.wait_window() - return result['text'], result['preview'] + + # Return default printer if none selected + if not selected['printer'] and printers: + return printers[0] + + return selected['printer'] def show_auto_close_info(title, message, timeout=3000): info = tk.Toplevel() @@ -167,33 +594,27 @@ def show_auto_close_info(title, message, timeout=3000): 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 +def load_printer_config(): + """Load the saved printer from configuration file""" + try: + if os.path.exists('printer_config.txt'): + with open('printer_config.txt', 'r') as f: + return f.read().strip() + except Exception: + pass + return None - 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) +def get_saved_printer(): + """Get the saved printer from configuration""" + saved_printer = load_printer_config() + if saved_printer: + # Verify printer still exists + available_printers = get_printers() + if saved_printer in available_printers: + return saved_printer + return None +# Replace your main() function with this if __name__ == "__main__": - main() \ No newline at end of file + app = LabelApp() + app.mainloop() \ No newline at end of file diff --git a/final_label.png b/final_label.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3405672582e3e4eb9b4d93c07a055969fbfc17 GIT binary patch literal 16752 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 literal 0 HcmV?d00001 diff --git a/print_label.py b/print_label.py new file mode 100644 index 0000000..daacf9d --- /dev/null +++ b/print_label.py @@ -0,0 +1,217 @@ +from PIL import Image, ImageTk, ImageDraw, ImageFont +import barcode +from barcode.writer import ImageWriter +import cups, time, os +import tkinter as tk # Add this explicitly at the top + +value = "A04444" +printer = "PDF" +preview = 2 + +def create_label_image(text): + """ + Create a label image with barcode and text. + + Args: + text (str): The text to encode in the barcode and display + + Returns: + PIL.Image: The generated label image + """ + # 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) # Clean up temporary barcode file + return label_img + +def print_label_standalone(value, printer, preview=0): + """ + Print a label with the specified text on the specified printer. + + Args: + value (str): The text to print on the label + printer (str): The name of the printer to use + preview (int): 0 = no preview, 1-3 = 3s preview, >3 = 5s preview + + Returns: + bool: True if printing was successful, False otherwise + """ + try: + # Debug output + print(f"Preview value: {preview}") + print(f"Preview type: {type(preview)}") + + # Create the label image + label_img = create_label_image(value) + label_img.save('final_label.png') + + # Convert preview to int if it's a string + if isinstance(preview, str): + preview = int(preview) + + if preview > 0: # Any value above 0 shows a preview + print("Showing preview window...") + # Calculate preview duration in milliseconds + if 1 <= preview <= 3: + preview_ms = 3000 # 3 seconds + else: # preview > 3 + preview_ms = 5000 # 5 seconds + + # Create a Tkinter window for preview - simpler approach + root = tk.Tk() + root.withdraw() # Hide the main window + preview_window = tk.Toplevel(root) + preview_window.title("Label Preview") + preview_window.geometry("1063x691") # A bit taller to accommodate buttons + + # Track if printing was done + printed = False + + # Function to print and close the preview + def do_print(): + nonlocal printed + print("Printing from preview...") + conn = cups.Connection() + conn.printFile(printer, 'final_label.png', "Label Print", {}) + printed = True + preview_window.destroy() + + # Function to close without printing + def do_cancel(): + preview_window.destroy() + + # Display the image + img = Image.open('final_label.png') + img_tk = ImageTk.PhotoImage(img) + label = tk.Label(preview_window, image=img_tk) + label.image = img_tk # Keep reference + label.pack(pady=10) + + # Add a timer label + timer_text = f"Auto-printing in {preview_ms//1000} seconds..." + timer_label = tk.Label(preview_window, text=timer_text, font=("Arial", 10)) + timer_label.pack(pady=(0, 5)) + + # Button frame + btn_frame = tk.Frame(preview_window) + btn_frame.pack(pady=10) + + # Add print and cancel buttons + print_btn = tk.Button(btn_frame, text="Print Now", command=do_print, + font=("Arial", 12), bg="#4CAF50", fg="white", padx=20, pady=5) + print_btn.pack(side="left", padx=10) + + cancel_btn = tk.Button(btn_frame, text="Cancel", command=do_cancel, + font=("Arial", 12), bg="#f44336", fg="white", padx=20, pady=5) + cancel_btn.pack(side="left", padx=10) + + # Auto-print after the specified time + print(f"Setting auto-print timer for {preview_ms}ms") + preview_window.after(preview_ms, do_print) + + # Make sure the window stays on top + preview_window.attributes('-topmost', True) + preview_window.update() + preview_window.attributes('-topmost', False) + + # Wait for the window to close + root.mainloop() + + if not printed: + # User cancelled, clean up and return False + os.remove('final_label.png') + return False + else: + print("Direct printing without preview...") + # Direct printing without preview (preview = 0) + conn = cups.Connection() + conn.printFile(printer, 'final_label.png', "Label Print", {}) + + # Clean up + os.remove('final_label.png') + return True + + except Exception as e: + print(f"Error printing label: {str(e)}") + # Try to clean up if the file exists + if os.path.exists('final_label.png'): + os.remove('final_label.png') + return False + +# Test the function +print_label_standalone(value, printer, preview) diff --git a/printer_config.txt b/printer_config.txt new file mode 100644 index 0000000..03e590d --- /dev/null +++ b/printer_config.txt @@ -0,0 +1 @@ +PDF \ No newline at end of file