个人财务管理系统

部署运行你感兴趣的模型镜像
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()

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值