Files
Finance-Control/app/ui/reports_frame.py

110 lines
4.2 KiB
Python

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("Готово", "Отчёт сохранён.")