Database Searcher with Fixed Widget Stacking
import os
from tkinter import Tk, Label, Button, Entry, Text, Scrollbar, filedialog, Canvas,
Toplevel, Frame
from tkinter import messagebox, ttk
from tkinter.ttk import Progressbar
from websocket import WebSocketApp
import time
import threading
import websocket
class DatabaseSearcherNPY:
def __init__(self, root):
self.root = root
self.root.title("Database Searcher")
self.root.geometry("1000x700")
self.root.configure(bg="#ffffff")
# Configure la police
self.sf_pro = ("Helvetica Neue", "Arial")
# Splash Screen Animation
self.splash_screen()
# Header avec animation de slide
self.header = Label(self.root, text="Database Searcher", bg="#ffffff",
fg="#000000",
font=(self.sf_pro[0], 32, "bold"))
self.header.place(x=500, y=-50)
self.slide_in(self.header, 500, 20)
# Main Container
self.main_frame = Frame(self.root, bg="#f5f5f7")
self.main_frame.place(relx=0.5, rely=0.5, anchor="center", width=900,
height=500)
# Border effect
self.main_frame.configure(highlightbackground="#E0E0E0",
highlightcolor="#E0E0E0",
highlightthickness=1,
bd=0)
# Search Bar Frame
self.search_frame = Frame(self.main_frame, bg="#ffffff")
self.search_frame.place(x=50, y=30, width=800, height=60)
self.search_frame.configure(highlightbackground="#E8E8E8",
highlightcolor="#E8E8E8",
highlightthickness=1,
bd=0)
# Folder Selection
self.folder_entry = Entry(self.search_frame, width=50,
font=(self.sf_pro[0], 12),
bg="#f5f5f7", fg="#000000", relief="flat")
self.folder_entry.place(x=20, y=15, width=600, height=30)
self.add_hover_effect(self.folder_entry)
self.browse_button = Button(self.search_frame, text="Browse",
command=self.browse_folder,
bg="#0071e3", fg="white", font=(self.sf_pro[0],
12, "bold"),
relief="flat", cursor="hand2")
self.browse_button.place(x=640, y=12, width=140, height=36)
self.add_button_effects(self.browse_button)
# Search Section
self.keyword_frame = Frame(self.main_frame, bg="#ffffff")
self.keyword_frame.place(x=50, y=110, width=800, height=60)
self.keyword_frame.configure(highlightbackground="#E8E8E8",
highlightcolor="#E8E8E8",
highlightthickness=1,
bd=0)
self.keyword_entry = Entry(self.keyword_frame, font=(self.sf_pro[0], 12),
bg="#f5f5f7", fg="#000000", relief="flat")
self.keyword_entry.place(x=20, y=15, width=600, height=30)
self.add_hover_effect(self.keyword_entry)
self.search_button = Button(self.keyword_frame, text="Search",
command=self.start_search,
bg="#0071e3", fg="white", font=(self.sf_pro[0],
12, "bold"),
relief="flat", cursor="hand2")
self.search_button.place(x=640, y=12, width=140, height=36)
self.add_button_effects(self.search_button)
# Results Area
self.results_frame = Frame(self.main_frame, bg="#ffffff")
self.results_frame.place(x=50, y=190, width=800, height=250)
self.results_frame.configure(highlightbackground="#E8E8E8",
highlightcolor="#E8E8E8",
highlightthickness=1,
bd=0)
self.results_text = Text(self.results_frame, wrap="word",
font=(self.sf_pro[0], 12),
bg="#ffffff", fg="#000000", relief="flat", padx=10,
pady=10)
self.results_text.place(x=0, y=0, width=780, height=250)
# Scrollbar
self.scrollbar = ttk.Scrollbar(self.results_frame,
command=self.results_text.yview)
self.scrollbar.place(x=780, y=0, height=250)
self.results_text.config(yscrollcommand=self.scrollbar.set)
# Progress Bar Frame
self.progress_frame = Frame(self.main_frame, bg="#ffffff")
self.progress_frame.place(x=50, y=460, width=800, height=20)
self.progress = ttk.Progressbar(self.progress_frame, mode="determinate",
length=800)
self.progress.place(x=0, y=0, width=800)
def slide_in(self, widget, target_x, target_y, duration=500):
"""Anime un widget avec un effet de slide"""
start_x = widget.winfo_x()
start_y = widget.winfo_y()
steps = 20
dx = (target_x - start_x) / steps
dy = (target_y - start_y) / steps
def animate(step):
if step < steps:
x = start_x + (dx * step)
y = start_y + (dy * step)
widget.place(x=x, y=y)
self.root.after(duration // steps, lambda: animate(step + 1))
animate(0)
def add_hover_effect(self, widget):
"""Ajoute un effet hover aux entrées"""
def on_enter(e):
widget.configure(bg="#e5e5e5")
def on_leave(e):
widget.configure(bg="#f5f5f7")
widget.bind("<Enter>", on_enter)
widget.bind("<Leave>", on_leave)
def add_button_effects(self, button):
"""Ajoute des effets de survol et de clic aux boutons"""
def on_enter(e):
button.configure(bg="#0077ED")
def on_leave(e):
button.configure(bg="#0071e3")
def on_click(e):
button.configure(bg="#005BBF")
self.root.after(100, lambda: button.configure(bg="#0071e3"))
button.bind("<Enter>", on_enter)
button.bind("<Leave>", on_leave)
button.bind("<Button-1>", on_click)
def splash_screen(self):
"""Écran de démarrage avec animation simple"""
splash = Toplevel(self.root)
splash.geometry("1000x700")
splash.configure(bg="#ffffff")
splash.overrideredirect(True)
# Centrer la fenêtre
screen_width = splash.winfo_screenwidth()
screen_height = splash.winfo_screenheight()
x = (screen_width - 1000) // 2
y = (screen_height - 700) // 2
splash.geometry(f"1000x700+{x}+{y}")
# Texte avec animation de slide
splash_label = Label(splash, text="Database Searcher",
bg="#ffffff", fg="#000000",
font=(self.sf_pro[0], 32, "bold"))
splash_label.place(relx=0.5, rely=-0.2, anchor="center")
# Animation de slide pour le texte
def slide_text():
for i in range(20):
y = -0.2 + (0.6 * (i / 19))
splash_label.place(relx=0.5, rely=y, anchor="center")
splash.update()
time.sleep(0.02)
self.root.withdraw()
slide_text()
# Progress bar
progress = ttk.Progressbar(splash, mode="indeterminate", length=400)
progress.place(relx=0.5, rely=0.7, anchor="center")
progress.start(15)
def close_splash():
progress.stop()
splash.destroy()
self.root.deiconify()
splash.after(2000, close_splash)
def browse_folder(self):
folder_path = filedialog.askdirectory()
if folder_path:
self.folder_entry.delete(0, "end")
self.folder_entry.insert(0, folder_path)
def start_search(self):
if not self.validate_inputs():
return
self.search_button.configure(state="disabled")
thread = threading.Thread(target=self.perform_search)
thread.daemon = True
thread.start()
def validate_inputs(self):
"""Valide les entrées avec effet de shake"""
folder_path = self.folder_entry.get()
keyword = self.keyword_entry.get()
if not folder_path or not keyword:
widget = self.folder_entry if not folder_path else self.keyword_entry
self.shake_widget(widget)
messagebox.showwarning("Input Error", "Please fill all fields")
return False
return True
def shake_widget(self, widget):
"""Anime un widget avec un effet de secousse"""
original_x = widget.winfo_x()
for i in range(10):
new_x = original_x + (-5 if i % 2 == 0 else 5)
widget.place(x=new_x)
self.root.update()
time.sleep(0.05)
widget.place(x=original_x)
def perform_search(self):
"""Effectue la recherche avec animations de progression"""
try:
folder_path = self.folder_entry.get()
keyword = self.keyword_entry.get()
total_files = sum(len(files) for _, _, files in os.walk(folder_path))
processed_files = 0
self.progress["value"] = 0
self.results_text.config(state="normal")
self.results_text.delete(1.0, "end")
for root_dir, _, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root_dir, file)
try:
with open(file_path, "r", encoding="utf-8",
errors="ignore") as f:
content = f.read()
if keyword.lower() in content.lower():
result = f"Found in: {file_path}\n"
self.results_text.insert("end", result)
except Exception as e:
continue
processed_files += 1
self.progress["value"] = (processed_files / total_files) * 100
self.root.update_idletasks()
self.results_text.config(state="disabled")
finally:
self.search_button.configure(state="normal")
self.progress["value"] = 100
if __name__ == "__main__":
root = Tk()
app = DatabaseSearcherNPY(root)
root.mainloop()