140 lines
5.1 KiB
Python
140 lines
5.1 KiB
Python
import customtkinter as ctk
|
|
|
|
from app.config import APP_SUBTITLE, APP_TITLE, MIN_HEIGHT, MIN_WIDTH, WINDOW_SIZE
|
|
from app.services import FinanceService
|
|
from app.ui.accounts_frame import AccountsFrame
|
|
from app.ui.budgets_frame import BudgetsFrame
|
|
from app.ui.categories_frame import CategoriesFrame
|
|
from app.ui.dashboard_frame import DashboardFrame
|
|
from app.ui.reports_frame import ReportsFrame
|
|
from app.ui.subscriptions_frame import SubscriptionsFrame
|
|
from app.ui.transactions_frame import TransactionsFrame
|
|
|
|
|
|
class FinanceApp(ctk.CTk):
|
|
"""Main application window and screen coordinator."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.service = FinanceService()
|
|
self.frames: dict[str, ctk.CTkFrame] = {}
|
|
self.nav_buttons: dict[str, ctk.CTkButton] = {}
|
|
|
|
saved_theme = self.service.get_setting("theme", "Тёмная")
|
|
ctk.set_appearance_mode(self._theme_to_mode(saved_theme))
|
|
ctk.set_default_color_theme("blue")
|
|
|
|
self.title(APP_TITLE)
|
|
self.geometry(WINDOW_SIZE)
|
|
self.minsize(MIN_WIDTH, MIN_HEIGHT)
|
|
|
|
self.grid_columnconfigure(1, weight=1)
|
|
self.grid_rowconfigure(0, weight=1)
|
|
|
|
self.sidebar = ctk.CTkFrame(self, width=260, corner_radius=0)
|
|
self.sidebar.grid(row=0, column=0, sticky="nsew")
|
|
self.sidebar.grid_rowconfigure(12, weight=1)
|
|
|
|
self.content = ctk.CTkFrame(self, fg_color="transparent")
|
|
self.content.grid(row=0, column=1, sticky="nsew", padx=18, pady=18)
|
|
self.content.grid_rowconfigure(0, weight=1)
|
|
self.content.grid_columnconfigure(0, weight=1)
|
|
|
|
self._build_sidebar(saved_theme)
|
|
self._build_frames()
|
|
self.show_frame("Главная")
|
|
self.refresh_all()
|
|
|
|
def _build_sidebar(self, saved_theme: str) -> None:
|
|
ctk.CTkLabel(
|
|
self.sidebar,
|
|
text="Finance Control",
|
|
font=ctk.CTkFont(size=26, weight="bold"),
|
|
).grid(row=0, column=0, padx=20, pady=(24, 4), sticky="w")
|
|
|
|
ctk.CTkLabel(
|
|
self.sidebar,
|
|
text=APP_SUBTITLE,
|
|
text_color="gray70",
|
|
wraplength=210,
|
|
).grid(row=1, column=0, padx=20, pady=(0, 18), sticky="w")
|
|
|
|
pages = [
|
|
"Главная",
|
|
"Счета",
|
|
"Категории",
|
|
"Операции",
|
|
"Подписки",
|
|
"Бюджеты",
|
|
"Отчёты",
|
|
]
|
|
for index, page_name in enumerate(pages, start=2):
|
|
button = ctk.CTkButton(
|
|
self.sidebar,
|
|
text=page_name,
|
|
height=44,
|
|
anchor="w",
|
|
command=lambda name=page_name: self.show_frame(name),
|
|
)
|
|
button.grid(row=index, column=0, padx=20, pady=5, sticky="ew")
|
|
self.nav_buttons[page_name] = button
|
|
|
|
ctk.CTkLabel(
|
|
self.sidebar,
|
|
text="Тема интерфейса",
|
|
text_color="gray70",
|
|
).grid(row=13, column=0, padx=20, pady=(12, 6), sticky="w")
|
|
|
|
self.theme_menu = ctk.CTkOptionMenu(
|
|
self.sidebar,
|
|
values=["Тёмная", "Светлая", "Системная"],
|
|
command=self.change_theme,
|
|
)
|
|
self.theme_menu.set(saved_theme)
|
|
self.theme_menu.grid(row=14, column=0, padx=20, pady=(0, 20), sticky="ew")
|
|
|
|
def _build_frames(self) -> None:
|
|
self.frames = {
|
|
"Главная": DashboardFrame(self.content, self.refresh_all),
|
|
"Счета": AccountsFrame(self.content, self.refresh_all),
|
|
"Категории": CategoriesFrame(self.content, self.refresh_all),
|
|
"Операции": TransactionsFrame(self.content, self.refresh_all),
|
|
"Подписки": SubscriptionsFrame(self.content, self.refresh_all),
|
|
"Бюджеты": BudgetsFrame(self.content, self.refresh_all),
|
|
"Отчёты": ReportsFrame(self.content, self.refresh_all),
|
|
}
|
|
for frame in self.frames.values():
|
|
frame.grid(row=0, column=0, sticky="nsew")
|
|
|
|
def show_frame(self, name: str) -> None:
|
|
frame = self.frames[name]
|
|
if hasattr(frame, "refresh_data"):
|
|
frame.refresh_data()
|
|
frame.tkraise()
|
|
self._highlight_nav_button(name)
|
|
|
|
def refresh_all(self) -> None:
|
|
for frame in self.frames.values():
|
|
if hasattr(frame, "refresh_data"):
|
|
frame.refresh_data()
|
|
|
|
def _highlight_nav_button(self, active_name: str) -> None:
|
|
for name, button in self.nav_buttons.items():
|
|
if name == active_name:
|
|
button.configure(fg_color=("#2563eb", "#2563eb"))
|
|
else:
|
|
button.configure(fg_color=("#3b8ed0", "#1f6aa5"))
|
|
|
|
def change_theme(self, theme_name: str) -> None:
|
|
ctk.set_appearance_mode(self._theme_to_mode(theme_name))
|
|
self.service.set_setting("theme", theme_name)
|
|
|
|
@staticmethod
|
|
def _theme_to_mode(theme_name: str) -> str:
|
|
theme_map = {
|
|
"Тёмная": "dark",
|
|
"Светлая": "light",
|
|
"Системная": "system",
|
|
}
|
|
return theme_map.get(theme_name, "dark")
|