背景介绍
在当今快节奏的生活中,合理管理个人财务变得越来越重要。许多人有记录支出的习惯,但往往缺乏有效的工具来分析这些数据。为了解决这个问题,我开发了一款个人月度支出分析工具,帮助用户直观了解自己的月度支出结构,从而更好地规划财务。
思路分析
这款工具的核心功能包括:
1. 图形界面:使用Python的tkinter库创建用户友好的界面
2. 数据解析:读取CSV格式的支出记录,统计各分类支出
3. 可视化展示:使用matplotlib生成饼图,直观展示支出占比
4. 结果导出:将统计结果保存为新的CSV文件
5. 错误处理:对无效数据和操作提供友好提示
工具的工作流程是:用户选择CSV文件 → 系统解析并统计数据 → 显示统计表格和饼图 → 用户可导出结果。
代码实现
以下是完整的Python代码实现:
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import csv
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from collections import defaultdict
import os
class ExpenseAnalyzer:
def __init__(self, root):
self.root = root
self.root.title("个人月度支出分析工具")
self.root.geometry("900x700")
# 初始化变量
self.csv_path = ""
self.analysis_result = []
# 创建GUI组件
self.create_widgets()
def create_widgets(self):
# 选择文件按钮
self.select_btn = ttk.Button(self.root, text="选择支出CSV文件", command=self.select_file)
self.select_btn.pack(pady=10)
# 显示当前选择的文件路径
self.file_label = ttk.Label(self.root, text="未选择文件")
self.file_label.pack(pady=5)
# 统计结果表格
self.result_frame = ttk.Frame(self.root)
self.result_frame.pack(pady=10, fill=tk.BOTH, expand=True)
# 表格标题
self.table_title = ttk.Label(self.result_frame, text="支出统计结果", font=("Arial", 12, "bold"))
self.table_title.pack(pady=5)
# 表格
self.tree = ttk.Treeview(self.result_frame, columns=("分类", "总金额(元)", "占比"), show="headings")
self.tree.heading("分类", text="分类")
self.tree.heading("总金额(元)", text="总金额(元)")
self.tree.heading("占比", text="占比")
# 设置列宽
self.tree.column("分类", width=100)
self.tree.column("总金额(元)", width=100)
self.tree.column("占比", width=100)
self.tree.pack(pady=5, fill=tk.BOTH, expand=True)
# 饼图区域
self.chart_frame = ttk.Frame(self.root)
self.chart_frame.pack(pady=10, fill=tk.BOTH, expand=True)
# 导出按钮
self.export_btn = ttk.Button(self.root, text="导出统计结果", command=self.export_results, state=tk.DISABLED)
self.export_btn.pack(pady=10)
def select_file(self):
"""选择CSV文件"""
file_path = filedialog.askopenfilename(
filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],
title="选择支出记录CSV文件"
)
if file_path:
self.csv_path = file_path
self.file_label.config(text=f"当前文件: {os.path.basename(file_path)}")
self.analyze_and_display()
def analyze_and_display(self):
"""分析支出数据并显示结果"""
try:
# 清空之前的结果
for item in self.tree.get_children():
self.tree.delete(item)
# 分析数据
result = self.analyze_expenses(self.csv_path)
if isinstance(result, str): # 如果返回的是错误信息
messagebox.showerror("错误", result)
return
self.analysis_result = result
# 显示统计结果
for row in result:
self.tree.insert("", tk.END, values=row)
# 生成饼图
self.generate_pie_chart()
# 启用导出按钮
self.export_btn.config(state=tk.NORMAL)
except Exception as e:
messagebox.showerror("错误", f"处理数据时发生错误: {str(e)}")
def analyze_expenses(self, csv_path):
"""分析支出数据"""
category_total = defaultdict(float)
total_expense = 0.0
try:
with open(csv_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
# 检查必要的字段
required_fields = ['日期', '分类', '金额']
for field in required_fields:
if field not in reader.fieldnames:
raise ValueError(f"CSV文件缺少必要字段: {field}")
for row in reader:
# 获取分类和金额
category = row['分类'].strip()
amount_str = row['金额'].strip()
if not category:
raise ValueError("分类字段不能为空")
try:
amount = float(amount_str)
except ValueError:
raise ValueError(f"金额 '{amount_str}' 不是有效的数字")
category_total[category] += amount
total_expense += amount
# 如果没有数据
if total_expense == 0:
raise ValueError("CSV文件中没有有效的支出记录")
# 计算占比并整理结果
result = []
for cat, amt in category_total.items():
ratio = (amt / total_expense) * 100
result.append((cat, round(amt, 1), f"{round(ratio, 1)}%"))
# 添加总支出行
result.append(("总支出", round(total_expense, 1), "100%"))
return result
except FileNotFoundError:
return "文件未找到"
except Exception as e:
return f"解析错误: {str(e)}"
def generate_pie_chart(self):
"""生成饼图"""
# 清空之前的图表
for widget in self.chart_frame.winfo_children():
widget.destroy()
# 准备数据(排除总支出行)
categories = []
amounts = []
percentages = []
for row in self.analysis_result[:-1]: # 排除最后一行总支出
categories.append(row[0])
amounts.append(row[1])
percentages.append(row[2])
# 创建饼图
fig, ax = plt.subplots(figsize=(6, 4), dpi=100)
wedges, texts, autotexts = ax.pie(amounts, labels=categories, autopct='%1.1f%%',
startangle=90, textprops={'fontsize': 10})
# 设置标题
ax.set_title("月度支出占比", fontsize=12)
# 调整布局
plt.tight_layout()
# 将matplotlib图表嵌入到tkinter窗口
canvas = FigureCanvasTkAgg(fig, master=self.chart_frame)
canvas.draw()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
def export_results(self):
"""导出统计结果为CSV文件"""
if not self.analysis_result:
messagebox.showwarning("警告", "没有可导出的统计结果")
return
# 获取原始文件名和路径
original_dir = os.path.dirname(self.csv_path)
original_name = os.path.basename(self.csv_path)
base_name, ext = os.path.splitext(original_name)
default_name = f"{base_name}_统计结果.csv"
# 选择保存路径
save_path = filedialog.asksaveasfilename(
defaultextension=".csv",
filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],
initialfile=default_name,
initialdir=original_dir,
title="保存统计结果"
)
if save_path:
try:
with open(save_path, 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
# 写入表头
writer.writerow(["分类", "总金额", "占比"])
# 写入数据
for row in self.analysis_result:
writer.writerow(row)
messagebox.showinfo("成功", f"统计结果已导出到:\n{save_path}")
except Exception as e:
messagebox.showerror("错误", f"导出失败: {str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = ExpenseAnalyzer(root)
root.mainloop()
总结
这款个人月度支出分析工具提供了一个直观、易用的方式来分析个人财务状况。通过简单的CSV文件导入,用户可以快速了解自己的支出结构,识别主要的支出类别,并通过饼图直观地看到各项支出的占比。工具还支持将分析结果导出为新的CSV文件,方便用户进一步处理或存档。
该工具的主要特点包括:
1. 用户友好的界面:通过tkinter创建直观的图形界面
2. 强大的数据处理:能够解析CSV格式的支出记录并进行统计分析
3. 直观的可视化:使用饼图清晰展示支出占比
4. 灵活的导出功能:支持将统计结果保存为CSV文件
5. 完善的错误处理:对各种异常情况提供友好提示
无论是个人财务管理新手还是有经验的用户,这款工具都能帮助你更好地理解和管理自己的财务状况,为制定合理的预算和储蓄计划提供数据支持。