66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
import tkinter as tk
|
|
from tkinter import ttk
|
|
from ui.theme import (
|
|
BG_DARK, BG_PANEL, BG_CONSOLE, FG_TEXT, FG_MUTED,
|
|
FG_HEADER, FG_ACCENT, FONT_SUB, FONT_NORMAL, FONT_CONSOLE
|
|
)
|
|
|
|
|
|
def create_path_row(parent, label_text, str_var, browse_fn, on_change=None):
|
|
"""Creates a consistent labeled path entry with browse button."""
|
|
lbl = tk.Label(parent, text=label_text, font=FONT_SUB, bg=BG_DARK, fg=FG_HEADER, anchor="w")
|
|
lbl.pack(fill="x", pady=(6, 2))
|
|
|
|
frame = tk.Frame(parent, bg=BG_PANEL)
|
|
frame.pack(fill="x", pady=2)
|
|
|
|
entry = tk.Entry(
|
|
frame, textvariable=str_var, font=FONT_NORMAL,
|
|
bg=BG_PANEL, fg=FG_TEXT, relief="flat", insertbackground=FG_TEXT
|
|
)
|
|
entry.pack(side="left", fill="x", expand=True, padx=8, pady=5)
|
|
if on_change:
|
|
str_var.trace_add("write", lambda *_: on_change())
|
|
|
|
btn = tk.Button(
|
|
frame, text="Waehlen...", font=FONT_NORMAL,
|
|
bg="#1e1e2e", fg=FG_ACCENT, activebackground="#2a2a40",
|
|
relief="flat", cursor="hand2", padx=10, command=browse_fn
|
|
)
|
|
btn.pack(side="right", padx=4, pady=3)
|
|
return frame
|
|
|
|
|
|
class ConsoleLogger:
|
|
"""Thread-safe text console with auto-scrolling."""
|
|
|
|
def __init__(self, parent, height=6):
|
|
self.frame = tk.Frame(parent, bg=BG_DARK)
|
|
self.text_widget = tk.Text(
|
|
self.frame, height=height, bg=BG_CONSOLE, fg=FG_ACCENT,
|
|
font=FONT_CONSOLE, relief="flat", insertbackground=FG_ACCENT
|
|
)
|
|
self.scrollbar = tk.Scrollbar(self.frame, command=self.text_widget.yview)
|
|
self.text_widget.configure(yscrollcommand=self.scrollbar.set)
|
|
self.text_widget.pack(side="left", fill="both", expand=True)
|
|
self.scrollbar.pack(side="right", fill="y")
|
|
|
|
def pack(self, **kwargs):
|
|
self.frame.pack(**kwargs)
|
|
|
|
def log(self, message):
|
|
"""Append log message and autoscroll to bottom."""
|
|
def _append():
|
|
try:
|
|
self.text_widget.insert("end", f"{message}\n")
|
|
self.text_widget.see("end")
|
|
except Exception:
|
|
pass
|
|
self.frame.after(0, _append)
|
|
|
|
def clear(self):
|
|
try:
|
|
self.text_widget.delete("1.0", "end")
|
|
except Exception:
|
|
pass
|