diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..cb42f24 --- /dev/null +++ b/gui.py @@ -0,0 +1,188 @@ +import customtkinter as ctk +import subprocess +import threading +import sys +import os +from PIL import Image, ImageTk +import webbrowser + +class CustomerThinkerApp(ctk.CTk): + def __init__(self): + super().__init__() + + self.title("CustomerThinker PDF Export Tool") + self.geometry("500x500") + self.resizable(False, False) + ctk.set_appearance_mode("System") # "Dark", "Light", "System" + ctk.set_default_color_theme("blue") # Andere Themes: "green", "dark-blue" + + self.script_path = "pdf_export.py" + self.labels_dir = "label" + self.orders_dir = "orders" + + self.frames = [] + self.current_gif_frame = 0 + self.animation_running = False + + self.build_gui() + self.load_gif() + self.update_file_dropdown() + + def build_gui(self): + # Titel + self.title_label = ctk.CTkLabel(self, text="PDF Export Tool", font=ctk.CTkFont(size=24, weight="bold")) + self.title_label.pack(pady=20) + + # Button + self.run_button = ctk.CTkButton(self, text="Alle Aufträge drucken", command=self.run_script) + self.run_button.pack(pady=10) + + # Auswahl: Letzte Labels oder Orders + self.dropdown_select = ctk.CTkComboBox(self, values=["Letzte Labels", "Letzte Orders"], + command=self.update_file_dropdown, width=200) + self.dropdown_select.set("Auswählen") + self.dropdown_select.pack(pady=10) + + # Werte-Dropdown + self.dropdown_values = ctk.CTkComboBox(self, values=[], command=self.file_selected,width=200) + self.dropdown_values.pack(pady=10) + + # Status + self.status_label = ctk.CTkLabel(self, text="", font=ctk.CTkFont(size=16)) + self.status_label.pack(pady=20) + + # Status Animation + self.status_label_logo = ctk.CTkLabel(self, text="") + self.status_label_logo.pack(pady=10) + + # Logo unten rechts + self.logo_image = ctk.CTkImage(Image.open("logo.png"), size=(50, 50)) # Hier dein Logo + self.logo_label = ctk.CTkLabel(self, image=self.logo_image, text="", fg_color="transparent") + self.logo_label.bind("", self.open_link) # Klick-Event + self.logo_label.bind("", self.on_logo_hover) # Hover-Ereignis (Maus über Logo) + self.logo_label.bind("", self.on_logo_leave) # Wenn die Maus das Logo verlässt + self.logo_label.place(x=0, y=0, anchor="se") # Vorläufige Position setzen + + # Configure-Event um das Logo nach dem Rendern richtig zu platzieren + self.bind("", self.update_logo_position) + + def open_link(self, event=None): + webbrowser.open("https://www.itdata-gera.de") # Ändere dies auf die gewünschte URL + + def on_logo_hover(self, event=None): + self.logo_label.configure(cursor="hand2") # Cursor auf Hand setzen, wenn Maus über dem Logo schwebt + + def on_logo_leave(self, event=None): + self.logo_label.configure(cursor="") # Setzt den Cursor zurück, wenn die Maus das Logo verlässt + + def update_logo_position(self, event=None): + # Positioniere das Logo in der unteren rechten Ecke, nachdem das Fenster die endgültige Größe hat + self.logo_label.place(x=self.winfo_width() - 10, y=self.winfo_height() - 10, anchor="se") + + def load_gif(self): + gif_path = "printer.gif" + if not os.path.exists(gif_path): + return + + try: + gif = Image.open(gif_path) + while True: + frame = ImageTk.PhotoImage(gif.copy()) + self.frames.append(frame) + gif.seek(len(self.frames)) + except EOFError: + pass + + def load_files(self, directory): + try: + return [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))] + except FileNotFoundError: + return [] + + def update_file_dropdown(self, event=None): + selected_option = self.dropdown_select.get() + directory = self.labels_dir if selected_option == "Letzte Labels" else self.orders_dir + files = self.load_files(directory) + + if files: + self.dropdown_values.configure(values=files) + self.dropdown_values.set(files[0]) + else: + self.dropdown_values.configure(values=[]) + self.dropdown_values.set("") + + self.run_button.configure(text="Alle Aufträge drucken", command=self.run_script) + + def file_selected(self, event=None): + selected_file = self.dropdown_values.get() + if not selected_file: + return + selected_folder = self.labels_dir if self.dropdown_select.get() == "Letzte Labels" else self.orders_dir + full_path = os.path.join(selected_folder, selected_file) + + self.run_button.configure(text=f"{selected_file} drucken", command=lambda: self.run_single_file(full_path)) + + def run_script(self): + self.set_status("Alle Aufträge werden gedruckt...", "blue") + self.start_animation() + threading.Thread(target=self.execute_script).start() + + def run_single_file(self, full_path): + filename = os.path.basename(full_path) + self.set_status(f"{filename} wird gedruckt...", "blue") + self.start_animation() + threading.Thread(target=self.execute_single_file, args=(full_path,)).start() + + def execute_script(self): + try: + result = subprocess.run([sys.executable, self.script_path], capture_output=True, text=True) + if result.returncode == 0: + self.set_status("✅ Alle Aufträge wurden gedruckt!", "green") + else: + self.set_status("❌ Fehler beim Ausführen des Skripts.", "red") + self.show_error(result.stderr) + except Exception as e: + self.set_status("❌ Unerwarteter Fehler.", "red") + self.show_error(str(e)) + finally: + self.stop_animation() + + def execute_single_file(self, full_path): + try: + result = subprocess.run([sys.executable, self.script_path, full_path], capture_output=True, text=True) + if result.returncode == 0: + filename = os.path.basename(full_path) + self.set_status(f"✅ {filename} wurde gedruckt!", "green") + else: + self.set_status("❌ Fehler beim Drucken.", "red") + self.show_error(result.stderr) + except Exception as e: + self.set_status("❌ Unerwarteter Fehler.", "red") + self.show_error(str(e)) + finally: + self.stop_animation() + self.run_button.configure(text="Alle Aufträge drucken", command=self.run_script) + + def set_status(self, text, color): + self.status_label.configure(text=text, text_color=color) + + def start_animation(self): + self.animation_running = True + self.animate() + + def stop_animation(self): + self.animation_running = False + + def animate(self): + if self.animation_running and self.frames: + frame = self.frames[self.current_gif_frame] + self.status_label_logo.configure(image=frame) + self.current_gif_frame = (self.current_gif_frame + 1) % len(self.frames) + self.after(30, self.animate) + + def show_error(self, message): + ctk.CTkMessagebox(title="Fehler", message=message, icon="cancel") if hasattr(ctk, "CTkMessagebox") else print(message) + +if __name__ == "__main__": + app = CustomerThinkerApp() + app.mainloop() diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..9668816 Binary files /dev/null and b/logo.png differ diff --git a/pdf_export.py b/pdf_export.py index 92d3b32..3f3cccb 100644 --- a/pdf_export.py +++ b/pdf_export.py @@ -1,13 +1,13 @@ import os +import sys +import time import requests from dotenv import load_dotenv -from datetime import datetime +from datetime import datetime, timedelta import fitz # PyMuPDF import win32print import win32ui from PIL import Image, ImageWin -import tempfile -import time # .env laden load_dotenv() @@ -17,6 +17,112 @@ url = os.getenv('API_URL') label_printer = os.getenv('LABEL_DRUCKER') # Name des Labels-Druckers order_printer = os.getenv('ORDER_DRUCKER') # Name des Order-Druckers +# Log-Dateipfad +log_dir = "logs" +os.makedirs(log_dir, exist_ok=True) + +def get_log_filename(): + """Erstellt einen Dateinamen für das Log basierend auf dem aktuellen Datum.""" + return os.path.join(log_dir, f"{datetime.now().strftime('%Y-%m-%d')}.log") + +def setup_logging(): + """Erstellt eine Log-Datei und löscht alte Log-Dateien, die älter als 7 Tage sind.""" + # Logdatei für den aktuellen Tag + log_file = get_log_filename() + + # Log-Datei öffnen + sys.stdout = open(log_file, 'a') + sys.stderr = sys.stdout + + # Alte Log-Dateien löschen (älter als 7 Tage) + delete_old_logs() + +def delete_old_logs(): + """Löscht Log-Dateien, die älter als 7 Tage sind.""" + for filename in os.listdir(log_dir): + file_path = os.path.join(log_dir, filename) + if os.path.isfile(file_path): + file_age = datetime.now() - datetime.fromtimestamp(os.path.getmtime(file_path)) + if file_age > timedelta(days=7): + os.remove(file_path) + print(f"Altes Log gelöscht: {filename}") + +def log_message(message): + """Schreibt eine Nachricht ins Log.""" + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}") + +# Verbesserte Druckfunktion +def print_pdf(file_path, printer_name): + if not os.path.exists(file_path): + log_message(f'Datei nicht gefunden: {file_path}') + return False + + try: + hprinter = win32print.OpenPrinter(printer_name) + hdc = win32ui.CreateDC() + hdc.CreatePrinterDC(printer_name) + hdc.StartDoc(file_path) + + pdf_document = fitz.open(file_path) + printer_size = hdc.GetDeviceCaps(8), hdc.GetDeviceCaps(10) # Horizontale/Vertikale Größe in Pixel + + for page_number in range(len(pdf_document)): + page = pdf_document.load_page(page_number) + + zoom = 3.0 + mat = fitz.Matrix(zoom, zoom) + pix = page.get_pixmap(matrix=mat, alpha=False) + + image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) + dib = ImageWin.Dib(image) + + scale_x = printer_size[0] / pix.width + scale_y = printer_size[1] / pix.height + scale = min(scale_x, scale_y) + + x_pos = int((printer_size[0] - (pix.width * scale)) // 2) + y_pos = int((printer_size[1] - (pix.height * scale)) // 2) + width = int(pix.width * scale) + height = int(pix.height * scale) + + hdc.StartPage() + try: + dib.draw(hdc.GetHandleOutput(), (x_pos, y_pos, x_pos + width, y_pos + height)) + except Exception as draw_error: + log_message(f"Zeichenfehler: {draw_error}") + dib.draw(hdc.GetHandleOutput(), (0, 0, printer_size[0], printer_size[1])) + hdc.EndPage() + + hdc.EndDoc() + hdc.DeleteDC() + win32print.ClosePrinter(hprinter) + log_message(f"Erfolgreich gedruckt: {file_path}") + return True + + except Exception as e: + log_message(f"Fehler beim Drucken von {file_path}: {str(e)}") + import traceback + traceback.print_exc() + return False + +# Debug-Funktion aktivieren +setup_logging() + +# Einzeldatei-Druck prüfen +if len(sys.argv) > 1: + file_to_print = sys.argv[1] + + if "label" in file_to_print.lower(): + printer = label_printer + else: + printer = order_printer + + log_message(f"Einzeldatei-Modus: {file_to_print} → {printer}") + print_pdf(file_to_print, printer) + sys.exit(0) + +# KEIN Dateiname angegeben → Standard-Workflow (Download, Aufteilen, Drucken) + # Zeitstempel für die Dateien timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') @@ -37,12 +143,12 @@ if url: file_name = f'{original_storage_path}/original_{timestamp}.pdf' with open(file_name, 'wb') as file: file.write(response.content) - print(f'PDF erfolgreich heruntergeladen: "{file_name}"') + log_message(f'PDF erfolgreich heruntergeladen: "{file_name}"') else: - print(f'Fehler beim Herunterladen. Statuscode: {response.status_code}') + log_message(f'Fehler beim Herunterladen. Statuscode: {response.status_code}') exit(1) else: - print('API-URL nicht in .env gefunden.') + log_message('API-URL nicht in .env gefunden.') exit(1) # Dokument verarbeiten @@ -94,97 +200,30 @@ if order_end_page is not None and order_end_page >= 0: order_doc.insert_pdf(doc, from_page=0, to_page=order_end_page) order_filename = f"{order_storage_path}/order_{timestamp}.pdf" order_doc.save(order_filename) - print(f"Bestellung gespeichert als {order_filename}") + log_message(f"Bestellung gespeichert als {order_filename}") # Labels speichern for label, docs in label_sections.items(): for idx, pdf in enumerate(docs, start=1): filename = f"{label_storage_path}/{label}_label_{idx}_{timestamp}.pdf" pdf.save(filename) - print(f"{label} Label gespeichert als {filename}") - - -# Verbesserte Druckfunktion mit reinen Python-Mitteln -def print_pdf(file_path, printer_name): - if not os.path.exists(file_path): - print(f'Datei nicht gefunden: {file_path}') - return False - - try: - # Drucker initialisieren - hprinter = win32print.OpenPrinter(printer_name) - hdc = win32ui.CreateDC() - hdc.CreatePrinterDC(printer_name) - hdc.StartDoc(file_path) - - # PDF öffnen - pdf_document = fitz.open(file_path) - - # Druckereinstellungen für korrekte Skalierung - printer_size = hdc.GetDeviceCaps(8), hdc.GetDeviceCaps(10) # Horizontale/Vertikale Größe in Pixel - printer_margins = (0, 0) # Keine Ränder - - for page_number in range(len(pdf_document)): - page = pdf_document.load_page(page_number) - - # Höhere Qualität für Labels - zoom = 3.0 # Erhöhter Zoom für bessere Qualität - mat = fitz.Matrix(zoom, zoom) - pix = page.get_pixmap(matrix=mat, alpha=False) - - # Bild vorbereiten - image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) - dib = ImageWin.Dib(image) - - # Skalierung berechnen, um das ganze Bild einzupassen - scale_x = printer_size[0] / pix.width - scale_y = printer_size[1] / pix.height - scale = min(scale_x, scale_y) - - # Zentrierte Positionierung - x_pos = int((printer_size[0] - (pix.width * scale)) // 2) - y_pos = int((printer_size[1] - (pix.height * scale)) // 2) - width = int(pix.width * scale) - height = int(pix.height * scale) - - # Seite drucken - hdc.StartPage() - try: - dib.draw(hdc.GetHandleOutput(), - (x_pos, y_pos, x_pos + width, y_pos + height)) - except Exception as draw_error: - print(f"Zeichenfehler: {draw_error}") - # Fallback: Einfache Skalierung - dib.draw(hdc.GetHandleOutput(), (0, 0, printer_size[0], printer_size[1])) - hdc.EndPage() - - hdc.EndDoc() - hdc.DeleteDC() - win32print.ClosePrinter(hprinter) - print(f"Erfolgreich gedruckt: {file_path}") - return True - - except Exception as e: - print(f"Fehler beim Drucken von {file_path}: {str(e)}") - import traceback - traceback.print_exc() - return False + log_message(f"{label} Label gespeichert als {filename}") # Druckvorgang -print("\nStarte Druckvorgang:") +log_message("\nStarte Druckvorgang:") # Labels drucken for label in label_sections: for idx, pdf in enumerate(label_sections[label], start=1): label_filename = f"{label_storage_path}/{label}_label_{idx}_{timestamp}.pdf" - print(f"\nDrucke {label} Label {idx}...") + log_message(f"Drucke {label} Label {idx}...") print_pdf(label_filename, label_printer) time.sleep(1) # Kurze Pause zwischen den Druckaufträgen # Bestellung drucken if order_end_page is not None: - print("\nDrucke Bestellung...") + log_message("\nDrucke Bestellung...") order_filename = f"{order_storage_path}/order_{timestamp}.pdf" print_pdf(order_filename, order_printer) -print("\nDruckvorgang abgeschlossen.") \ No newline at end of file +log_message("\nDruckvorgang abgeschlossen.") diff --git a/printer.gif b/printer.gif new file mode 100644 index 0000000..ecd6f1e Binary files /dev/null and b/printer.gif differ