Files
SysDevCleaner/sysdevcleaner/gui/theme_tk.py
HeagBoKaT b01cf37e66 Первый выпуск SysDevCleaner
Анализ 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>
2026-09-11 14:55:00 +03:00

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"]},
}