Codice: Seleziona tutto
from pathlib import Path
from datetime import datetime
import math
import fnmatch
import threading
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from tkcalendar import DateEntry
# Flag per l'interruzione della ricerca
interrupt_flag = False
# Gruppo widget per il filtro data (popolato dopo la creazione dei widget)
widget_filtri_data = []
def inizializza_avvio():
percorso_corrente = str(Path.cwd())
cmbPercorso.set(percorso_corrente)
unit_list = leggi_unita_attive()
valori = list(cmbPercorso["values"])
for unit in unit_list:
if unit not in valori:
valori.append(unit)
if percorso_corrente not in valori:
valori.append(percorso_corrente)
cmbPercorso["values"] = valori
btnStop.config(state=tk.DISABLED)
cmbNome.focus_set()
def leggi_unita_attive():
return [f"{chr(d)}:\\" for d in range(ord('A'), ord('Z') + 1) if Path(f"{chr(d)}:\\").exists()]
def cerca_files_cartelle(cartella_radice, nome_da_cercare, callback_stato=None):
global interrupt_flag
risultati = []
nome_lower = nome_da_cercare.lower()
usa_wildcard = "*" in nome_da_cercare
dt_inizio, dt_fine = None, None
if varDataAttiva.get() == 1:
try:
dt_inizio = datetime.strptime(f"{calDataInizio.get()} {spnOraInizio.get()}:{spnMinInizio.get()}", "%d/%m/%Y %H:%M")
dt_fine = datetime.strptime(f"{calDataFine.get()} {spnOraFine.get()}:{spnMinFine.get()}", "%d/%m/%Y %H:%M")
except Exception as e:
messagebox.showerror("Errore data", f"Formato data/ora non valido: {e}")
return []
percorso = Path(cartella_radice)
try:
files = percorso.rglob("*") if varSubDir.get() else percorso.glob("*")
except Exception as e:
messagebox.showerror("Errore accesso", f"Impossibile accedere a: {cartella_radice}\n\n{e}")
return []
for elemento in files:
if interrupt_flag:
interrupt_flag = False
return risultati
if callback_stato:
callback_stato(str(elemento.parent))
nome = elemento.name
if (usa_wildcard and fnmatch.fnmatch(nome, nome_da_cercare)) or (not usa_wildcard and nome_lower in nome.lower()):
try:
info = elemento.stat()
mtime = datetime.fromtimestamp(info.st_mtime)
if dt_inizio and dt_fine and not (dt_inizio <= mtime <= dt_fine):
continue
risultati.append((
nome,
str(elemento.parent),
dimensione_elemento(info.st_size),
elemento.suffix,
datetime.fromtimestamp(info.st_ctime).strftime('%d %B %Y'),
mtime.strftime('%d %B %Y')
))
except:
continue
return risultati
def interrompi_ricerca():
global interrupt_flag
interrupt_flag = True
lblBarraStato.config(text="Ricerca interrotta.")
def dimensione_elemento(size_bytes):
if size_bytes == 0:
return "0 B"
try:
i = int(math.log(size_bytes, 1024))
p = 1024 ** i
return f"{size_bytes / p:.0f} {'B KMGT'[i]}B"
except:
return "???"
def btnTrova_Click():
cartella = cmbPercorso.get()
nome = cmbNome.get()
tvRisultati.delete(*tvRisultati.get_children())
if not cartella or not nome:
messagebox.showwarning("Attenzione", "Inserisci sia il percorso della cartella che il nome del file/cartella da cercare.")
return
btnStop.config(state=tk.NORMAL)
lblBarraStato.config(text=f"Ricerca in corso in: {cartella}")
threading.Thread(
target=esegui_ricerca_thread,
args=(cartella, nome),
daemon=True
).start()
def esegui_ricerca_thread(cartella, nome):
risultati = cerca_files_cartelle(cartella, nome, callback_stato=lambda p: frmMain.after(0, aggiorna_barra_stato, p))
frmMain.after(0, aggiorna_risultati_gui, risultati)
def aggiorna_risultati_gui(risultati):
for riga in risultati:
tvRisultati.insert("", 1, text=str(riga[0]), values=(riga[1], riga[2], riga[3], riga[4], riga[5]))
btnStop.config(state=tk.DISABLED)
lblBarraStato.config(text=f"Ricerca completata. {len(risultati)} risultati trovati.")
def aggiorna_barra_stato(percorso_corrente):
lblBarraStato.config(text=f"Cercando in: {percorso_corrente}")
def btnSfoglia_Click():
percorso_ricerca = filedialog.askdirectory(parent=frmMain, initialdir=str(Path.cwd()), title="Selezionare la cartella di ricerca:")
if percorso_ricerca:
path_sel = Path(percorso_ricerca).resolve()
valori = list(cmbPercorso["values"])
if str(path_sel) not in valori:
valori.append(str(path_sel))
cmbPercorso["values"] = valori
cmbPercorso.set(str(path_sel))
def btnNuovaRicerca_Click():
cmbNome.set("")
tvRisultati.delete(*tvRisultati.get_children())
cmbNome.focus_set()
def chkDataAttiva_Check():
stato = 'normal' if varDataAttiva.get() == 1 else 'disabled'
for w in widget_filtri_data:
w.config(state=stato)
#--- Finestra principale ---
frmMain = tk.Tk()
frmMain.title("PowerApps - xFinder")
frmMain.geometry("800x500")
frmMain.resizable(False, False)
frmMain.iconbitmap('images/Search95.ico')
# Icona pixel art XF
#icon = PhotoImage(width=16, height=16)
#icon.put("#0000cc", to=(0, 0, 16, 16))
#x_pixels = [(1, 1), (2, 2), (3, 3), (4, 4), (1, 4), (2, 3), (3, 2), (4, 1)]
#f_pixels = [(7, 6), (8, 6), (9, 6), (7, 7), (7, 8), (7, 9),
# (8, 8), (9, 8), (7, 10), (7, 11)]
#for x, y in x_pixels + f_pixels:
# icon.put("#ffffff", (x, y))
#frmMain.iconphoto(False, icon)
# Pulsanti principali
btnTrova = ttk.Button(frmMain, text="Trova", command=btnTrova_Click)
btnTrova.place(x=700, y=26, height=25, width=90)
btnStop = ttk.Button(frmMain, text="Interrompi", command=interrompi_ricerca)
btnStop.place(x=700, y=56, height=25, width=90)
btnNuovaRicerca = ttk.Button(frmMain, text="Nuova ricerca", command=btnNuovaRicerca_Click)
btnNuovaRicerca.place(x=700, y=86, height=25, width=90)
#--- Creazione Notebook (frame) con 3 schede ---
notebook = ttk.Notebook(frmMain)
pgPercorso = ttk.Frame(notebook)
pgData = ttk.Frame(notebook)
pgAvanzate = ttk.Frame(notebook)
notebook.add(pgPercorso, text="Nome e percorso")
notebook.add(pgData, text="Data")
notebook.add(pgAvanzate, text="Avanzate")
notebook.select(pgPercorso)
notebook.place(x=4, y=4, height=184, width=690)
#--- Contenuto Frame #1 ---
lblNome = ttk.Label(pgPercorso, anchor='e', text="Nome:")
lblNome.place(x=10, y=20, height=25, width=54)
cmbNome = ttk.Combobox(pgPercorso)
cmbNome.bind("<Return>", lambda event: btnTrova_Click())
cmbNome.place(x=70, y=20, height=25, width=600)
lblPercorso = ttk.Label(pgPercorso, anchor='e', text="Cerca in:")
lblPercorso.place(x=10, y=60, height=25, width=54)
cmbPercorso = ttk.Combobox(pgPercorso, state='readonly')
cmbPercorso.place(x=70, y=60, height=25, width=500)
btnSfoglia = ttk.Button(pgPercorso, text="Sfoglia", command=btnSfoglia_Click)
btnSfoglia.place(x=580, y=60, height=25, width=90)
varSubDir = tk.IntVar(value=1)
chkSubDir = ttk.Checkbutton(pgPercorso, text="Ricerca nelle sottocartelle", onvalue=1, offvalue=0, variable=varSubDir)
chkSubDir.place(x=70, y=100, height=25, width=160)
##---Contenuto Frame #2---
varDataAttiva = tk.IntVar(value=0)
chkDataAttiva = ttk.Checkbutton(pgData, text="Abilita filtro per data ultima modifica:", variable=varDataAttiva, onvalue=1, offvalue=0, command=chkDataAttiva_Check)
chkDataAttiva.place(x=10, y=10, height=25)
lblDataInizio = ttk.Label(pgData, text="Data da:", anchor='e')
lblDataInizio.place(x=10, y=40, width=54, height=25)
calDataInizio = DateEntry(pgData, width=12, background='darkblue', foreground='white', borderwidth=2, date_pattern='dd/mm/yyyy')
calDataInizio.set_date(datetime.now())
calDataInizio.place(x=70, y=40, height=25)
lblOraInizio = ttk.Label(pgData, text="ora:")
lblOraInizio.place(x=170, y=40, height=25)
spnOraInizio = tk.Spinbox(pgData, from_=0, to=23, width=2, format="%02.0f")
spnOraInizio.place(x=200, y=40, height=25, width=40)
lblSep1 = ttk.Label(pgData, text=":")
lblSep1.place(x=240, y=40, width=10, height=25)
spnMinInizio = tk.Spinbox(pgData, from_=0, to=59, width=2, format="%02.0f")
spnMinInizio.place(x=250, y=40, height=25, width=40)
lblDataFine = ttk.Label(pgData, text="a:", anchor='e')
lblDataFine.place(x=10, y=80, width=54, height=25)
calDataFine = DateEntry(pgData, width=12, background='darkblue', foreground='white', borderwidth=2, date_pattern='dd/mm/yyyy')
calDataFine.set_date(datetime.now())
calDataFine.place(x=70, y=80, height=25)
lblOraFine = ttk.Label(pgData, text="ora:")
lblOraFine.place(x=170, y=80, height=25)
spnOraFine = tk.Spinbox(pgData, from_=0, to=23, width=2, format="%02.0f")
spnOraFine.place(x=200, y=80, height=25, width=40)
lblSep2 = ttk.Label(pgData, text=":")
lblSep2.place(x=240, y=80, width=10, height=25)
spnMinFine = tk.Spinbox(pgData, from_=0, to=59, width=2, format="%02.0f")
spnMinFine.place(x=250, y=80, height=25, width=40)
chkDataAttiva_Check()
##---Contenuto Frame #3---
#--- Contenuto Finestra principale ---
tvRisultati = ttk.Treeview(frmMain)
tvRisultati["columns"] = ("#0", "#1", "#2", "#3", "#4", "#5")
tvRisultati.column("#0", width=200, minwidth=200)
tvRisultati.column("#1", width=220, minwidth=220)
tvRisultati.column("#2", width=80, minwidth=80)
tvRisultati.column("#3", width=50, minwidth=50)
tvRisultati.column("#4", width=100, minwidth=100)
tvRisultati.column("#5", width=100, minwidth=100)
tvRisultati.heading("#0", text="Nome")
tvRisultati.heading("#1", text="Nella cartella")
tvRisultati.heading("#2", text="Dimensione")
tvRisultati.heading("#3", text="Tipo")
tvRisultati.heading("#4", text="Data creazione")
tvRisultati.heading("#5", text="Data modifica")
tvRisultati.place(x=4, y=200, height=266, width=770)
sbVerticale = ttk.Scrollbar(frmMain, orient="vertical", command=tvRisultati.yview)
sbVerticale.place(x=775, y=200, height=266)
tvRisultati.configure(yscrollcommand=sbVerticale.set)
lblBarraStato = ttk.Label(frmMain, text="Pronto.", relief=tk.SUNKEN, anchor='w')
lblBarraStato.place(x=4, y=468, width=790, height=24)
if __name__ == "__main__":
inizializza_avvio()
frmMain.mainloop()