Gestione pacchetti pip con interfaccia grafica

Forum di discussioni su Python
Rispondi
FrancyDotNet
Moderatore
Moderatore
Messaggi: 981
Iscritto il: 01/05/2024, 23:26

Gestione pacchetti pip con interfaccia grafica

Messaggio da FrancyDotNet »

Il codice che segue include:
  • Finestra non ridimensionabile
  • Selezione/Deselezione di tutti i pacchetti
  • Mostra pacchetti selezionati
  • Aggiorna selezionati con possibilità di annullare
  • Barra di progresso + stato numerato
  • Evidenziazione dei pacchetti obsoleti in rosso
  • Compatibilità multipiattaforma

Codice: Seleziona tutto

import tkinter as tk
from tkinter import ttk, messagebox
import subprocess
import json
import threading

# Usa importlib.metadata invece di pkg_resources
try:
    from importlib.metadata import distributions
except ImportError:
    from importlib_metadata import distributions  # Python < 3.8

# Variabili globali per il controllo del processo
interrompi = False
in_aggiornamento = False

# Ottieni la lista dei pacchetti installati
def get_installed_packages():
    return sorted([dist.metadata["Name"] for dist in distributions()])

def get_outdated_packages():
    result = subprocess.run(
        ["pip", "list", "--outdated", "--format=json"],
        capture_output=True,
        text=True,
        check=True
    )
    return json.loads(result.stdout)

# Mostra i pacchetti selezionati
def mostra_selezionati():
    selezionati = [nome for nome, var in check_vars.items() if var.get()]
    if selezionati:
        print("Hai selezionato:")
        for nome in selezionati:
            print("-", nome)
    else:
        print("Nessun pacchetto selezionato.")

# Seleziona o deseleziona tutto
def toggle_selezione():
    global tutto_selezionato
    tutto_selezionato = not tutto_selezionato
    for var in check_vars.values():
        var.set(tutto_selezionato)
    btn_toggle.config(text="Deseleziona tutto" if tutto_selezionato else "Seleziona tutto")

# Aggiorna i pacchetti selezionati con pip (in thread)
def aggiorna_selezionati():
    global interrompi, in_aggiornamento

    # Se già in aggiornamento, l'utente vuole annullare
    if in_aggiornamento:
        interrompi = True
        btn_aggiorna.config(text="Aggiorna selezionati", state="disabled")
        return

    def worker():
        nonlocal selezionati
        totale = len(selezionati)
        progress["value"] = 0
        progress["maximum"] = totale
        errori = []

        for i, pkg in enumerate(selezionati, start=1):
            if interrompi:
                break

            root.after(0, status_label.config, {"text": f"[{i}/{totale}] Aggiornamento di: {pkg}"})
            root.after(0, progress.config, {"value": i})
            root.update_idletasks()

            try:
                result = subprocess.run(
                    ["python", "-m", "pip", "install", "--upgrade", pkg],
                    capture_output=True,
                    text=True,
                    check=True
                )
                print(result.stdout)
            except subprocess.CalledProcessError as e:
                print(f"Errore aggiornando {pkg}:\n{e.stderr}")
                errori.append(pkg)

        # Ripristina interfaccia
        def fine_aggiornamento():
            global interrompi, in_aggiornamento
            status_label.config(text="Stato: annullato" if interrompi else "Stato: completato")
            btn_aggiorna.config(text="Aggiorna selezionati", state="normal")
            btn_toggle.config(state="normal")
            btn_mostra.config(state="normal")
            for widget in scrollable_frame.winfo_children():
                widget.config(state="normal")
            in_aggiornamento = False
            interrompi = False
            if not interrompi:
                if errori:
                    messagebox.showwarning("Completato con errori", f"{len(errori)} pacchetti non aggiornati.")
                else:
                    messagebox.showinfo("Aggiornamento completato", "Tutti i pacchetti aggiornati con successo.")

        root.after(0, fine_aggiornamento)

    # Step iniziale: disabilita GUI, prepara lista
    selezionati = [nome for nome, var in check_vars.items() if var.get()]
    if not selezionati:
        messagebox.showinfo("Aggiornamento", "Nessun pacchetto selezionato.")
        return

    risposta = messagebox.askyesno(
        "Conferma aggiornamento",
        f"Vuoi davvero aggiornare {len(selezionati)} pacchetti?"
    )
    if not risposta:
        return

    # Blocca UI e avvia thread
    interrompi = False
    in_aggiornamento = True
    btn_aggiorna.config(text="Annulla aggiornamento")
    btn_toggle.config(state="disabled")
    btn_mostra.config(state="disabled")
    for widget in scrollable_frame.winfo_children():
        widget.config(state="disabled")

    threading.Thread(target=worker, daemon=True).start()

# ----------------- GUI -----------------

root = tk.Tk()
root.title("Pacchetti Python installati")
root.resizable(False, False)  # Finestra fissa

# Frame principale a griglia
main_frame = ttk.Frame(root)
main_frame.pack(fill="both", expand=True)

# Canvas + scrollbar sinistra
canvas = tk.Canvas(main_frame, width=300, height=400)
scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
scrollable_frame = ttk.Frame(canvas)

scrollable_frame.bind(
    "<Configure>",
    lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)

canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)

canvas.grid(row=0, column=0, sticky="nsew")
scrollbar.grid(row=0, column=1, sticky="ns")

# Pannello laterale con pulsanti
side_panel = ttk.Frame(main_frame, padding=10)
side_panel.grid(row=0, column=2, sticky="ns")

btn_toggle = ttk.Button(side_panel, text="Seleziona tutto", command=toggle_selezione)
btn_toggle.pack(pady=5)

btn_mostra = ttk.Button(side_panel, text="Mostra selezionati", command=mostra_selezionati)
btn_mostra.pack(pady=5)

btn_aggiorna = ttk.Button(side_panel, text="Aggiorna selezionati", command=aggiorna_selezionati)
btn_aggiorna.pack(pady=5)

# Etichetta stato e barra di avanzamento
status_label = ttk.Label(side_panel, text="Stato: inattivo")
status_label.pack(pady=(10, 2))

progress = ttk.Progressbar(side_panel, orient="horizontal", length=150, mode="determinate")
progress.pack()

# Configura layout griglia
main_frame.columnconfigure(0, weight=1)
main_frame.rowconfigure(0, weight=1)

# Ottieni pacchetti obsoleti
outdated_pkgs = get_outdated_packages()

check_vars = {}
for pkg in get_installed_packages():
    var = tk.BooleanVar()
    
    if pkg in outdated_pkgs:
        # Pacchetto obsoleto → evidenziato in rosso
        lbl = tk.Label(scrollable_frame, text=pkg, fg="red", anchor="w")
        lbl.pack(anchor="w", padx=(2,0))
        chk = ttk.Checkbutton(scrollable_frame, variable=var)
        chk.pack(anchor="w", padx=(20,0))
    else:
        # Pacchetto aggiornato
        chk = ttk.Checkbutton(scrollable_frame, text=pkg, variable=var)
        chk.pack(anchor="w")
    
    check_vars[pkg] = var

# Stato iniziale del toggle
tutto_selezionato = False

# Avvio GUI
root.mainloop()
Rispondi

Torna a “Python”