from tkinter import filedialog, messagebox import customtkinter as ctk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure from app.ui.base_frame import BaseAppFrame class ReportsFrame(BaseAppFrame): """Screen with text reports and charts.""" def __init__(self, master, refresh_callback) -> None: super().__init__(master, refresh_callback) self.last_report_text = "" self.grid_columnconfigure((0, 1), weight=1) self.grid_rowconfigure(1, weight=1) self._build_header() self._build_left_panel() self._build_right_panel() def _build_header(self) -> None: header = ctk.CTkFrame(self, fg_color="transparent") header.grid(row=0, column=0, columnspan=2, sticky="ew", padx=8, pady=(8, 12)) header.grid_columnconfigure(0, weight=1) self.title_label(header, "Аналитика и отчёты").grid(row=0, column=0, sticky="w") self.subtitle_label( header, "Текстовая сводка, диаграмма расходов и экспорт отчёта.", ).grid(row=1, column=0, sticky="w", pady=(4, 0)) self.export_button = self.primary_button( header, "Сохранить отчёт в TXT", lambda: self.run_safe(self.export_report, self.export_button), ) self.export_button.grid(row=0, column=1, rowspan=2, padx=(12, 0)) def _build_left_panel(self) -> None: left = ctk.CTkFrame(self, corner_radius=20) left.grid(row=1, column=0, sticky="nsew", padx=(8, 12), pady=8) left.grid_rowconfigure(1, weight=1) left.grid_columnconfigure(0, weight=1) self.section_label(left, "Отчёт за текущий месяц").grid( row=0, column=0, sticky="w", padx=18, pady=(18, 8), ) self.report_box = ctk.CTkTextbox(left) self.report_box.grid(row=1, column=0, sticky="nsew", padx=18, pady=(0, 18)) def _build_right_panel(self) -> None: right = ctk.CTkFrame(self, corner_radius=20) right.grid(row=1, column=1, sticky="nsew", padx=(0, 8), pady=8) right.grid_rowconfigure(1, weight=1) right.grid_columnconfigure(0, weight=1) self.section_label(right, "Структура расходов").grid( row=0, column=0, sticky="w", padx=18, pady=(18, 8), ) self.chart_container = ctk.CTkFrame(right, fg_color="transparent") self.chart_container.grid(row=1, column=0, sticky="nsew", padx=18, pady=(0, 18)) self.figure = Figure(figsize=(5, 4), dpi=100) self.canvas = FigureCanvasTkAgg(self.figure, master=self.chart_container) self.canvas.get_tk_widget().pack(fill="both", expand=True) def refresh_data(self) -> None: self.last_report_text = self.service.build_report_text() self.report_box.delete("1.0", "end") self.report_box.insert("1.0", self.last_report_text) self._draw_chart() def _draw_chart(self) -> None: data = self.service.report_data() rows = data["categories"] self.figure.clear() axis = self.figure.add_subplot(111) axis.set_title("Расходы по категориям") if rows: labels = [row["name"] for row in rows] values = [row["total"] for row in rows] axis.pie(values, labels=labels, autopct="%1.1f%%") else: axis.text(0.5, 0.5, "Нет данных для диаграммы", ha="center", va="center") axis.axis("off") self.canvas.draw() def export_report(self) -> None: if not self.last_report_text: self.refresh_data() file_path = filedialog.asksaveasfilename( defaultextension=".txt", filetypes=[("Text files", "*.txt")], title="Сохранить отчёт", ) if not file_path: return with open(file_path, "w", encoding="utf-8") as file: file.write(self.last_report_text) messagebox.showinfo("Готово", "Отчёт сохранён.")