финальная версия Finance Control
This commit is contained in:
111
app/ui/base_frame.py
Normal file
111
app/ui/base_frame.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from collections.abc import Callable
|
||||
from tkinter import messagebox
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from app.config import (
|
||||
DANGER_COLOR,
|
||||
DANGER_HOVER_COLOR,
|
||||
PRIMARY_COLOR,
|
||||
PRIMARY_HOVER_COLOR,
|
||||
SUCCESS_COLOR,
|
||||
WARNING_COLOR,
|
||||
)
|
||||
from app.services import FinanceService
|
||||
from app.utils.formatters import format_money
|
||||
|
||||
|
||||
class BaseAppFrame(ctk.CTkFrame):
|
||||
"""Base class for application screens with shared UI helpers."""
|
||||
|
||||
def __init__(self, master, refresh_callback: Callable[[], None]) -> None:
|
||||
super().__init__(master)
|
||||
self.refresh_callback = refresh_callback
|
||||
self.service = FinanceService()
|
||||
self._busy = False
|
||||
|
||||
def run_safe(
|
||||
self,
|
||||
action: Callable[[], None],
|
||||
button: ctk.CTkButton | None = None,
|
||||
) -> None:
|
||||
"""Run UI action with protection from repeated rapid clicks."""
|
||||
if self._busy:
|
||||
return
|
||||
self._busy = True
|
||||
if button is not None:
|
||||
button.configure(state="disabled")
|
||||
try:
|
||||
action()
|
||||
except ValueError as error:
|
||||
messagebox.showerror("Ошибка ввода", str(error))
|
||||
except Exception as error: # noqa: BLE001 - user-facing desktop app guard
|
||||
messagebox.showerror("Ошибка", f"Действие не выполнено: {error}")
|
||||
finally:
|
||||
if button is not None:
|
||||
self.after(350, lambda: button.configure(state="normal"))
|
||||
self.after(350, self._unlock)
|
||||
else:
|
||||
self._unlock()
|
||||
|
||||
def _unlock(self) -> None:
|
||||
self._busy = False
|
||||
|
||||
@staticmethod
|
||||
def clear(container: ctk.CTkBaseClass) -> None:
|
||||
for widget in container.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
@staticmethod
|
||||
def money(value: float | int | None) -> str:
|
||||
return format_money(value)
|
||||
|
||||
@staticmethod
|
||||
def title_label(master, text: str) -> ctk.CTkLabel:
|
||||
return ctk.CTkLabel(
|
||||
master,
|
||||
text=text,
|
||||
font=ctk.CTkFont(size=28, weight="bold"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def section_label(master, text: str) -> ctk.CTkLabel:
|
||||
return ctk.CTkLabel(
|
||||
master,
|
||||
text=text,
|
||||
font=ctk.CTkFont(size=18, weight="bold"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def subtitle_label(master, text: str) -> ctk.CTkLabel:
|
||||
return ctk.CTkLabel(master, text=text, text_color="gray70")
|
||||
|
||||
@staticmethod
|
||||
def primary_button(master, text: str, command) -> ctk.CTkButton:
|
||||
return ctk.CTkButton(
|
||||
master,
|
||||
text=text,
|
||||
height=42,
|
||||
fg_color=PRIMARY_COLOR,
|
||||
hover_color=PRIMARY_HOVER_COLOR,
|
||||
command=command,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def danger_button(master, text: str, command, width: int = 92) -> ctk.CTkButton:
|
||||
return ctk.CTkButton(
|
||||
master,
|
||||
text=text,
|
||||
width=width,
|
||||
fg_color=DANGER_COLOR,
|
||||
hover_color=DANGER_HOVER_COLOR,
|
||||
command=command,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def status_color(value: float) -> str:
|
||||
if value >= 1:
|
||||
return DANGER_COLOR
|
||||
if value >= 0.8:
|
||||
return WARNING_COLOR
|
||||
return SUCCESS_COLOR
|
||||
Reference in New Issue
Block a user