fix: tqdm-Patch durch Timeout-basierten Wartebalken ersetzen
huggingface_hub nutzt jetzt Xet (Rust-Engine) fuer model.bin-Downloads, welche Python-tqdm komplett bypassen. Der Dialog erschien deshalb nie. Neuer Ansatz: Nach 500ms Wartezeit wird ein indeterminater Wartebalken angezeigt -- sowohl bei Downloads als auch bei langsamer Initialisierung. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,10 +39,11 @@ def load_model_with_progress(
|
|||||||
compute_type: str,
|
compute_type: str,
|
||||||
download_root: str | None,
|
download_root: str | None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""Lädt WhisperModel — zeigt bei Bedarf einen Download-Fortschrittsdialog.
|
"""Lädt WhisperModel — zeigt bei längerem Laden einen Wartebalken.
|
||||||
|
|
||||||
Wenn das Modell bereits gecacht ist (kein tqdm-Update kommt), erscheint
|
Wenn das Modell in unter 500 ms bereit ist (vollständiger Cache), erscheint
|
||||||
kein Dialog. Auf Download-Fehler wird ein Fehlerdialog gezeigt und sys.exit(1)
|
kein Dialog. Bei Download oder langsamer Initialisierung wird ein indeterminater
|
||||||
|
Wartebalken angezeigt. Auf Fehler wird ein Fehlerdialog gezeigt und sys.exit(1)
|
||||||
aufgerufen.
|
aufgerufen.
|
||||||
"""
|
"""
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
@@ -52,14 +53,11 @@ def load_model_with_progress(
|
|||||||
|
|
||||||
from whisper_local.tray._theme import apply_system_theme
|
from whisper_local.tray._theme import apply_system_theme
|
||||||
|
|
||||||
q: queue.Queue[dict[str, Any] | None] = queue.Queue()
|
result: list[Any] = [None]
|
||||||
result: list[WhisperModel | None] = [None]
|
|
||||||
error: list[BaseException | None] = [None]
|
error: list[BaseException | None] = [None]
|
||||||
original_tqdm = tqdm_module.tqdm
|
done_event = threading.Event()
|
||||||
|
|
||||||
def worker() -> None:
|
def worker() -> None:
|
||||||
TkProgressTqdm._queue = q
|
|
||||||
tqdm_module.tqdm = TkProgressTqdm
|
|
||||||
try:
|
try:
|
||||||
result[0] = WhisperModel(
|
result[0] = WhisperModel(
|
||||||
model_name,
|
model_name,
|
||||||
@@ -69,16 +67,23 @@ def load_model_with_progress(
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
error[0] = exc
|
error[0] = exc
|
||||||
finally:
|
finally:
|
||||||
tqdm_module.tqdm = original_tqdm
|
done_event.set()
|
||||||
TkProgressTqdm._queue = None
|
|
||||||
q.put(None) # Sentinel: signalisiert Fertigstellung
|
|
||||||
|
|
||||||
thread = threading.Thread(target=worker, daemon=True)
|
thread = threading.Thread(target=worker, daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
# --- tkinter-Dialog (lazy: erscheint nur bei echtem Download) ---
|
# Kurz warten – wenn Modell sofort bereit (vollständiger Cache), kein Dialog
|
||||||
|
if done_event.wait(timeout=0.5):
|
||||||
|
if error[0] is not None:
|
||||||
|
root = tk.Tk()
|
||||||
|
root.withdraw()
|
||||||
|
messagebox.showerror("Fehler beim Modell-Laden", str(error[0]))
|
||||||
|
root.destroy()
|
||||||
|
sys.exit(1)
|
||||||
|
return result[0]
|
||||||
|
|
||||||
|
# Modell braucht länger (Download oder Initialisierung) → Dialog anzeigen
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
root.withdraw() # zunächst versteckt
|
|
||||||
root.title("whisper-local – Modell wird geladen")
|
root.title("whisper-local – Modell wird geladen")
|
||||||
root.resizable(False, False)
|
root.resizable(False, False)
|
||||||
apply_system_theme(root)
|
apply_system_theme(root)
|
||||||
@@ -87,48 +92,26 @@ def load_model_with_progress(
|
|||||||
frame.pack(fill=tk.BOTH, expand=True)
|
frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
ttk.Label(frame, text=f"Lade Whisper-Modell '{model_name}'...").pack(anchor=tk.W)
|
ttk.Label(frame, text=f"Lade Whisper-Modell '{model_name}'...").pack(anchor=tk.W)
|
||||||
|
ttk.Label(frame, text="Bitte warten\u2026", foreground="gray").pack(
|
||||||
|
anchor=tk.W, pady=(4, 8)
|
||||||
|
)
|
||||||
|
|
||||||
file_var = tk.StringVar(value="")
|
pb = ttk.Progressbar(frame, length=320, mode="indeterminate")
|
||||||
ttk.Label(frame, textvariable=file_var, foreground="gray").pack(anchor=tk.W, pady=(4, 8))
|
pb.pack(fill=tk.X)
|
||||||
|
pb.start(10)
|
||||||
progress_var = tk.DoubleVar(value=0.0)
|
|
||||||
ttk.Progressbar(
|
|
||||||
frame, variable=progress_var, maximum=100, length=320, mode="determinate"
|
|
||||||
).pack(fill=tk.X)
|
|
||||||
|
|
||||||
pct_var = tk.StringVar(value="0 %")
|
|
||||||
ttk.Label(frame, textvariable=pct_var).pack(anchor=tk.E, pady=(2, 0))
|
|
||||||
|
|
||||||
_dialog_shown = False
|
|
||||||
|
|
||||||
def poll() -> None:
|
def poll() -> None:
|
||||||
nonlocal _dialog_shown
|
if done_event.is_set():
|
||||||
try:
|
root.quit()
|
||||||
while True:
|
return
|
||||||
msg = q.get_nowait()
|
root.after(100, poll)
|
||||||
if msg is None: # Sentinel → fertig
|
|
||||||
root.quit()
|
|
||||||
return
|
|
||||||
# Erste echte Meldung → Dialog anzeigen
|
|
||||||
if not _dialog_shown:
|
|
||||||
root.deiconify()
|
|
||||||
_dialog_shown = True
|
|
||||||
# UI aktualisieren
|
|
||||||
file_var.set(msg["file"])
|
|
||||||
if msg["total"] > 0:
|
|
||||||
pct = 100.0 * msg["n"] / msg["total"]
|
|
||||||
progress_var.set(pct)
|
|
||||||
pct_var.set(f"{pct:.0f} %")
|
|
||||||
except queue.Empty:
|
|
||||||
pass
|
|
||||||
root.after(50, poll)
|
|
||||||
|
|
||||||
root.after(50, poll)
|
root.after(100, poll)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|
||||||
if error[0] is not None:
|
if error[0] is not None:
|
||||||
root.withdraw()
|
root.withdraw()
|
||||||
messagebox.showerror("Fehler beim Modell-Download", str(error[0]))
|
messagebox.showerror("Fehler beim Modell-Laden", str(error[0]))
|
||||||
root.destroy()
|
root.destroy()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user