whisper-dictation/whisper_app/log_window.py

120 lines
3.8 KiB
Python

# whisper_app/log_window.py
import os
import queue
import tkinter as tk
from whisper_app import app
BG = "#18181f"
BG2 = "#22222c"
BG3 = "#2c2c38"
BORDER = "#38384a"
FG = "#e8e8f0"
FG2 = "#7878a0"
AMBER = "#f5a623"
GREEN = "#4ade80"
RED = "#f87171"
YELLOW = "#facc15"
_sans = "Segoe UI" if os.name == "nt" else "sans-serif"
_mono = "Consolas" if os.name == "nt" else "monospace"
_window: tk.Toplevel | None = None
_text: tk.Text | None = None
_log_q: queue.Queue | None = None
_MAX_LINES = 200
def create(root: tk.Tk, log_queue: queue.Queue,
on_settings, on_vocab) -> tk.Toplevel:
global _window, _text, _log_q
_log_q = log_queue
win = tk.Toplevel(root)
win.title("Whisper Dictation")
win.configure(bg=BG)
win.resizable(True, True)
win.minsize(400, 300)
win.protocol("WM_DELETE_WINDOW", win.withdraw)
# ── Header ──
hdr = tk.Frame(win, bg=BG2)
hdr.pack(fill="x")
tk.Frame(hdr, bg=AMBER, height=2).pack(fill="x")
hdr_inner = tk.Frame(hdr, bg=BG2, padx=12, pady=6)
hdr_inner.pack(fill="x")
tk.Label(hdr_inner, text="WHISPER DICTATION",
font=(_sans, 11, "bold"), bg=BG2, fg=FG).pack(side="left")
tk.Button(hdr_inner, text="", command=win.withdraw,
bg=BG2, fg=FG2, relief="flat", bd=0,
font=(_sans, 11), cursor="hand2").pack(side="right")
# ── Log text ──
txt = tk.Text(win, bg=BG, fg=FG, font=(_mono, 10),
relief="flat", bd=0, padx=10, pady=6,
state="disabled", wrap="none",
width=48, height=10,
highlightthickness=0)
txt.pack(fill="both", expand=True)
txt.tag_config("green", foreground=GREEN)
txt.tag_config("red", foreground=RED)
txt.tag_config("yellow", foreground=YELLOW)
txt.tag_config("grey", foreground=FG2)
_text = txt
# ── Button bar ──
bar = tk.Frame(win, bg=BG2, pady=6, padx=10)
bar.pack(fill="x")
for label, cmd in [("⚙ Einstellungen", on_settings), ("📚 Vokabular", on_vocab)]:
b = tk.Button(bar, text=label, command=cmd,
bg=BG3, fg=FG, relief="flat", bd=0,
font=(_sans, 10), padx=10, pady=4, cursor="hand2")
b.pack(side="left", padx=(0, 4))
tk.Button(bar, text="🗑", command=_clear_log,
bg=BG3, fg=FG2, relief="flat", bd=0,
font=(_sans, 10), padx=8, pady=4, cursor="hand2").pack(side="right")
_window = win
win.withdraw()
root.after(100, _poll)
return win
def show() -> None:
if _window:
_window.deiconify()
_window.lift()
def _clear_log() -> None:
if _text:
_text.config(state="normal")
_text.delete("1.0", "end")
_text.config(state="disabled")
def _tag_for(msg: str) -> str:
low = msg.lower()
if any(x in low for x in ("recording", "aufnahme")):
return "red"
if any(x in low for x in ("transcrib", "loading", "laden")):
return "yellow"
if any(x in low for x in ("result:", "ready", "bereit")):
return "green"
return "grey"
def _poll() -> None:
if _log_q and _text:
try:
while True:
msg = _log_q.get_nowait()
_append(msg)
except queue.Empty:
pass
if app.overlay_tk:
app.overlay_tk.after(100, _poll)
def _append(msg: str) -> None:
_text.config(state="normal")
# Trim to MAX_LINES
lines = int(_text.index("end-1c").split(".")[0])
if lines >= _MAX_LINES:
_text.delete("1.0", "2.0")
_text.insert("end", msg + "\n", _tag_for(msg))
_text.see("end")
_text.config(state="disabled")