|
| 1 | +import tkinter as tk |
| 2 | +from tkinter import ttk, messagebox, simpledialog |
| 3 | +import csv |
| 4 | +import matplotlib.pyplot as plt |
| 5 | + |
| 6 | +class ExpenseTrackerApp(tk.Tk): |
| 7 | + def __init__(self): |
| 8 | + super().__init__() |
| 9 | + self.title("Expense Tracker") |
| 10 | + self.geometry("1300x600") |
| 11 | + self.expenses = [] |
| 12 | + self.categories = [ |
| 13 | + "Food", |
| 14 | + "Transportation", |
| 15 | + "Utilities", |
| 16 | + "Entertainment", |
| 17 | + "Other", |
| 18 | + ] |
| 19 | + self.category_var = tk.StringVar(self) |
| 20 | + self.category_var.set(self.categories[0]) |
| 21 | + self.currencies = ["USD", "EUR", "GBP", "JPY", "INR"] |
| 22 | + self.create_widgets() |
| 23 | + |
| 24 | + def create_widgets(self): |
| 25 | + self.label = tk.Label( |
| 26 | + self, text="Expense Tracker", font=("Helvetica", 20, "bold") |
| 27 | + ) |
| 28 | + self.label.pack(pady=10) |
| 29 | + self.frame_input = tk.Frame(self) |
| 30 | + self.frame_input.pack(pady=10) |
| 31 | + self.expense_label = tk.Label( |
| 32 | + self.frame_input, text="Expense Amount:", font=("Helvetica", 12) |
| 33 | + ) |
| 34 | + self.expense_label.grid(row=0, column=0, padx=5) |
| 35 | + self.expense_entry = tk.Entry( |
| 36 | + self.frame_input, font=("Helvetica", 12), width=15 |
| 37 | + ) |
| 38 | + self.expense_entry.grid(row=0, column=1, padx=5) |
| 39 | + self.item_label = tk.Label( |
| 40 | + self.frame_input, text="Item Description:", font=("Helvetica", 12) |
| 41 | + ) |
| 42 | + self.item_label.grid(row=0, column=2, padx=5) |
| 43 | + self.item_entry = tk.Entry(self.frame_input, font=("Helvetica", 12), width=20) |
| 44 | + self.item_entry.grid(row=0, column=3, padx=5) |
| 45 | + self.category_label = tk.Label( |
| 46 | + self.frame_input, text="Category:", font=("Helvetica", 12) |
| 47 | + ) |
| 48 | + self.category_label.grid(row=0, column=4, padx=5) |
| 49 | + self.category_dropdown = ttk.Combobox( |
| 50 | + self.frame_input, |
| 51 | + textvariable=self.category_var, |
| 52 | + values=self.categories, |
| 53 | + font=("Helvetica", 12), |
| 54 | + width=15, |
| 55 | + ) |
| 56 | + self.category_dropdown.grid(row=0, column=5, padx=5) |
| 57 | + self.date_label = tk.Label( |
| 58 | + self.frame_input, text="Date (YYYY-MM-DD):", font=("Helvetica", 12) |
| 59 | + ) |
| 60 | + self.date_label.grid(row=0, column=6, padx=5) |
| 61 | + self.date_entry = tk.Entry(self.frame_input, font=("Helvetica", 12), width=15) |
| 62 | + self.date_entry.grid(row=0, column=7, padx=5) |
| 63 | + self.add_button = tk.Button(self, text="Add Expense", command=self.add_expense) |
| 64 | + self.add_button.pack(pady=5) |
| 65 | + self.frame_list = tk.Frame(self) |
| 66 | + self.frame_list.pack(pady=10) |
| 67 | + self.scrollbar = tk.Scrollbar(self.frame_list) |
| 68 | + self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) |
| 69 | + self.expense_listbox = tk.Listbox( |
| 70 | + self.frame_list, |
| 71 | + font=("Helvetica", 12), |
| 72 | + width=70, |
| 73 | + yscrollcommand=self.scrollbar.set, |
| 74 | + ) |
| 75 | + self.expense_listbox.pack(pady=5) |
| 76 | + self.scrollbar.config(command=self.expense_listbox.yview) |
| 77 | + self.edit_button = tk.Button( |
| 78 | + self, text="Edit Expense", command=self.edit_expense |
| 79 | + ) |
| 80 | + self.edit_button.pack(pady=5) |
| 81 | + self.delete_button = tk.Button( |
| 82 | + self, text="Delete Expense", command=self.delete_expense |
| 83 | + ) |
| 84 | + self.delete_button.pack(pady=5) |
| 85 | + self.save_button = tk.Button( |
| 86 | + self, text="Save Expenses", command=self.save_expenses |
| 87 | + ) |
| 88 | + self.save_button.pack(pady=5) |
| 89 | + self.total_label = tk.Label( |
| 90 | + self, text="Total Expenses:", font=("Helvetica", 12) |
| 91 | + ) |
| 92 | + self.total_label.pack(pady=5) |
| 93 | + self.show_chart_button = tk.Button( |
| 94 | + self, text="Show Expenses Chart", command=self.show_expenses_chart |
| 95 | + ) |
| 96 | + self.show_chart_button.pack(pady=5) |
| 97 | + self.update_total_label() |
| 98 | + |
| 99 | + def add_expense(self): |
| 100 | + expense = self.expense_entry.get() |
| 101 | + item = self.item_entry.get() |
| 102 | + category = self.category_var.get() |
| 103 | + date = self.date_entry.get() |
| 104 | + if expense and date: |
| 105 | + self.expenses.append((expense, item, category, date)) |
| 106 | + self.expense_listbox.insert( |
| 107 | + tk.END, f"{expense} - {item} - {category} ({date})" |
| 108 | + ) |
| 109 | + self.expense_entry.delete(0, tk.END) |
| 110 | + self.item_entry.delete(0, tk.END) |
| 111 | + self.date_entry.delete(0, tk.END) |
| 112 | + else: |
| 113 | + messagebox.showwarning("Warning", "Expense and Date cannot be empty.") |
| 114 | + self.update_total_label() |
| 115 | + |
| 116 | + def edit_expense(self): |
| 117 | + selected_index = self.expense_listbox.curselection() |
| 118 | + if selected_index: |
| 119 | + selected_index = selected_index[0] |
| 120 | + selected_expense = self.expenses[selected_index] |
| 121 | + new_expense = simpledialog.askstring( |
| 122 | + "Edit Expense", "Enter new expense:", initialvalue=selected_expense[0] |
| 123 | + ) |
| 124 | + if new_expense: |
| 125 | + self.expenses[selected_index] = ( |
| 126 | + new_expense, |
| 127 | + selected_expense[1], |
| 128 | + selected_expense[2], |
| 129 | + selected_expense[3], |
| 130 | + ) |
| 131 | + self.refresh_list() |
| 132 | + self.update_total_label() |
| 133 | + |
| 134 | + def delete_expense(self): |
| 135 | + selected_index = self.expense_listbox.curselection() |
| 136 | + if selected_index: |
| 137 | + selected_index = selected_index[0] |
| 138 | + del self.expenses[selected_index] |
| 139 | + self.expense_listbox.delete(selected_index) |
| 140 | + self.update_total_label() |
| 141 | + |
| 142 | + def refresh_list(self): |
| 143 | + self.expense_listbox.delete(0, tk.END) |
| 144 | + for expense, item, category, date in self.expenses: |
| 145 | + self.expense_listbox.insert( |
| 146 | + tk.END, f"{expense} - {item} - {category} ({date})" |
| 147 | + ) |
| 148 | + |
| 149 | + def update_total_label(self): |
| 150 | + total_expenses = sum(float(expense[0]) for expense in self.expenses) |
| 151 | + self.total_label.config(text=f"Total Expenses: USD {total_expenses:.2f}") |
| 152 | + |
| 153 | + def save_expenses(self): |
| 154 | + with open("expenses.csv", "w", newline="") as csvfile: |
| 155 | + writer = csv.writer(csvfile) |
| 156 | + for expense, item, category, date in self.expenses: |
| 157 | + writer.writerow([expense, item, category, date]) |
| 158 | + |
| 159 | + def show_expenses_chart(self): |
| 160 | + category_totals = {} |
| 161 | + for expense, _, category, _ in self.expenses: |
| 162 | + try: |
| 163 | + amount = float(expense) |
| 164 | + except ValueError: |
| 165 | + continue |
| 166 | + category_totals[category] = category_totals.get(category, 0) + amount |
| 167 | + categories = list(category_totals.keys()) |
| 168 | + expenses = list(category_totals.values()) |
| 169 | + plt.figure(figsize=(8, 6)) |
| 170 | + plt.pie( |
| 171 | + expenses, labels=categories, autopct="%1.1f%%", startangle=140, shadow=True |
| 172 | + ) |
| 173 | + plt.axis("equal") |
| 174 | + plt.title(f"Expense Categories Distribution (USD)") |
| 175 | + plt.show() |
| 176 | + |
| 177 | +if __name__ == "__main__": |
| 178 | + app = ExpenseTrackerApp() |
| 179 | + app.mainloop() |
0 commit comments