import os import tkinter as tk from tkinter import ttk, filedialog, messagebox import subprocess from collections import defaultdict from PIL import Image, ImageTk import mimetypes from docx import Document from openpyxl import load_workbook import pandas as pd from io import StringIO import fitz # PyMuPDF for PDF import pythoncom # For DWG (AutoCAD) import win32com.client # For .doc file manipulation via COM # Global file dictionary arquivos_dict = {} historico_caminhos = [] arquivo_historico = os.path.join(os.environ['TEMP'], "historico_caminhos.txt") class AutoCompleteCombobox(ttk.Combobox): def __init__(self, parent, **kwargs): super().__init__(parent, **kwargs) self.bind("", self.on_keyrelease) self.bind("<>", self.on_select) def on_keyrelease(self, event): if event.keysym == "BackSpace": return current_text = self.get().lower() if not current_text: return values = self['values'] matching_values = [item for item in values if item.lower().startswith(current_text)] if matching_values: self.set(matching_values[0]) self.icursor(len(current_text)) self.selection_range(len(current_text), tk.END) def on_select(self, event): pass class PreviewPanel: def __init__(self, parent, colors): self.parent = parent self.colors = colors self.current_file = None # Main preview frame self.preview_frame = tk.Frame(parent, bg=colors['bg_card'], relief="flat", bd=1, highlightbackground=colors['border'], highlightthickness=1) # Preview header header_frame = tk.Frame(self.preview_frame, bg=colors['bg_card']) header_frame.pack(fill="x", padx=15, pady=(15, 5)) tk.Label(header_frame, text="Preview", font=("Segoe UI", 10, "bold"), fg=colors['accent'], bg=colors['bg_card']).pack(anchor="w") separator = tk.Frame(header_frame, height=1, bg=colors['border']) separator.pack(fill="x", pady=(5, 0)) # Main preview container self.content_frame = tk.Frame(self.preview_frame, bg=colors['bg_card']) self.content_frame.pack(fill="both", expand=True, padx=15, pady=10) # File information labels self.info_frame = tk.Frame(self.content_frame, bg=colors['bg_card']) self.info_frame.pack(fill="x", pady=(0, 10)) self.label_nome = tk.Label(self.info_frame, text="No file selected", font=("Segoe UI", 9, "bold"), fg=colors['text_primary'], bg=colors['bg_card'], wraplength=250, justify="left") self.label_nome.pack(anchor="w") self.label_caminho = tk.Label(self.info_frame, text="", font=("Segoe UI", 8), fg=colors['text_secondary'], bg=colors['bg_card'], wraplength=250, justify="left") self.label_caminho.pack(anchor="w", pady=(2, 0)) self.label_info = tk.Label(self.info_frame, text="", font=("Segoe UI", 8), fg=colors['text_secondary'], bg=colors['bg_card']) self.label_info.pack(anchor="w", pady=(2, 0)) # Visual preview frame self.visual_frame = tk.Frame(self.content_frame, bg="white", relief="flat", bd=1, highlightbackground=colors['border'], highlightthickness=1) self.visual_frame.pack(fill="both", expand=True) # Label for image or icon self.image_label = tk.Label(self.visual_frame, bg="white", text="Select a file\nto preview", font=("Segoe UI", 10), fg=colors['text_secondary']) self.image_label.pack(expand=True) # Text content frame self.text_frame = tk.Frame(self.visual_frame, bg="white") self.text_widget = tk.Text(self.text_frame, wrap=tk.WORD, font=("Segoe UI", 9), bg="white", fg=colors['text_primary'], relief="flat", bd=0, state=tk.DISABLED) text_scroll = tk.Scrollbar(self.text_frame, command=self.text_widget.yview) self.text_widget.config(yscrollcommand=text_scroll.set) self.text_widget.pack(side="left", fill="both", expand=True, padx=10, pady=10) text_scroll.pack(side="right", fill="y") # Add resize binding self.visual_frame.bind("", self.on_frame_resize) self.last_frame_size = (0, 0) def on_frame_resize(self, event): """Called when frame is resized""" if self.current_file and os.path.splitext(self.current_file)[1].lower() in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.pdf']: # Only redraw if size changed significantly if abs(event.width - self.last_frame_size[0]) > 5 or abs(event.height - self.last_frame_size[1]) > 5: self.last_frame_size = (event.width, event.height) if os.path.splitext(self.current_file)[1].lower() == '.pdf': self.show_pdf_preview(self.current_file) else: self.show_image_preview(self.current_file, event.width, event.height) def show_preview(self, arquivo_nome, arquivo_caminho): """Show preview of selected file""" self.current_file = arquivo_caminho # Update file information self.label_nome.config(text=arquivo_nome) self.label_caminho.config(text=arquivo_caminho) # Get file info try: stat = os.stat(arquivo_caminho) tamanho = self.format_size(stat.st_size) modificado = self.format_date(stat.st_mtime) self.label_info.config(text=f"Size: {tamanho} | Modified: {modificado}") except: self.label_info.config(text="Information not available") # Clear previous preview self.clear_preview() # Determine file type and show appropriate preview ext = os.path.splitext(arquivo_caminho)[1].lower() if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp']: self.show_image_preview(arquivo_caminho, self.visual_frame.winfo_width(), self.visual_frame.winfo_height()) elif ext in ['.txt', '.py', '.js', '.html', '.css', '.xml', '.json', '.csv', '.log']: self.show_text_preview(arquivo_caminho) elif ext in ['.sldprt', '.sldasm', '.slddrw']: self.show_solidworks_preview() elif ext in ['.pdf']: self.show_pdf_preview(arquivo_caminho) elif ext in ['.dwg']: self.show_dwg_preview(arquivo_caminho) elif ext in ['.docx', '.doc', '.xlsx', '.xls']: self.show_office_preview(arquivo_caminho) else: self.show_file_icon_preview(arquivo_caminho) def clear_preview(self): """Clear current preview""" self.text_frame.pack_forget() self.image_label.pack(expand=True) self.image_label.config(image="", text="") def show_image_preview(self, caminho, frame_width=None, frame_height=None): """Show image preview with dynamic resizing""" try: # If no dimensions passed, use current frame dimensions if frame_width is None or frame_height is None: frame_width = self.visual_frame.winfo_width() frame_height = self.visual_frame.winfo_height() # Ensure minimum size frame_width = max(frame_width, 50) frame_height = max(frame_height, 50) # Open and resize image with Image.open(caminho) as img: # Calculate new size maintaining aspect ratio img_width, img_height = img.size ratio = min((frame_width-20)/img_width, (frame_height-20)/img_height) # -20 for margin new_width = int(img_width * ratio) new_height = int(img_height * ratio) img_resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS) photo = ImageTk.PhotoImage(img_resized) self.image_label.config(image=photo, text="") self.image_label.image = photo # Keep reference except Exception as e: self.image_label.config(text=f"Error loading image:\n{str(e)[:50]}...", image="") def show_text_preview(self, caminho): """Show text file preview""" try: # Detect encoding encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1'] content = "" for encoding in encodings: try: with open(caminho, 'r', encoding=encoding) as f: content = f.read(2000) # First 2000 characters break except UnicodeDecodeError: continue if content: self.image_label.pack_forget() self.text_frame.pack(fill="both", expand=True) self.text_widget.config(state=tk.NORMAL) self.text_widget.delete(1.0, tk.END) self.text_widget.insert(1.0, content) if len(content) >= 2000: self.text_widget.insert(tk.END, "\n\n... (file truncated)") self.text_widget.config(state=tk.DISABLED) else: self.image_label.config(text="Could not\ndecode file", image="") except Exception as e: self.image_label.config(text=f"Error reading file:\n{str(e)[:50]}...", image="") def show_solidworks_preview(self): """Show info for SolidWorks files""" self.image_label.config(text="📐 SolidWorks File\n\nPreview not available yet.", image="", font=("Segoe UI", 10)) def show_pdf_preview(self, caminho): """Show PDF file preview with dynamic resizing""" try: # Get current frame dimensions frame_width = self.visual_frame.winfo_width() frame_height = self.visual_frame.winfo_height() # Ensure minimum size frame_width = max(frame_width, 50) frame_height = max(frame_height, 50) # Create thumbnail of first page doc = fitz.open(caminho) page = doc.load_page(0) # First page # Calculate zoom based on frame size zoom = min(frame_width/page.rect.width, frame_height/page.rect.height) * 0.9 mat = fitz.Matrix(zoom, zoom) pix = page.get_pixmap(matrix=mat) # Convert to PIL format img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) # Resize to fit preview img_width, img_height = img.size ratio = min((frame_width-20)/img_width, (frame_height-20)/img_height) new_width = int(img_width * ratio) new_height = int(img_height * ratio) img_resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS) photo = ImageTk.PhotoImage(img_resized) self.image_label.config(image=photo, text="") self.image_label.image = photo # Keep reference # Add PDF info num_paginas = len(doc) info_text = f"📄 PDF ({num_paginas} page{'s' if num_paginas > 1 else ''})" self.label_nome.config(text=f"{os.path.basename(caminho)}\n{info_text}") doc.close() except Exception as e: self.image_label.config(text=f"Error reading PDF:\n{str(e)[:50]}...", image="") def show_dwg_preview(self, caminho): """Show DWG (AutoCAD) file preview""" try: # Try using Autodesk TrueView if available trueview_path = r"C:\Program Files\Autodesk\DWG TrueView 2026 - English\dwgviewr.exe" if os.path.exists(trueview_path): # Export to temporary image temp_img = os.path.join(os.environ['TEMP'], "dwg_preview.png") subprocess.run([trueview_path, caminho, "/export", temp_img], check=True) if os.path.exists(temp_img): self.show_image_preview(temp_img) os.remove(temp_img) # Clean temp file else: self.image_label.config(text="📐 DWG File\n\nPreview not available yet", image="", font=("Segoe UI", 10)) else: self.image_label.config(text="📐 DWG File\n\nPreview not available yet", image="", font=("Segoe UI", 10)) except Exception as e: self.image_label.config(text=f"📐 DWG File\n\nError generating preview", image="", font=("Segoe UI", 10)) def show_office_preview(self, caminho): """Show preview for Office files (Word, Excel)""" ext = os.path.splitext(caminho)[1].lower() try: if ext in ['.docx', '.doc']: self.show_word_preview(caminho) elif ext in ['.xlsx', '.xls']: self.show_excel_preview(caminho) else: self.show_file_icon_preview(caminho) except Exception as e: error_msg = str(e) if "file format cannot be determined" in error_msg.lower(): self.image_label.config(text="Invalid or corrupted file format", image="") elif "password" in error_msg.lower(): self.image_label.config(text="Password protected file", image="") else: self.image_label.config(text=f"Error reading Office file:\n{error_msg[:50]}...", image="") def show_word_preview(self, caminho): """Show Word file preview""" try: doc = Document(caminho) content = "" # Extract text from paragraphs (first 10 paragraphs) for para in doc.paragraphs[:10]: if para.text.strip(): content += para.text + "\n\n" if not content.strip(): content = "[Empty document or non-text content]" # Show in text widget self.image_label.pack_forget() self.text_frame.pack(fill="both", expand=True) self.text_widget.config(state=tk.NORMAL) self.text_widget.delete(1.0, tk.END) self.text_widget.insert(1.0, content) if len(doc.paragraphs) > 10: self.text_widget.insert(tk.END, "\n\n... (document truncated)") self.text_widget.config(state=tk.DISABLED) except Exception as e: raise e def show_excel_preview(self, caminho): """Show Excel file preview""" try: # Use pandas to read first sheet df = pd.read_excel(caminho, nrows=10) # Read only first 10 rows # Convert to formatted text output = StringIO() df.to_string(output, index=False) content = output.getvalue() output.close() # Show in text widget self.image_label.pack_forget() self.text_frame.pack(fill="both", expand=True) self.text_widget.config(state=tk.NORMAL) self.text_widget.delete(1.0, tk.END) self.text_widget.insert(1.0, f"Spreadsheet: {os.path.basename(caminho)}\n\n") self.text_widget.insert(tk.END, content) self.text_widget.insert(tk.END, "\n\n... (spreadsheet truncated)") self.text_widget.config(state=tk.DISABLED) except Exception as e: raise e def show_file_icon_preview(self, caminho): """Show generic file icon and info""" ext = os.path.splitext(caminho)[1].upper() mime_type, _ = mimetypes.guess_type(caminho) if mime_type: tipo = mime_type.split('/')[0].title() else: tipo = "File" texto = f"📁 {tipo}\n\nExtension: {ext}\n\nClick 'Open'\nto view" self.image_label.config(text=texto, image="", font=("Segoe UI", 10)) def format_size(self, bytes_size): """Format file size""" for unit in ['B', 'KB', 'MB', 'GB']: if bytes_size < 1024: return f"{bytes_size:.1f} {unit}" bytes_size /= 1024 return f"{bytes_size:.1f} TB" def format_date(self, timestamp): """Format modification date""" import datetime return datetime.datetime.fromtimestamp(timestamp).strftime("%d/%m/%Y %H:%M") class LupaWorksApp: def __init__(self, root): self.root = root self.root.title("Lupa - File Search") self.pasta_principal = "" # Configure minimalist theme self.setup_minimal_theme() self.build_ui() self.carregar_historico() def setup_minimal_theme(self): # Minimalist color settings self.colors = { 'bg_primary': '#ffffff', 'bg_secondary': '#f5f5f5', 'bg_card': '#f9f9f9', 'text_primary': '#333333', 'text_secondary': '#666666', 'accent': '#4a6fa5', 'accent_hover': '#3a5a80', 'border': '#e0e0e0' } # Configure ttk style style = ttk.Style() style.theme_use('clam') # Combobox style style.configure('Minimal.TCombobox', fieldbackground=self.colors['bg_secondary'], background=self.colors['bg_secondary'], foreground=self.colors['text_primary'], bordercolor=self.colors['border'], arrowcolor=self.colors['text_secondary'], padding=5) style.map('Minimal.TCombobox', fieldbackground=[('readonly', self.colors['bg_secondary'])], background=[('readonly', self.colors['bg_secondary'])]) def add_footer_image(self, parent): """Add footer image""" try: from urllib.request import urlopen from io import BytesIO # Image URL image_url = " " # Download image from URL with timeout with urlopen(image_url, timeout=10) as response: if response.getcode() == 200: image_data = response.read() else: raise Exception(f"HTTP Error {response.getcode()}") # Load and resize image image = Image.open(BytesIO(image_data)) image = image.resize((500, 82), Image.Resampling.LANCZOS) # Resize to fit better photo = ImageTk.PhotoImage(image) # Create label for image with centering footer_label = tk.Label(parent, image=photo, bg=self.colors['bg_primary']) footer_label.image = photo # Keep reference footer_label.pack(side="bottom", pady=10, anchor="center") except Exception as e: print(f"Error loading footer image: {e}") # Fallback to simple text tk.Label(parent, text="Lupa", font=("Segoe UI", 8), fg=self.colors['text_secondary'], bg=self.colors['bg_primary']).pack(side="bottom", pady=10, anchor="w") def build_ui(self): # Configure main window self.root.configure(bg=self.colors['bg_primary']) # Main frame main_frame = tk.Frame(self.root, bg=self.colors['bg_primary']) main_frame.pack(fill="both", expand=True, padx=10, pady=10) # Header header_frame = tk.Frame(main_frame, bg=self.colors['bg_primary']) header_frame.pack(fill="x", pady=(0, 15)) title_label = tk.Label(header_frame, text="Lupa", font=("Segoe UI", 18, "bold"), fg=self.colors['accent'], bg=self.colors['bg_primary']) title_label.pack() subtitle_label = tk.Label(header_frame, text="File Search System with Preview", font=("Segoe UI", 10), fg=self.colors['text_secondary'], bg=self.colors['bg_primary']) subtitle_label.pack() # Main container with resizing container = tk.PanedWindow(main_frame, orient=tk.HORIZONTAL, bg=self.colors['bg_primary'], sashrelief=tk.RAISED, sashwidth=8) container.pack(fill="both", expand=True) # Left frame (controls) left_frame = tk.Frame(container, bg=self.colors['bg_card'], relief="flat", bd=1, highlightbackground=self.colors['border'], highlightthickness=1) left_frame.configure(width=280) container.add(left_frame, minsize=200, stretch='never') # Center frame (results) center_frame = tk.Frame(container, bg=self.colors['bg_card'], relief="flat", bd=1, highlightbackground=self.colors['border'], highlightthickness=1) container.add(center_frame, minsize=300) # Right frame (preview) right_frame = tk.Frame(container, bg=self.colors['bg_card']) right_frame.configure(width=350) container.add(right_frame, minsize=280, stretch='always') # Build controls interface (left) self.build_controls(left_frame) # Build results interface (center) self.build_results(center_frame) # Build preview panel (right) self.preview_panel = PreviewPanel(right_frame, self.colors) self.preview_panel.preview_frame.pack(fill="both", expand=True, padx=0, pady=0) # Add donate button instead of footer image self.add_donate_button(main_frame) def add_donate_button(self, parent): """Add donate button with description""" donate_frame = tk.Frame(parent, bg=self.colors['bg_primary']) donate_frame.pack(side="bottom", fill="x", pady=(10, 20)) # Description label desc_label = tk.Label(donate_frame, text="This tool is free thanks to the support of people like you. If it was useful, please consider contributing - so it can keep evolving for everyone.", font=("Segoe UI", 9), fg=self.colors['text_secondary'], bg=self.colors['bg_primary'], wraplength=600) desc_label.pack(pady=(0, 10)) # Donate button btn_donate = tk.Button(donate_frame, text="Donate", command=self.open_donate_link, bg='#4CAF50', # Green color for the button fg='white', font=("Segoe UI", 10, "bold"), relief="flat", bd=0, padx=20, pady=8, activebackground='#45a049') btn_donate.pack() def open_donate_link(self): """Open browser with donation link""" import webbrowser webbrowser.open("https://www.reddit.com/user/OkPaleontologist9333/") # Replace with your actual donation link def build_controls(self, parent): # Internal padding controls_frame = tk.Frame(parent, bg=self.colors['bg_card']) controls_frame.pack(fill="both", expand=True, padx=15, pady=15) # Section: Folder Selection self.create_section_header(controls_frame, "Folder Selection") tk.Label(controls_frame, text="Main folder:", font=("Segoe UI", 9), fg=self.colors['text_primary'], bg=self.colors['bg_card']).pack(anchor="w", pady=(10, 5)) path_frame = tk.Frame(controls_frame, bg=self.colors['bg_card']) path_frame.pack(fill="x", pady=(0, 10)) self.combo_caminhos = ttk.Combobox(path_frame, style='Minimal.TCombobox', height=25, postcommand=self.atualizar_combo_caminhos) self.combo_caminhos.pack(side="left", fill="x", expand=True) self.combo_caminhos.bind("<>", self.on_path_selected) btn_browse = tk.Button(path_frame, text="...", command=self.selecionar_pasta, bg=self.colors['accent'], fg='white', font=("Segoe UI", 9), relief="flat", bd=0, padx=8, activebackground=self.colors['accent_hover']) btn_browse.pack(side="right", padx=(5, 0)) tk.Label(controls_frame, text="Subfolder:", font=("Segoe UI", 9), fg=self.colors['text_primary'], bg=self.colors['bg_card']).pack(anchor="w", pady=(5, 5)) self.combo_subpasta = AutoCompleteCombobox(controls_frame, style='Minimal.TCombobox', height=25) self.combo_subpasta.pack(fill="x", pady=(0, 5)) # Section: Filters self.create_section_header(controls_frame, "Search Filters") tk.Label(controls_frame, text="Search term:", font=("Segoe UI", 9), fg=self.colors['text_primary'], bg=self.colors['bg_card']).pack(anchor="w", pady=(10, 5)) self.entrada_filtro = tk.Entry(controls_frame, font=("Segoe UI", 10), bg=self.colors['bg_secondary'], fg=self.colors['text_primary'], insertbackground=self.colors['text_primary'], relief="flat", bd=1, highlightbackground=self.colors['border'], highlightthickness=1, highlightcolor=self.colors['accent']) self.entrada_filtro.pack(fill="x", pady=(0, 10)) self.entrada_filtro.bind('', lambda event: self.pesquisar()) # Checkbox for detailed search self.var_detalhada = tk.BooleanVar() check_detail = tk.Checkbutton(controls_frame, text="Detailed search (includes path)", variable=self.var_detalhada, font=("Segoe UI", 8), bg=self.colors['bg_card'], fg=self.colors['text_primary'], selectcolor=self.colors['bg_secondary'], activebackground=self.colors['bg_card'], activeforeground=self.colors['text_primary']) check_detail.pack(anchor="w", pady=(0, 15)) # Search button btn_search = tk.Button(controls_frame, text="Search", command=self.pesquisar, bg=self.colors['accent'], fg='white', font=("Segoe UI", 10, "bold"), relief="flat", bd=0, pady=8, activebackground=self.colors['accent_hover']) btn_search.pack(fill="x", pady=(0, 10)) # Action buttons frame action_frame = tk.Frame(controls_frame, bg=self.colors['bg_card']) action_frame.pack(fill="x", pady=(5, 0)) buttons = [ ("Open", self.abrir_arquivo), ("Open Location", self.abrir_local) ] for text, command in buttons: btn = tk.Button(action_frame, text=text, command=command, bg=self.colors['accent'], fg='white', font=("Segoe UI", 9), relief="flat", bd=0, pady=6, activebackground=self.colors['accent_hover']) btn.pack(fill="x", pady=2) def build_results(self, parent): results_inner = tk.Frame(parent, bg=self.colors['bg_card']) results_inner.pack(fill="both", expand=True, padx=15, pady=15) # Section: Results self.create_section_header(results_inner, "Search Results") # Frame for listbox with scrollbar list_frame = tk.Frame(results_inner, bg=self.colors['bg_card']) list_frame.pack(fill="both", expand=True, pady=(10, 0)) # Scrollbar scrollbar = tk.Scrollbar(list_frame, bg=self.colors['bg_secondary']) scrollbar.pack(side="right", fill="y") # Listbox self.listbox_resultados = tk.Listbox(list_frame, font=("Segoe UI", 10), bg='white', fg=self.colors['text_primary'], selectbackground=self.colors['accent'], selectforeground='white', relief="flat", bd=1, highlightbackground=self.colors['border'], highlightthickness=1, yscrollcommand=scrollbar.set) self.listbox_resultados.pack(side="left", fill="both", expand=True) scrollbar.config(command=self.listbox_resultados.yview) # Bind events self.listbox_resultados.bind('<>', self.on_file_select) self.listbox_resultados.bind('', self.on_double_click) def create_section_header(self, parent, text): header_frame = tk.Frame(parent, bg=self.colors['bg_card']) header_frame.pack(fill="x", pady=(10, 0)) tk.Label(header_frame, text=text, font=("Segoe UI", 10, "bold"), fg=self.colors['accent'], bg=self.colors['bg_card']).pack(anchor="w") # Separator line separator = tk.Frame(header_frame, height=1, bg=self.colors['border']) separator.pack(fill="x", pady=(5, 0)) def on_file_select(self, event): """Event called when a file is selected in the list""" sel = self.listbox_resultados.curselection() if sel: nome_arquivo = self.listbox_resultados.get(sel[0]) if nome_arquivo in arquivos_dict: caminho_arquivo = arquivos_dict[nome_arquivo] self.preview_panel.show_preview(nome_arquivo, caminho_arquivo) def on_double_click(self, event): """Event called when a file is double-clicked in the list""" self.abrir_arquivo() def on_path_selected(self, event=None): caminho_selecionado = self.combo_caminhos.get() if caminho_selecionado and caminho_selecionado != self.pasta_principal: self.pasta_principal = caminho_selecionado self.atualizar_historico(caminho_selecionado) self.atualizar_subpastas() def selecionar_pasta(self): pasta = filedialog.askdirectory() if pasta: self.pasta_principal = pasta self.combo_caminhos.set(pasta) self.atualizar_historico(pasta) self.atualizar_subpastas() def atualizar_combo_caminhos(self): self.combo_caminhos['values'] = historico_caminhos def atualizar_historico(self, caminho): if caminho not in historico_caminhos: historico_caminhos.append(caminho) self.salvar_historico() def salvar_historico(self): try: with open(arquivo_historico, 'w') as f: for caminho in historico_caminhos: f.write(caminho + '\n') except Exception as e: print("Error saving history:", e) def carregar_historico(self): if os.path.exists(arquivo_historico): try: with open(arquivo_historico, 'r') as f: for linha in f: caminho = linha.strip() if caminho: historico_caminhos.append(caminho) except: pass def atualizar_subpastas(self): if not self.pasta_principal: return try: subpastas = [p for p in os.listdir(self.pasta_principal) if os.path.isdir(os.path.join(self.pasta_principal, p))] self.combo_subpasta['values'] = subpastas self.combo_subpasta.set('') except Exception as e: print(f"Error updating subfolders: {e}") def pesquisar(self): global arquivos_dict arquivos_dict.clear() self.listbox_resultados.delete(0, tk.END) if not self.pasta_principal: messagebox.showwarning("Warning", "Please select a main folder first.") return pasta = self.combo_subpasta.get() if pasta: dir_busca = os.path.join(self.pasta_principal, pasta) else: dir_busca = self.pasta_principal if not os.path.exists(dir_busca): messagebox.showerror("Error", "Invalid directory.") return chave = self.entrada_filtro.get().lower().split() for root_dir, _, files in os.walk(dir_busca): for file in files: nome = file.lower() caminho = os.path.join(root_dir, file).lower() if self.var_detalhada.get(): texto_busca = nome + " " + caminho else: texto_busca = nome if all(k in texto_busca for k in chave): arquivos_dict[file] = os.path.join(root_dir, file) self.atualizar_lista() def atualizar_lista(self): self.listbox_resultados.delete(0, tk.END) for nome in sorted(arquivos_dict): self.listbox_resultados.insert(tk.END, nome) def abrir_arquivo(self): nome = self.get_arquivo_selecionado() if nome: caminho = arquivos_dict[nome] try: os.startfile(caminho) except Exception as e: messagebox.showerror("Error", f"Could not open file:\n{str(e)}") def abrir_preview_edrawings(self): nome = self.get_arquivo_selecionado() if nome: caminho = arquivos_dict[nome] if not caminho.lower().endswith(('.sldprt', '.sldasm', '.slddrw')): messagebox.showwarning("Warning", "Preview only available for SolidWorks files") return try: subprocess.Popen(f'"{EDRAWINGS_PATH}" "{caminho}"') except FileNotFoundError: messagebox.showerror("Error", "eDrawings Viewer not found.") def abrir_local(self): nome = self.get_arquivo_selecionado() if nome: caminho = arquivos_dict[nome] pasta = os.path.dirname(caminho) try: os.startfile(pasta) except Exception as e: messagebox.showerror("Error", f"Could not open location:\n{str(e)}") def get_arquivo_selecionado(self): sel = self.listbox_resultados.curselection() if not sel: messagebox.showwarning("Warning", "No file selected.") return None return self.listbox_resultados.get(sel[0]) if __name__ == '__main__': root = tk.Tk() app = LupaWorksApp(root) root.geometry("1000x700") root.minsize(800, 600) root.mainloop()