import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import json
import datetime
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pd
from tkcalendar import DateEntry
class PersonalFinanceManager:
def __init__(self, root):
self.root = root
self.root.title("个人财务管理系统 v2.0")
self.root.geometry("1200x700")
self.root.configure(bg='#f0f0f0')
# 初始化数据
self.users = {}
self.current_user = None
self.transactions = []
self.categories = {
'收入': ['工资', '奖金', '投资回报', '其他收入'],
'支出': ['食品', '交通', '住房', '娱乐', '医疗', '教育', '购物', '其他支出']
}
self.budgets = {}
# 创建界面
self.create_login_screen()
self.load_data()
def create_login_screen(self):
"""创建登录/注册界面"""
self.clear_screen()
# 主框架
main_frame = tk.Frame(self.root, bg='#f0f0f0', padx=50, pady=30)
main_frame.pack(expand=True)
# 标题
title_label = tk.Label(main_frame, text="个人财务管理系统",
font=('Arial', 24, 'bold'), bg='#f0f0f0', fg='#2c3e50')
title_label.pack(pady=20)
# 登录框架
login_frame = tk.LabelFrame(main_frame, text="用户登录", font=('Arial', 12),
bg='#f0f0f0', padx=20, pady=20)
login_frame.pack(pady=20)
# 用户名
tk.Label(login_frame, text="用户名:", bg='#f0f0f0', font=('Arial', 10)).grid(row=0, column=0, pady=10, sticky='e')
self.username_entry = tk.Entry(login_frame, width=20, font=('Arial', 10))
self.username_entry.grid(row=0, column=1, pady=10, padx=10)
# 密码
tk.Label(login_frame, text="密码:", bg='#f0f0f0', font=('Arial', 10)).grid(row=1, column=0, pady=10, sticky='e')
self.password_entry = tk.Entry(login_frame, show='*', width=20, font=('Arial', 10))
self.password_entry.grid(row=1, column=1, pady=10, padx=10)
# 按钮框架
button_frame = tk.Frame(login_frame, bg='#f0f0f0')
button_frame.grid(row=2, column=0, columnspan=2, pady=20)
tk.Button(button_frame, text="登录", command=self.login,
bg='#3498db', fg='white', font=('Arial', 10), width=10).pack(side='left', padx=10)
tk.Button(button_frame, text="注册", command=self.register,
bg='#2ecc71', fg='white', font=('Arial', 10), width=10).pack(side='left', padx=10)
# 绑定回车键
self.root.bind('<Return>', lambda event: self.login())
def create_main_interface(self):
"""创建主界面"""
self.clear_screen()
# 创建菜单栏
self.create_menu()
# 创建主框架
main_frame = tk.Frame(self.root, bg='#f0f0f0')
main_frame.pack(fill='both', expand=True, padx=10, pady=10)
# 左侧功能面板
self.create_sidebar(main_frame)
# 右侧内容区域
self.create_content_area(main_frame)
# 显示欢迎信息
self.show_welcome()
def create_menu(self):
"""创建菜单栏"""
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
# 文件菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="导出数据", command=self.export_data)
file_menu.add_command(label="导入数据", command=self.import_data)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.root.quit)
# 工具菜单
tool_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="工具", menu=tool_menu)
tool_menu.add_command(label="预算设置", command=self.show_budget_settings)
tool_menu.add_command(label="数据统计", command=self.show_statistics)
# 帮助菜单
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="帮助", menu=help_menu)
help_menu.add_command(label="关于", command=self.show_about)
def create_sidebar(self, parent):
"""创建侧边栏"""
sidebar = tk.Frame(parent, bg='#2c3e50', width=200)
sidebar.pack(side='left', fill='y', padx=(0, 10))
sidebar.pack_propagate(False)
# 用户信息
user_frame = tk.Frame(sidebar, bg='#34495e', pady=10)
user_frame.pack(fill='x', padx=10, pady=10)
tk.Label(user_frame, text=f"用户: {self.current_user}",
bg='#34495e', fg='white', font=('Arial', 10, 'bold')).pack()
# 功能按钮
buttons = [
("📊 仪表盘", self.show_welcome),
("💰 记账", self.show_add_transaction),
("📈 收支记录", self.show_transactions),
("📋 分类统计", self.show_category_stats),
("📅 月度报表", self.show_monthly_report),
("🎯 预算管理", self.show_budget_management),
("🚪 退出登录", self.logout)
]
for text, command in buttons:
btn = tk.Button(sidebar, text=text, command=command,
bg='#34495e', fg='white', font=('Arial', 10),
anchor='w', relief='flat', padx=20, pady=12)
btn.pack(fill='x', padx=10, pady=2)
btn.bind("<Enter>", lambda e, b=btn: b.config(bg='#3498db'))
btn.bind("<Leave>", lambda e, b=btn: b.config(bg='#34495e'))
def create_content_area(self, parent):
"""创建内容区域"""
self.content_frame = tk.Frame(parent, bg='white')
self.content_frame.pack(side='left', fill='both', expand=True)
def show_welcome(self):
"""显示欢迎界面"""
self.clear_content()
# 欢迎标题
welcome_frame = tk.Frame(self.content_frame, bg='white', pady=20)
welcome_frame.pack(fill='x')
tk.Label(welcome_frame, text="财务概览",
font=('Arial', 18, 'bold'), bg='white').pack()
# 统计卡片
stats_frame = tk.Frame(self.content_frame, bg='white')
stats_frame.pack(fill='x', pady=20)
# 计算统计数据
today = datetime.datetime.now()
month_transactions = [t for t in self.transactions
if t['date'].month == today.month and t['date'].year == today.year]
total_income = sum(t['amount'] for t in month_transactions if t['type'] == '收入')
total_expense = sum(t['amount'] for t in month_transactions if t['type'] == '支出')
balance = total_income - total_expense
stats_data = [
("本月收入", f"¥{total_income:,.2f}", "#2ecc71"),
("本月支出", f"¥{total_expense:,.2f}", "#e74c3c"),
("本月结余", f"¥{balance:,.2f}", "#3498db"),
("交易总数", str(len(month_transactions)), "#9b59b6")
]
for i, (title, value, color) in enumerate(stats_data):
card = tk.Frame(stats_frame, bg=color, relief='raised', bd=1)
card.grid(row=0, column=i, padx=10, sticky='nsew')
tk.Label(card, text=title, bg=color, fg='white',
font=('Arial', 10)).pack(pady=(10, 5))
tk.Label(card, text=value, bg=color, fg='white',
font=('Arial', 14, 'bold')).pack(pady=(0, 10))
stats_frame.columnconfigure(i, weight=1)
# 快速操作按钮
quick_actions = tk.Frame(self.content_frame, bg='white', pady=20)
quick_actions.pack(fill='x')
tk.Label(quick_actions, text="快速操作",
font=('Arial', 14, 'bold'), bg='white').pack()
action_buttons = [
("➕ 添加收入", lambda: self.show_add_transaction('收入')),
("➖ 添加支出", lambda: self.show_add_transaction('支出')),
("📊 查看报表", self.show_monthly_report),
("🎯 预算设置", self.show_budget_settings)
]
button_frame = tk.Frame(quick_actions, bg='white')
button_frame.pack(pady=10)
for text, command in action_buttons:
tk.Button(button_frame, text=text, command=command,
bg='#3498db', fg='white', font=('Arial', 10),
padx=15, pady=8).pack(side='left', padx=10)
# 最近交易
self.show_recent_transactions()
def show_recent_transactions(self):
"""显示最近交易"""
recent_frame = tk.Frame(self.content_frame, bg='white')
recent_frame.pack(fill='both', expand=True, pady=20)
tk.Label(recent_frame, text="最近交易",
font=('Arial', 14, 'bold'), bg='white').pack(anchor='w')
# 创建表格
columns = ('日期', '类型', '分类', '金额', '描述')
tree = ttk.Treeview(recent_frame, columns=columns, show='headings', height=8)
# 设置列
for col in columns:
tree.heading(col, text=col)
tree.column(col, width=100)
# 添加数据(最近10条)
recent_transactions = sorted(self.transactions, key=lambda x: x['date'], reverse=True)[:10]
for transaction in recent_transactions:
tree.insert('', 'end', values=(
transaction['date'].strftime('%Y-%m-%d'),
transaction['type'],
transaction['category'],
f"¥{transaction['amount']:,.2f}",
transaction['description']
))
# 滚动条
scrollbar = ttk.Scrollbar(recent_frame, orient='vertical', command=tree.yview)
tree.configure(yscrollcommand=scrollbar.set)
tree.pack(side='left', fill='both', expand=True)
scrollbar.pack(side='right', fill='y')
def show_add_transaction(self, transaction_type=None):
"""显示添加交易界面"""
self.clear_content()
form_frame = tk.Frame(self.content_frame, bg='white', padx=20, pady=20)
form_frame.pack(fill='both', expand=True)
tk.Label(form_frame, text="添加交易记录",
font=('Arial', 16, 'bold'), bg='white').pack(pady=10)
# 交易类型
type_frame = tk.Frame(form_frame, bg='white')
type_frame.pack(fill='x', pady=10)
tk.Label(type_frame, text="交易类型:", bg='white', font=('Arial', 10)).pack(side='left')
self.trans_type = tk.StringVar(value=transaction_type if transaction_type else '收入')
type_menu = ttk.Combobox(type_frame, textvariable=self.trans_type,
values=['收入', '支出'], state='readonly', width=15)
type_menu.pack(side='left', padx=10)
type_menu.bind('<<ComboboxSelected>>', self.update_categories)
# 分类
category_frame = tk.Frame(form_frame, bg='white')
category_frame.pack(fill='x', pady=10)
tk.Label(category_frame, text="分类:", bg='white', font=('Arial', 10)).pack(side='left')
self.category_var = tk.StringVar()
self.category_menu = ttk.Combobox(category_frame, textvariable=self.category_var, width=15)
self.category_menu.pack(side='left', padx=10)
self.update_categories()
# 金额
amount_frame = tk.Frame(form_frame, bg='white')
amount_frame.pack(fill='x', pady=10)
tk.Label(amount_frame, text="金额:", bg='white', font=('Arial', 10)).pack(side='left')
self.amount_entry = tk.Entry(amount_frame, width=20, font=('Arial', 10))
self.amount_entry.pack(side='left', padx=10)
# 日期
date_frame = tk.Frame(form_frame, bg='white')
date_frame.pack(fill='x', pady=10)
tk.Label(date_frame, text="日期:", bg='white', font=('Arial', 10)).pack(side='left')
self.date_entry = DateEntry(date_frame, width=18, background='darkblue',
foreground='white', borderwidth=2, date_pattern='y-mm-dd')
self.date_entry.set_date(datetime.datetime.now())
self.date_entry.pack(side='left', padx=10)
# 描述
desc_frame = tk.Frame(form_frame, bg='white')
desc_frame.pack(fill='x', pady=10)
tk.Label(desc_frame, text="描述:", bg='white', font=('Arial', 10)).pack(side='left')
self.desc_entry = tk.Entry(desc_frame, width=30, font=('Arial', 10))
self.desc_entry.pack(side='left', padx=10)
# 按钮
button_frame = tk.Frame(form_frame, bg='white')
button_frame.pack(pady=20)
tk.Button(button_frame, text="保存记录", command=self.save_transaction,
bg='#2ecc71', fg='white', font=('Arial', 10), padx=15).pack(side='left', padx=10)
tk.Button(button_frame, text="清空", command=self.clear_form,
bg='#e74c3c', fg='white', font=('Arial', 10), padx=15).pack(side='left', padx=10)
def update_categories(self, event=None):
"""更新分类选项"""
trans_type = self.trans_type.get()
if trans_type in self.categories:
self.category_menu['values'] = self.categories[trans_type]
if self.categories[trans_type]:
self.category_var.set(self.categories[trans_type][0])
def save_transaction(self):
"""保存交易记录"""
try:
# 验证数据
amount = float(self.amount_entry.get())
if amount <= 0:
messagebox.showerror("错误", "金额必须大于0")
return
transaction = {
'type': self.trans_type.get(),
'category': self.category_var.get(),
'amount': amount,
'date': datetime.datetime.strptime(self.date_entry.get(), '%Y-%m-%d'),
'description': self.desc_entry.get(),
'user': self.current_user
}
self.transactions.append(transaction)
self.save_data()
messagebox.showinfo("成功", "交易记录已保存!")
self.clear_form()
self.show_welcome()
except ValueError:
messagebox.showerror("错误", "请输入有效的金额")
except Exception as e:
messagebox.showerror("错误", f"保存失败: {str(e)}")
def clear_form(self):
"""清空表单"""
self.amount_entry.delete(0, tk.END)
self.desc_entry.delete(0, tk.END)
self.date_entry.set_date(datetime.datetime.now())
def show_transactions(self):
"""显示所有交易记录"""
self.clear_content()
main_frame = tk.Frame(self.content_frame, bg='white')
main_frame.pack(fill='both', expand=True, padx=10, pady=10)
# 标题和筛选
header_frame = tk.Frame(main_frame, bg='white')
header_frame.pack(fill='x', pady=10)
tk.Label(header_frame, text="交易记录",
font=('Arial', 16, 'bold'), bg='white').pack(side='left')
# 筛选框架
filter_frame = tk.Frame(header_frame, bg='white')
filter_frame.pack(side='right')
tk.Label(filter_frame, text="类型:", bg='white').pack(side='left')
filter_type = ttk.Combobox(filter_frame, values=['全部', '收入', '支出'],
state='readonly', width=8)
filter_type.set('全部')
filter_type.pack(side='left', padx=5)
# 创建表格
columns = ('日期', '类型', '分类', '金额', '描述', '操作')
self.transactions_tree = ttk.Treeview(main_frame, columns=columns, show='headings', height=15)
# 设置列
col_widths = [100, 80, 100, 100, 150, 80]
for i, col in enumerate(columns):
self.transactions_tree.heading(col, text=col)
self.transactions_tree.column(col, width=col_widths[i])
# 滚动条
scrollbar = ttk.Scrollbar(main_frame, orient='vertical', command=self.transactions_tree.yview)
self.transactions_tree.configure(yscrollcommand=scrollbar.set)
self.transactions_tree.pack(side='left', fill='both', expand=True)
scrollbar.pack(side='right', fill='y')
# 刷新数据
self.refresh_transactions_table()
def refresh_transactions_table(self):
"""刷新交易表格"""
# 清空现有数据
for item in self.transactions_tree.get_children():
self.transactions_tree.delete(item)
# 添加数据
for i, transaction in enumerate(self.transactions):
values = (
transaction['date'].strftime('%Y-%m-%d'),
transaction['type'],
transaction['category'],
f"¥{transaction['amount']:,.2f}",
transaction['description'],
"删除"
)
item = self.transactions_tree.insert('', 'end', values=values, iid=str(i))
# 绑定删除事件
self.transactions_tree.bind('<Button-1>', self.on_tree_click)
def on_tree_click(self, event):
"""处理表格点击事件(删除操作)"""
item = self.transactions_tree.identify_row(event.y)
column = self.transactions_tree.identify_column(event.x)
if item and column == '#6': # 操作列
if messagebox.askyesno("确认", "确定要删除这条记录吗?"):
index = int(item)
del self.transactions[index]
self.save_data()
self.refresh_transactions_table()
messagebox.showinfo("成功", "记录已删除")
def show_category_stats(self):
"""显示分类统计"""
self.clear_content()
main_frame = tk.Frame(self.content_frame, bg='white')
main_frame.pack(fill='both', expand=True, padx=10, pady=10)
tk.Label(main_frame, text="分类统计",
font=('Arial', 16, 'bold'), bg='white').pack(pady=10)
# 创建图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 收入分类统计
income_data = {}
expense_data = {}
for transaction in self.transactions:
if transaction['type'] == '收入':
income_data[transaction['category']] = income_data.get(transaction['category'], 0) + transaction['amount']
else:
expense_data[transaction['category']] = expense_data.get(transaction['category'], 0) + transaction['amount']
# 收入饼图
if income_data:
ax1.pie(income_data.values(), labels=income_data.keys(), autopct='%1.1f%%', startangle=90)
ax1.set_title('收入分类统计')
else:
ax1.text(0.5, 0.5, '暂无收入数据', ha='center', va='center', transform=ax1.transAxes)
# 支出饼图
if expense_data:
ax2.pie(expense_data.values(), labels=expense_data.keys(), autopct='%1.1f%%', startangle=90)
ax2.set_title('支出分类统计')
else:
ax2.text(0.5, 0.5, '暂无支出数据', ha='center', va='center', transform=ax2.transAxes)
# 显示图表
canvas = FigureCanvasTkAgg(fig, main_frame)
canvas.draw()
canvas.get_tk_widget().pack(fill='both', expand=True)
def show_monthly_report(self):
"""显示月度报表"""
self.clear_content()
main_frame = tk.Frame(self.content_frame, bg='white')
main_frame.pack(fill='both', expand=True, padx=10, pady=10)
tk.Label(main_frame, text="月度收支趋势",
font=('Arial', 16, 'bold'), bg='white').pack(pady=10)
# 计算月度数据
monthly_data = {}
for transaction in self.transactions:
month_key = transaction['date'].strftime('%Y-%m')
if month_key not in monthly_data:
monthly_data[month_key] = {'income': 0, 'expense': 0}
if transaction['type'] == '收入':
monthly_data[month_key]['income'] += transaction['amount']
else:
monthly_data[month_key]['expense'] += transaction['amount']
# 排序月份
sorted_months = sorted(monthly_data.keys())
incomes = [monthly_data[month]['income'] for month in sorted_months]
expenses = [monthly_data[month]['expense'] for month in sorted_months]
# 创建柱状图
fig, ax = plt.subplots(figsize=(10, 6))
x = range(len(sorted_months))
width = 0.35
ax.bar([i - width/2 for i in x], incomes, width, label='收入', color='#2ecc71')
ax.bar([i + width/2 for i in x], expenses, width, label='支出', color='#e74c3c')
ax.set_xlabel('月份')
ax.set_ylabel('金额 (元)')
ax.set_title('月度收支对比')
ax.set_xticks(x)
ax.set_xticklabels(sorted_months, rotation=45)
ax.legend()
# 显示图表
canvas = FigureCanvasTkAgg(fig, main_frame)
canvas.draw()
canvas.get_tk_widget().pack(fill='both', expand=True)
def show_budget_management(self):
"""显示预算管理"""
self.clear_content()
main_frame = tk.Frame(self.content_frame, bg='white', padx=20, pady=20)
main_frame.pack(fill='both', expand=True)
tk.Label(main_frame, text="预算管理",
font=('Arial', 16, 'bold'), bg='white').pack(pady=10)
# 预算设置表单
form_frame = tk.Frame(main_frame, bg='white')
form_frame.pack(fill='x', pady=20)
tk.Label(form_frame, text="分类:", bg='white').grid(row=0, column=0, padx=5, pady=5, sticky='e')
self.budget_category = ttk.Combobox(form_frame, values=self.categories['支出'], state='readonly')
self.budget_category.grid(row=0, column=1, padx=5, pady=5, sticky='w')
tk.Label(form_frame, text="月度预算:", bg='white').grid(row=1, column=0, padx=5, pady=5, sticky='e')
self.budget_amount = tk.Entry(form_frame, width=15)
self.budget_amount.grid(row=1, column=1, padx=5, pady=5, sticky='w')
tk.Button(form_frame, text="设置预算", command=self.set_budget,
bg='#3498db', fg='white').grid(row=2, column=0, columnspan=2, pady=10)
# 预算显示
budget_frame = tk.Frame(main_frame, bg='white')
budget_frame.pack(fill='both', expand=True)
tk.Label(budget_frame, text="当前预算设置",
font=('Arial', 12, 'bold'), bg='white').pack(anchor='w')
# 创建预算表格
columns = ('分类', '预算金额', '本月支出', '剩余预算', '状态')
budget_tree = ttk.Treeview(budget_frame, columns=columns, show='headings', height=10)
for col in columns:
budget_tree.heading(col, text=col)
budget_tree.column(col, width=120)
# 计算预算使用情况
today = datetime.datetime.now()
month_expenses = {}
for transaction in self.transactions:
if (transaction['type'] == '支出' and
transaction['date'].month == today.month and
transaction['date'].year == today.year):
category = transaction['category']
month_expenses[category] = month_expenses.get(category, 0) + transaction['amount']
# 添加预算数据
for category, budget in self.budgets.items():
expense = month_expenses.get(category, 0)
remaining = budget - expense
status = "正常" if remaining >= 0 else "超支"
budget_tree.insert('', 'end', values=(
category,
f"¥{budget:,.2f}",
f"¥{expense:,.2f}",
f"¥{remaining:,.2f}",
status
))
budget_tree.pack(fill='both', expand=True, pady=10)
def set_budget(self):
"""设置预算"""
try:
category = self.budget_category.get()
amount = float(self.budget_amount.get())
if not category:
messagebox.showerror("错误", "请选择分类")
return
if amount <= 0:
messagebox.showerror("错误", "预算金额必须大于0")
return
self.budgets[category] = amount
self.save_data()
messagebox.showinfo("成功", f"{category}的预算已设置为¥{amount:,.2f}")
self.show_budget_management()
except ValueError:
messagebox.showerror("错误", "请输入有效的金额")
def show_budget_settings(self):
"""显示预算设置(简化版本)"""
self.show_budget_management()
def show_statistics(self):
"""显示数据统计"""
self.show_category_stats()
def export_data(self):
"""导出数据"""
filename = filedialog.asksaveasfilename(
defaultextension=".json",
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
)
if filename:
try:
data = {
'transactions': [
{**t, 'date': t['date'].isoformat()}
for t in self.transactions
],
'budgets': self.budgets
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
messagebox.showinfo("成功", f"数据已导出到 {filename}")
except Exception as e:
messagebox.showerror("错误", f"导出失败: {str(e)}")
def import_data(self):
"""导入数据"""
filename = filedialog.askopenfilename(
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
)
if filename:
try:
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
# 转换日期格式
for transaction in data.get('transactions', []):
transaction['date'] = datetime.datetime.fromisoformat(transaction['date'])
self.transactions.extend(data.get('transactions', []))
self.budgets.update(data.get('budgets', {}))
self.save_data()
messagebox.showinfo("成功", f"数据已从 {filename} 导入")
self.show_welcome()
except Exception as e:
messagebox.showerror("错误", f"导入失败: {str(e)}")
def show_about(self):
"""显示关于信息"""
messagebox.showinfo("关于",
"个人财务管理系统 v2.0\n\n"
"功能特色:\n"
"• 完整的收支记录管理\n"
"• 分类统计和可视化\n"
"• 预算管理和预警\n"
"• 数据导入导出\n"
"• 友好的用户界面\n\n"
"基于Python和Tkinter开发")
def login(self):
"""用户登录"""
username = self.username_entry.get().strip()
password = self.password_entry.get()
if not username or not password:
messagebox.showerror("错误", "请输入用户名和密码")
return
if username in self.users and self.users[username] == password:
self.current_user = username
self.create_main_interface()
else:
messagebox.showerror("错误", "用户名或密码错误")
def register(self):
"""用户注册"""
username = self.username_entry.get().strip()
password = self.password_entry.get()
if not username or not password:
messagebox.showerror("错误", "请输入用户名和密码")
return
if username in self.users:
messagebox.showerror("错误", "用户名已存在")
return
self.users[username] = password
self.current_user = username
self.save_data()
messagebox.showinfo("成功", "注册成功!")
self.create_main_interface()
def logout(self):
"""退出登录"""
self.current_user = None
self.create_login_screen()
def clear_screen(self):
"""清空屏幕"""
for widget in self.root.winfo_children():
widget.destroy()
def clear_content(self):
"""清空内容区域"""
for widget in self.content_frame.winfo_children():
widget.destroy()
def save_data(self):
"""保存数据到文件"""
try:
data = {
'users': self.users,
'transactions': [
{**t, 'date': t['date'].isoformat()}
for t in self.transactions
if t.get('user') == self.current_user
],
'budgets': self.budgets
}
with open('finance_data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存数据失败: {e}")
def load_data(self):
"""从文件加载数据"""
try:
with open('finance_data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
self.users = data.get('users', {})
# 转换日期格式
self.transactions = []
for transaction in data.get('transactions', []):
transaction['date'] = datetime.datetime.fromisoformat(transaction['date'])
self.transactions.append(transaction)
self.budgets = data.get('budgets', {})
except FileNotFoundError:
# 文件不存在,使用默认数据
pass
except Exception as e:
print(f"加载数据失败: {e}")
def main():
root = tk.Tk()
app = PersonalFinanceManager(root)
root.mainloop()
if __name__ == "__main__":
main()
个人财务管理系统
最新推荐文章于 2025-12-13 11:28:09 发布
部署运行你感兴趣的模型镜像
您可能感兴趣的与本文相关的镜像
Python3.11
Conda
Python
Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本
1415

被折叠的 条评论
为什么被折叠?



