Анализ Windows и очистка кешей, мусора и остатков разработки. Ядро: - каталог правил: 228 правил путей (496 шаблонов) + 43 правила артефактов проектов — Windows, браузеры, кеши сборки, IDE, приложения, пакетные менеджеры; - рекурсивный поиск node_modules, target, build, __pycache__, .venv и т.п. с проверкой маркеров проекта, чтобы не ловить ложные срабатывания; - поиск крупных файлов и дубликатов (хеш по началу/середине/концу и полная сверка), инвентаризация установленных программ через реестр; - ограничение области сканирования по дискам; - рекомендации и текстовый отчёт. Безопасность: - четыре уровня риска, автоматически отмечается только «Безопасно»; - список защищённых путей проверяется и при выборе, и перед удалением; - для каталогов вроде %TEMP% удаляется содержимое, а не сам каталог; - удаление в Корзину по умолчанию, пробный запуск, журнал очисток. Интерфейс на Dear PyGui: - JetBrains Mono Nerd Font: кириллица и иконки в одном шрифте; - тёмная и светлая темы, адаптивная раскладка; - заголовок окна ставится через SetWindowTextW — Dear PyGui отдаёт его в ANSI-функцию как UTF-8 и портит кириллицу; - запуск всегда с правами администратора: манифест UAC в exe и самоперезапуск через ShellExecuteW при работе из исходников. Сборка в один exe через PyInstaller со вшитым шрифтом и значком. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
152 lines
6.2 KiB
Python
152 lines
6.2 KiB
Python
"""Оформление интерфейса."""
|
|
from __future__ import annotations
|
|
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
|
|
DARK = {
|
|
"bg": "#1b1d22",
|
|
"bg2": "#22252b",
|
|
"bg3": "#2a2e36",
|
|
"fg": "#e6e8ec",
|
|
"fg_dim": "#9aa1ad",
|
|
"accent": "#4a90d9",
|
|
"accent_hi": "#5fa4e8",
|
|
"ok": "#2e9e5b",
|
|
"warn": "#d99a2b",
|
|
"danger": "#d9534f",
|
|
"border": "#343943",
|
|
"sel": "#2f3a4a",
|
|
}
|
|
|
|
LIGHT = {
|
|
"bg": "#f4f5f7",
|
|
"bg2": "#ffffff",
|
|
"bg3": "#e9ebef",
|
|
"fg": "#1d2127",
|
|
"fg_dim": "#5f6773",
|
|
"accent": "#2f6fb5",
|
|
"accent_hi": "#3d82cf",
|
|
"ok": "#1e7a45",
|
|
"warn": "#a5710f",
|
|
"danger": "#b8332f",
|
|
"border": "#cfd4dc",
|
|
"sel": "#d6e4f5",
|
|
}
|
|
|
|
|
|
class Theme:
|
|
def __init__(self, root: tk.Misc, dark: bool = True) -> None:
|
|
self.root = root
|
|
self.dark = dark
|
|
self.c = DARK if dark else LIGHT
|
|
self.style = ttk.Style(root)
|
|
self.apply()
|
|
|
|
def toggle(self) -> None:
|
|
self.dark = not self.dark
|
|
self.c = DARK if self.dark else LIGHT
|
|
self.apply()
|
|
|
|
def apply(self) -> None:
|
|
c = self.c
|
|
s = self.style
|
|
try:
|
|
s.theme_use("clam")
|
|
except tk.TclError:
|
|
pass
|
|
|
|
self.root.configure(bg=c["bg"])
|
|
base_font = ("Segoe UI", 10)
|
|
|
|
s.configure(".", background=c["bg"], foreground=c["fg"], font=base_font,
|
|
fieldbackground=c["bg2"], bordercolor=c["border"],
|
|
lightcolor=c["bg2"], darkcolor=c["bg"])
|
|
s.configure("TFrame", background=c["bg"])
|
|
s.configure("Card.TFrame", background=c["bg2"], relief="flat")
|
|
s.configure("TLabel", background=c["bg"], foreground=c["fg"])
|
|
s.configure("Card.TLabel", background=c["bg2"], foreground=c["fg"])
|
|
s.configure("Dim.TLabel", background=c["bg"], foreground=c["fg_dim"])
|
|
s.configure("CardDim.TLabel", background=c["bg2"], foreground=c["fg_dim"])
|
|
s.configure("H1.TLabel", background=c["bg"], foreground=c["fg"],
|
|
font=("Segoe UI Semibold", 17))
|
|
s.configure("H2.TLabel", background=c["bg"], foreground=c["fg"],
|
|
font=("Segoe UI Semibold", 12))
|
|
s.configure("CardH2.TLabel", background=c["bg2"], foreground=c["fg"],
|
|
font=("Segoe UI Semibold", 12))
|
|
s.configure("Big.TLabel", background=c["bg"], foreground=c["accent_hi"],
|
|
font=("Segoe UI Semibold", 26))
|
|
s.configure("CardBig.TLabel", background=c["bg2"], foreground=c["accent_hi"],
|
|
font=("Segoe UI Semibold", 24))
|
|
|
|
s.configure("TButton", background=c["bg3"], foreground=c["fg"],
|
|
borderwidth=0, focusthickness=0, padding=(14, 7))
|
|
s.map("TButton",
|
|
background=[("active", c["border"]), ("disabled", c["bg2"])],
|
|
foreground=[("disabled", c["fg_dim"])])
|
|
|
|
s.configure("Accent.TButton", background=c["accent"], foreground="#ffffff",
|
|
padding=(16, 8), font=("Segoe UI Semibold", 10))
|
|
s.map("Accent.TButton",
|
|
background=[("active", c["accent_hi"]), ("disabled", c["bg3"])],
|
|
foreground=[("disabled", c["fg_dim"])])
|
|
|
|
s.configure("Danger.TButton", background=c["danger"], foreground="#ffffff",
|
|
padding=(16, 8), font=("Segoe UI Semibold", 10))
|
|
s.map("Danger.TButton", background=[("active", "#e2645f"), ("disabled", c["bg3"])],
|
|
foreground=[("disabled", c["fg_dim"])])
|
|
|
|
s.configure("TNotebook", background=c["bg"], borderwidth=0, tabmargins=(8, 6, 8, 0))
|
|
s.configure("TNotebook.Tab", background=c["bg"], foreground=c["fg_dim"],
|
|
padding=(18, 9), borderwidth=0, font=("Segoe UI", 10))
|
|
s.map("TNotebook.Tab",
|
|
background=[("selected", c["bg2"])],
|
|
foreground=[("selected", c["fg"])])
|
|
|
|
s.configure("Treeview", background=c["bg2"], fieldbackground=c["bg2"],
|
|
foreground=c["fg"], borderwidth=0, rowheight=26)
|
|
s.configure("Treeview.Heading", background=c["bg3"], foreground=c["fg_dim"],
|
|
borderwidth=0, padding=(8, 6), font=("Segoe UI Semibold", 9))
|
|
s.map("Treeview.Heading", background=[("active", c["border"])])
|
|
s.map("Treeview", background=[("selected", c["sel"])],
|
|
foreground=[("selected", c["fg"])])
|
|
|
|
s.configure("TCheckbutton", background=c["bg"], foreground=c["fg"],
|
|
focuscolor=c["bg"])
|
|
s.map("TCheckbutton", background=[("active", c["bg"])])
|
|
s.configure("Card.TCheckbutton", background=c["bg2"], foreground=c["fg"])
|
|
s.map("Card.TCheckbutton", background=[("active", c["bg2"])])
|
|
|
|
s.configure("TRadiobutton", background=c["bg"], foreground=c["fg"])
|
|
s.map("TRadiobutton", background=[("active", c["bg"])])
|
|
|
|
s.configure("TEntry", fieldbackground=c["bg2"], foreground=c["fg"],
|
|
bordercolor=c["border"], insertcolor=c["fg"], padding=6)
|
|
s.configure("TCombobox", fieldbackground=c["bg2"], foreground=c["fg"],
|
|
background=c["bg3"], arrowcolor=c["fg"])
|
|
|
|
s.configure("TProgressbar", background=c["accent"], troughcolor=c["bg3"],
|
|
borderwidth=0, thickness=6)
|
|
s.configure("Thin.Horizontal.TProgressbar", background=c["accent"],
|
|
troughcolor=c["bg3"], borderwidth=0, thickness=6)
|
|
|
|
s.configure("TSeparator", background=c["border"])
|
|
s.configure("TPanedwindow", background=c["bg"])
|
|
s.configure("Vertical.TScrollbar", background=c["bg3"], troughcolor=c["bg"],
|
|
borderwidth=0, arrowcolor=c["fg_dim"])
|
|
s.map("Vertical.TScrollbar", background=[("active", c["border"])])
|
|
s.configure("Horizontal.TScrollbar", background=c["bg3"], troughcolor=c["bg"],
|
|
borderwidth=0, arrowcolor=c["fg_dim"])
|
|
|
|
|
|
def tag_colors(theme: Theme) -> dict[str, dict]:
|
|
c = theme.c
|
|
return {
|
|
"safe": {"foreground": c["ok"]},
|
|
"low": {"foreground": c["accent_hi"]},
|
|
"medium": {"foreground": c["warn"]},
|
|
"high": {"foreground": c["danger"]},
|
|
"cat": {"foreground": c["fg"], "font": ("Segoe UI Semibold", 10)},
|
|
"dim": {"foreground": c["fg_dim"]},
|
|
}
|