OK
This commit is contained in:
+98
-32
@@ -1,36 +1,72 @@
|
||||
import tkinter as tk
|
||||
from tkinter import scrolledtext, ttk
|
||||
from tkinter import ttk
|
||||
from typing import Callable, Optional
|
||||
|
||||
from core.ui.theme import COLORS, FONT_MONO, FONT_SECTION, FONT_SMALL
|
||||
from core.ui.theme import COLORS, FONT_MONO, FONT_SMALL
|
||||
|
||||
|
||||
BUTTON_ICONS = {
|
||||
"应用": "✓",
|
||||
"开始": "▶",
|
||||
"批量": "▶",
|
||||
"测试": "▶",
|
||||
"停止": "■",
|
||||
"刷新": "⟳",
|
||||
"重新": "⟳",
|
||||
"自动": "◎",
|
||||
"启用": "▷",
|
||||
"禁用": "⊗",
|
||||
"修复": "◇",
|
||||
"复制": "⧉",
|
||||
"导出": "⇩",
|
||||
"导入": "⇧",
|
||||
"保存": "+",
|
||||
"删除": "×",
|
||||
}
|
||||
|
||||
|
||||
class Console:
|
||||
def __init__(self, parent, height: int = 16):
|
||||
self.widget = scrolledtext.ScrolledText(
|
||||
parent,
|
||||
self.frame = tk.Frame(parent, bg=COLORS["console_bg"], bd=0, highlightthickness=0)
|
||||
self.frame.rowconfigure(0, weight=1)
|
||||
self.frame.columnconfigure(0, weight=1)
|
||||
|
||||
self.widget = tk.Text(
|
||||
self.frame,
|
||||
height=height,
|
||||
wrap="word",
|
||||
bg=COLORS["console_bg"],
|
||||
fg=COLORS["console_fg"],
|
||||
insertbackground=COLORS["console_fg"],
|
||||
selectbackground="#2b4a6f",
|
||||
selectbackground="#17385c",
|
||||
relief="flat",
|
||||
borderwidth=0,
|
||||
font=FONT_MONO,
|
||||
padx=14,
|
||||
pady=12,
|
||||
padx=16,
|
||||
pady=14,
|
||||
)
|
||||
self.widget.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
scrollbar = ttk.Scrollbar(
|
||||
self.frame,
|
||||
orient="vertical",
|
||||
command=self.widget.yview,
|
||||
style="Modern.Vertical.TScrollbar",
|
||||
)
|
||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
self.widget.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
self.widget.tag_config("muted", foreground=COLORS["console_muted"])
|
||||
self.widget.tag_config("accent", foreground=COLORS["console_accent"])
|
||||
self.widget.tag_config("success", foreground="#86efac")
|
||||
self.widget.tag_config("warning", foreground="#fde68a")
|
||||
self.widget.tag_config("error", foreground="#fca5a5")
|
||||
|
||||
def grid(self, **kwargs):
|
||||
self.widget.grid(**kwargs)
|
||||
self.frame.grid(**kwargs)
|
||||
|
||||
def pack(self, **kwargs):
|
||||
self.widget.pack(**kwargs)
|
||||
self.frame.pack(**kwargs)
|
||||
|
||||
def clear(self) -> None:
|
||||
self.widget.delete("1.0", tk.END)
|
||||
@@ -45,30 +81,35 @@ class Console:
|
||||
|
||||
class Page(ttk.Frame):
|
||||
def __init__(self, parent, title: str, subtitle: str):
|
||||
super().__init__(parent, style="Panel.TFrame")
|
||||
super().__init__(parent, style="Page.TFrame")
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(1, weight=1)
|
||||
|
||||
header = ttk.Frame(self, style="Panel.TFrame")
|
||||
header.grid(row=0, column=0, sticky="ew", padx=28, pady=(24, 8))
|
||||
header = ttk.Frame(self, style="Page.TFrame")
|
||||
header.grid(row=0, column=0, sticky="ew", padx=28, pady=(28, 12))
|
||||
header.columnconfigure(0, weight=1)
|
||||
|
||||
ttk.Label(header, text=title, style="Title.TLabel").grid(row=0, column=0, sticky="w")
|
||||
ttk.Label(header, text=subtitle, style="Muted.TLabel").grid(row=1, column=0, sticky="w", pady=(4, 0))
|
||||
ttk.Label(header, text=subtitle, style="PageMuted.TLabel").grid(row=1, column=0, sticky="w", pady=(8, 0))
|
||||
|
||||
body_shell = ttk.Frame(self, style="Panel.TFrame")
|
||||
body_shell.grid(row=1, column=0, sticky="nsew", padx=28, pady=(4, 20))
|
||||
body_shell = ttk.Frame(self, style="Page.TFrame")
|
||||
body_shell.grid(row=1, column=0, sticky="nsew", padx=28, pady=(0, 22))
|
||||
body_shell.columnconfigure(0, weight=1)
|
||||
body_shell.rowconfigure(0, weight=1)
|
||||
|
||||
self._canvas = tk.Canvas(body_shell, bg=COLORS["panel"], highlightthickness=0, bd=0)
|
||||
self._canvas = tk.Canvas(body_shell, bg=COLORS["bg"], highlightthickness=0, bd=0)
|
||||
self._canvas.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
scrollbar = ttk.Scrollbar(body_shell, orient="vertical", command=self._canvas.yview)
|
||||
scrollbar.grid(row=0, column=1, sticky="ns", padx=(8, 0))
|
||||
self._canvas.configure(yscrollcommand=scrollbar.set)
|
||||
self._scrollbar = ttk.Scrollbar(
|
||||
body_shell,
|
||||
orient="vertical",
|
||||
command=self._canvas.yview,
|
||||
style="Modern.Vertical.TScrollbar",
|
||||
)
|
||||
self._scrollbar.grid(row=0, column=1, sticky="ns", padx=(8, 0))
|
||||
self._canvas.configure(yscrollcommand=self._sync_scrollbar)
|
||||
|
||||
self.body = ttk.Frame(self._canvas, style="Panel.TFrame")
|
||||
self.body = ttk.Frame(self._canvas, style="Page.TFrame")
|
||||
self.body.columnconfigure(0, weight=1)
|
||||
self._body_window = self._canvas.create_window((0, 0), window=self.body, anchor="nw")
|
||||
|
||||
@@ -77,14 +118,22 @@ class Page(ttk.Frame):
|
||||
self._bind_mousewheel(self._canvas)
|
||||
|
||||
def section(self, title: str, row: int, columns: int = 4):
|
||||
frame = tk.Frame(self.body, bg=COLORS["panel"], highlightbackground=COLORS["border"], highlightthickness=1)
|
||||
frame.grid(row=row, column=0, sticky="ew", pady=(0, 14))
|
||||
frame = tk.Frame(
|
||||
self.body,
|
||||
bg=COLORS["panel"],
|
||||
highlightbackground=COLORS["border"],
|
||||
highlightcolor=COLORS["border"],
|
||||
highlightthickness=1,
|
||||
bd=0,
|
||||
)
|
||||
frame.grid(row=row, column=0, sticky="ew", pady=(0, 16))
|
||||
for col in range(columns):
|
||||
frame.columnconfigure(col, weight=1)
|
||||
|
||||
ttk.Label(frame, text=title, style="Section.TLabel").grid(
|
||||
row=0, column=0, columnspan=columns, sticky="w", padx=18, pady=(14, 8)
|
||||
)
|
||||
title_bar = ttk.Frame(frame, style="Panel.TFrame")
|
||||
title_bar.grid(row=0, column=0, columnspan=columns, sticky="ew", padx=22, pady=(18, 10))
|
||||
ttk.Label(title_bar, text="▣", style="Section.TLabel", foreground=COLORS["primary"]).pack(side="left", padx=(0, 10))
|
||||
ttk.Label(title_bar, text=title, style="Section.TLabel").pack(side="left")
|
||||
return frame
|
||||
|
||||
def _update_scroll_region(self, _event=None) -> None:
|
||||
@@ -93,6 +142,13 @@ class Page(ttk.Frame):
|
||||
def _resize_body(self, event) -> None:
|
||||
self._canvas.itemconfigure(self._body_window, width=event.width)
|
||||
|
||||
def _sync_scrollbar(self, first: str, last: str) -> None:
|
||||
self._scrollbar.set(first, last)
|
||||
if float(first) <= 0 and float(last) >= 1:
|
||||
self._scrollbar.grid_remove()
|
||||
else:
|
||||
self._scrollbar.grid()
|
||||
|
||||
def _bind_mousewheel(self, widget) -> None:
|
||||
widget.bind("<Enter>", lambda _event: widget.bind_all("<MouseWheel>", self._on_mousewheel))
|
||||
widget.bind("<Leave>", lambda _event: widget.unbind_all("<MouseWheel>"))
|
||||
@@ -105,10 +161,10 @@ class Page(ttk.Frame):
|
||||
|
||||
def field(parent, label: str, row: int, column: int, value: str = "", width: int = 24, colspan: int = 1):
|
||||
frame = ttk.Frame(parent, style="Panel.TFrame")
|
||||
frame.grid(row=row, column=column, columnspan=colspan, sticky="ew", padx=18, pady=(4, 14))
|
||||
frame.grid(row=row, column=column, columnspan=colspan, sticky="ew", padx=22, pady=(4, 16))
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
ttk.Label(frame, text=label, style="Muted.TLabel").grid(row=0, column=0, sticky="w", pady=(0, 5))
|
||||
ttk.Label(frame, text=label, style="Muted.TLabel").grid(row=0, column=0, sticky="w", pady=(0, 7))
|
||||
var = tk.StringVar(value=value)
|
||||
entry = ttk.Entry(frame, textvariable=var, width=width)
|
||||
entry.grid(row=1, column=0, sticky="ew")
|
||||
@@ -118,10 +174,10 @@ def field(parent, label: str, row: int, column: int, value: str = "", width: int
|
||||
def combo(parent, label: str, row: int, column: int, values=None, value: str = "", width: int = 24, colspan: int = 1):
|
||||
values = values or []
|
||||
frame = ttk.Frame(parent, style="Panel.TFrame")
|
||||
frame.grid(row=row, column=column, columnspan=colspan, sticky="ew", padx=18, pady=(4, 14))
|
||||
frame.grid(row=row, column=column, columnspan=colspan, sticky="ew", padx=22, pady=(4, 16))
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
ttk.Label(frame, text=label, style="Muted.TLabel").grid(row=0, column=0, sticky="w", pady=(0, 5))
|
||||
ttk.Label(frame, text=label, style="Muted.TLabel").grid(row=0, column=0, sticky="w", pady=(0, 7))
|
||||
var = tk.StringVar(value=value)
|
||||
control = ttk.Combobox(frame, textvariable=var, values=values, width=width, state="readonly")
|
||||
control.grid(row=1, column=0, sticky="ew")
|
||||
@@ -130,15 +186,25 @@ def combo(parent, label: str, row: int, column: int, values=None, value: str = "
|
||||
|
||||
def action_bar(parent, row: int, columnspan: int = 4):
|
||||
frame = ttk.Frame(parent, style="Panel.TFrame")
|
||||
frame.grid(row=row, column=0, columnspan=columnspan, sticky="ew", padx=18, pady=(0, 16))
|
||||
frame.grid(row=row, column=0, columnspan=columnspan, sticky="ew", padx=22, pady=(0, 18))
|
||||
return frame
|
||||
|
||||
|
||||
def button(parent, text: str, command: Callable, style: str = "Secondary.TButton"):
|
||||
btn = ttk.Button(parent, text=text, command=command, style=style)
|
||||
btn.pack(side="left", padx=(0, 10))
|
||||
btn = ttk.Button(parent, text=_button_label(text), command=command, style=style)
|
||||
btn.pack(side="left", padx=(0, 12))
|
||||
return btn
|
||||
|
||||
|
||||
def _button_label(text: str) -> str:
|
||||
stripped = text.strip()
|
||||
if not stripped:
|
||||
return text
|
||||
for keyword, icon in BUTTON_ICONS.items():
|
||||
if stripped.startswith(keyword):
|
||||
return f"{icon} {stripped}"
|
||||
return stripped
|
||||
|
||||
|
||||
def set_entry_state(item, enabled: bool) -> None:
|
||||
item["entry"].configure(state="normal" if enabled else "disabled")
|
||||
|
||||
Reference in New Issue
Block a user