import tkinter as tk from tkinter import ttk, filedialog, messagebox import PyLibrary.funciones_comunes as fc import x1_importar_to_master import x2_master_export2translate import x3_llm_auto_translate import x4_integrate_translates_to_master import x4B_integrate_manual_translates_to_master import x5_complete_empty_cells_master import x6_update_from_master from translation_config import TranslationConfig import sys import threading import os import queue class RedirectText: def __init__(self, queue): self.queue = queue def write(self, string): self.queue.put(string) def flush(self): pass class TranslationApp: def __init__(self, master): self.master = master self.master.title("Ayuda para traducir textos de TIA Portal y Allen Bradley") self.master.geometry("1200x800") self.config = TranslationConfig.load() self.create_variables() self.create_widgets() self.place_widgets() self.queue = queue.Queue() self.update_output() # Cargar el log existente al iniciar self.load_existing_log() def create_variables(self): self.codigo_tipo_PLC_var = tk.StringVar(value=self.config.codigo_tipo_PLC) self.codigo_columna_maestra_var = tk.StringVar( value=self.config.codigo_columna_maestra ) self.codigo_idioma_var = tk.StringVar( value=self.config.codigo_idioma_seleccionado ) self.codigo_idioma_var2 = tk.StringVar( value=self.config.codigo_idioma_secundario ) self.work_dir_var = tk.StringVar(value=self.config.work_dir) self.master_name_var = tk.StringVar(value=self.config.master_name) self.translate_name_var = tk.StringVar(value=self.config.translate_name) self.auto_translate_name_var = tk.StringVar( value=self.config.auto_translate_name ) def create_widgets(self): self.main_frame = ttk.Frame(self.master) self.main_frame.grid(row=0, column=0, sticky="nsew") self.master.grid_rowconfigure(0, weight=1) self.master.grid_columnconfigure(0, weight=1) self.frame_combos = ttk.Frame(self.main_frame) self.label_tipo_PLC = ttk.Label( self.frame_combos, text="Selecciona el tipo de PLC:" ) self.combo_tipo_PLC = ttk.Combobox(self.frame_combos, state="readonly") self.combo_tipo_PLC["values"] = [nombre for nombre, _ in fc.PLCs.values()] self.combo_tipo_PLC.bind("<>", self.update_tipo_PLC) self.set_combo_value(self.combo_tipo_PLC, self.codigo_tipo_PLC_var.get()) self.label_codigo_maestra = ttk.Label( self.frame_combos, text="Idioma Columna Maestra:" ) self.combo_codigo_maestra = ttk.Combobox(self.frame_combos, state="readonly") self.combo_codigo_maestra["values"] = [ nombre for nombre, _ in fc.IDIOMAS.values() ] self.combo_codigo_maestra.bind( "<>", self.update_codigo_maestra ) self.set_combo_value( self.combo_codigo_maestra, self.codigo_columna_maestra_var.get() ) self.label1 = ttk.Label(self.frame_combos, text="Idioma de Traducción:") self.combo = ttk.Combobox(self.frame_combos, state="readonly") self.combo["values"] = [nombre for nombre, _ in fc.IDIOMAS.values()] self.combo.bind("<>", self.update_idioma) self.set_combo_value(self.combo, self.codigo_idioma_var.get()) self.label2 = ttk.Label(self.frame_combos, text="Selecciona segundo idioma:") self.combo2 = ttk.Combobox(self.frame_combos, state="readonly") self.combo2["values"] = [nombre for nombre, _ in fc.IDIOMAS.values()] self.combo2.bind("<>", self.update_idioma2) self.set_combo_value(self.combo2, self.codigo_idioma_var2.get()) self.work_dir_frame = ttk.Frame(self.main_frame) self.work_dir_label = ttk.Label( self.work_dir_frame, text="Directorio de trabajo:" ) self.work_dir_entry = ttk.Entry( self.work_dir_frame, textvariable=self.work_dir_var, width=50 ) self.work_dir_button = ttk.Button( self.work_dir_frame, text="Seleccionar", command=self.select_work_dir ) self.open_explorer_button = ttk.Button( self.work_dir_frame, text="Abrir Explorador", command=self.open_explorer ) style = ttk.Style() style.configure("TButton", padding=(10, 10), font=("Helvetica", 10, "bold")) style.configure( "Excel.TButton", padding=(10, 10), font=("Helvetica", 10), background="#69cf81", ) self.button_width = 60 self.entry_width = 50 self.paso1 = ttk.Button( self.main_frame, text="1 - Importar al Master", command=lambda: self.ejecutar_run(x1_importar_to_master.run), width=self.button_width, ) self.master_name_button = ttk.Button( self.main_frame, textvariable=self.master_name_var, command=lambda: self.open_file(self.config.get_master_path()), width=self.entry_width, style="Excel.TButton", ) self.paso2 = ttk.Button( self.main_frame, text="2 - Exportar Idioma a 2_master_export2translate.xlsx", command=lambda: self.ejecutar_run(x2_master_export2translate.run), width=self.button_width, ) self.translate_name_button = ttk.Button( self.main_frame, textvariable=self.translate_name_var, command=lambda: self.open_file(self.config.get_translate_path()), width=self.entry_width, style="Excel.TButton", ) self.paso3 = ttk.Button( self.main_frame, text="3 - Traducir y generar 3_master_export2translate_translated.xlsx", command=lambda: self.ejecutar_run(x3_llm_auto_translate.run), width=self.button_width, ) self.auto_translate_name_button = ttk.Button( self.main_frame, textvariable=self.auto_translate_name_var, command=lambda: self.open_file(self.config.get_auto_translate_path()), width=self.entry_width, style="Excel.TButton", ) self.paso4 = ttk.Button( self.main_frame, text="4 - Integrar las traducciones al 1_hmi_master_translates", command=lambda: self.ejecutar_run(x4_integrate_translates_to_master.run), width=self.button_width, ) self.paso4B = ttk.Button( self.main_frame, text="4B - Integrar traducciones manuales al 1_hmi_master_translates", command=lambda: self.ejecutar_run( x4B_integrate_manual_translates_to_master.run ), width=self.button_width, ) self.paso5 = ttk.Button( self.main_frame, text="5 - Completar en 1_hmi_master_translates el idioma seleccionado usando el segundo idioma", command=lambda: self.ejecutar_run(x5_complete_empty_cells_master.run), width=self.button_width, ) self.paso6 = ttk.Button( self.main_frame, text="6 - Exportar usando un archivo exportado desde TIA Portal usando 1_hmi_master_translates", command=lambda: self.ejecutar_run(x6_update_from_master.run), width=self.button_width, ) self.output_text = tk.Text(self.main_frame, wrap="none", height=20) self.scrollbar_y = ttk.Scrollbar( self.main_frame, orient="vertical", command=self.output_text.yview ) self.scrollbar_x = ttk.Scrollbar( self.main_frame, orient="horizontal", command=self.output_text.xview ) self.output_text.configure( yscrollcommand=self.scrollbar_y.set, xscrollcommand=self.scrollbar_x.set ) self.clear_button = ttk.Button( self.main_frame, text="Limpiar salida", command=self.clear_output ) self.open_log_button = ttk.Button( self.main_frame, text="Abrir Log", command=self.open_log_file ) def place_widgets(self): self.main_frame.grid_columnconfigure(0, weight=1) self.main_frame.grid_columnconfigure(1, weight=1) self.frame_combos.grid(row=0, column=0, columnspan=2, pady=10, sticky="ew") self.label_tipo_PLC.grid(row=0, column=0, padx=5, pady=5, sticky="e") self.combo_tipo_PLC.grid(row=0, column=1, padx=5, pady=5, sticky="w") self.label_codigo_maestra.grid(row=1, column=0, padx=5, pady=5, sticky="e") self.combo_codigo_maestra.grid(row=1, column=1, padx=5, pady=5, sticky="w") self.label1.grid(row=0, column=2, padx=5, pady=5, sticky="e") self.combo.grid(row=0, column=3, padx=5, pady=5, sticky="w") self.label2.grid(row=1, column=2, padx=5, pady=5, sticky="e") self.combo2.grid(row=1, column=3, padx=5, pady=5, sticky="w") self.work_dir_frame.grid(row=1, column=0, columnspan=2, pady=10, sticky="ew") self.work_dir_label.grid(row=0, column=0, padx=5, sticky="w") self.work_dir_entry.grid(row=0, column=1, padx=5, sticky="ew") self.work_dir_button.grid(row=0, column=2, padx=5, sticky="e") self.open_explorer_button.grid(row=0, column=3, padx=5, sticky="e") self.work_dir_frame.grid_columnconfigure(1, weight=1) current_row = 2 self.paso1.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") self.master_name_button.grid( row=current_row, column=1, pady=5, padx=5, sticky="ew" ) current_row += 1 self.paso2.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") self.translate_name_button.grid( row=current_row, column=1, pady=5, padx=5, sticky="ew" ) current_row += 1 self.paso3.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") self.auto_translate_name_button.grid( row=current_row, column=1, pady=5, padx=5, sticky="ew" ) current_row += 1 self.paso4.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") current_row += 1 self.paso4B.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") current_row += 1 self.paso5.grid( row=current_row, column=0, columnspan=2, pady=5, padx=5, sticky="ew" ) current_row += 1 self.paso6.grid( row=current_row, column=0, columnspan=2, pady=5, padx=5, sticky="ew" ) current_row += 1 self.output_text.grid( row=current_row, column=0, columnspan=2, padx=(10, 0), pady=10, sticky="nsew", ) self.scrollbar_y.grid(row=current_row, column=2, pady=10, sticky="ns") self.scrollbar_x.grid( row=current_row + 1, column=0, columnspan=2, padx=10, sticky="ew" ) current_row += 2 self.clear_button.grid(row=current_row, column=0, pady=10, padx=5, sticky="e") self.open_log_button.grid( row=current_row, column=1, pady=10, padx=5, sticky="w" ) self.main_frame.grid_rowconfigure(current_row - 2, weight=1) def set_combo_value(self, combo, codigo): for nombre, code in fc.IDIOMAS.values(): if code == codigo: combo.set(nombre) return for nombre, code in fc.PLCs.values(): if code == codigo: combo.set(nombre) return def update_tipo_PLC(self, event): nombre_seleccionado = self.combo_tipo_PLC.get() for nombre, codigo in fc.PLCs.values(): if nombre == nombre_seleccionado: self.codigo_tipo_PLC_var.set(codigo) break self.update_file_names() def update_codigo_maestra(self, event): nombre_seleccionado = self.combo_codigo_maestra.get() for nombre, codigo in fc.IDIOMAS.values(): if nombre == nombre_seleccionado: self.codigo_columna_maestra_var.set(codigo) break self.update_file_names() def update_idioma(self, event): nombre_seleccionado = self.combo.get() for nombre, codigo in fc.IDIOMAS.values(): if nombre == nombre_seleccionado: self.codigo_idioma_var.set(codigo) break self.update_file_names() def update_idioma2(self, event): nombre_seleccionado = self.combo2.get() for nombre, codigo in fc.IDIOMAS.values(): if nombre == nombre_seleccionado: self.codigo_idioma_var2.set(codigo) break self.update_file_names() def save_config(self): self.config.update( codigo_tipo_PLC=self.codigo_tipo_PLC_var.get(), codigo_columna_maestra=self.codigo_columna_maestra_var.get(), codigo_idioma_seleccionado=self.codigo_idioma_var.get(), codigo_idioma_secundario=self.codigo_idioma_var2.get(), work_dir=self.work_dir_var.get(), master_name=self.master_name_var.get(), translate_name=self.translate_name_var.get(), auto_translate_name=self.auto_translate_name_var.get(), ) self.config.save() def select_work_dir(self): dir_path = filedialog.askdirectory() if dir_path: self.work_dir_var.set(dir_path) self.config.work_dir = dir_path self.save_config() self.load_existing_log() # Cargar el log del nuevo directorio def load_existing_log(self): log_file = os.path.join(self.config.work_dir, "logs", "app_log.txt") if os.path.exists(log_file): with open(log_file, "r", encoding="utf-8") as f: log_content = f.read() self.output_text.delete("1.0", tk.END) self.output_text.insert(tk.END, log_content) self.output_text.see(tk.END) else: self.output_text.delete("1.0", tk.END) self.output_text.insert(tk.END, "No se encontró un log existente.\n") def open_file(self, file_path): if os.path.exists(file_path): os.startfile(file_path) else: messagebox.showerror("Error", f"El archivo {file_path} no existe.") def open_explorer(self): if os.path.exists(self.config.work_dir): os.startfile(self.config.work_dir) else: messagebox.showerror( "Error", f"El directorio {self.config.work_dir} no existe." ) def open_log_file(self): log_file_path = os.path.join(self.config.work_dir, "logs", "translate_log.log") if os.path.exists(log_file_path): if sys.platform == "win32": os.startfile(log_file_path) else: opener = "open" if sys.platform == "darwin" else "xdg-open" subprocess.call([opener, log_file_path]) else: messagebox.showerror("Error", f"El archivo de log {log_file_path} no existe.") def update_file_names(self, event=None): codigo_tipo_PLC = self.codigo_tipo_PLC_var.get() codigo_idioma_seleccionado = self.codigo_idioma_var.get() self.master_name_var.set(f"1_hmi_master_translates_{codigo_tipo_PLC}.xlsx") self.translate_name_var.set( f"2_master_export2translate_{codigo_tipo_PLC}_{codigo_idioma_seleccionado}.xlsx" ) self.auto_translate_name_var.set( f"3_master_export2translate_translated_{codigo_tipo_PLC}_{codigo_idioma_seleccionado}.xlsx" ) self.save_config() def ejecutar_run(self, funcion_run): self.save_config() # No limpiar el texto anterior # self.output_text.delete('1.0', tk.END) sys.stdout = RedirectText(self.queue) def run_script(): try: if funcion_run == x1_importar_to_master.run: archivo_importacion = filedialog.askopenfilename( title="Seleccione el archivo para Importar", filetypes=[("Excel files", "*.xls*")], ) if archivo_importacion: funcion_run(self.config,archivo_importacion) elif funcion_run == x2_master_export2translate.run: funcion_run(self.config) elif funcion_run == x3_llm_auto_translate.run: traducir_todo = messagebox.askyesno( "Traducir todo", "¿Desea traducir todas las celdas?" ) self.config.traducir_todo = traducir_todo funcion_run(self.config) elif funcion_run == x4_integrate_translates_to_master.run: funcion_run(self.config) elif funcion_run == x4B_integrate_manual_translates_to_master.run: funcion_run(self.config) elif funcion_run == x5_complete_empty_cells_master.run: funcion_run(self.config) elif funcion_run == x6_update_from_master.run: archivo_to_update = filedialog.askopenfilename( title="Seleccione el archivo a actualizar", filetypes=[("Excel files", "*.xlsx")], ) if archivo_to_update: funcion_run(self.config, archivo_to_update) except Exception as e: self.queue.put(f"Error: {str(e)}\n") finally: sys.stdout = sys.__stdout__ self.save_config() self.save_log() self.queue.put("DONE") threading.Thread(target=run_script, daemon=True).start() def update_output(self): while True: try: message = self.queue.get_nowait() if message == "DONE": break self.output_text.insert(tk.END, message) self.output_text.see(tk.END) self.output_text.update_idletasks() except queue.Empty: break self.master.after(100, self.update_output) def clear_output(self): self.output_text.delete("1.0", tk.END) self.save_log() def save_log(self): log_content = self.output_text.get("1.0", tk.END) log_dir = os.path.join(self.config.work_dir, "logs") os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, "app_log.txt") with open(log_file, "w", encoding="utf-8") as f: f.write(log_content) def main(): root = tk.Tk() app = TranslationApp(root) root.mainloop() if __name__ == "__main__": main()