|
1 |
| -import csv |
2 |
| -import os |
| 1 | +import datetime |
| 2 | +import sqlite3 |
| 3 | +from tkcalendar import DateEntry |
| 4 | +from tkinter import * |
| 5 | +import tkinter.messagebox as tb |
| 6 | +import tkinter.ttk as ttk |
3 | 7 |
|
4 |
| -CSV_FILE = "expenses.csv" |
| 8 | +# Functions |
| 9 | +def list_all_expenses(): |
| 10 | + global connector, table |
5 | 11 |
|
6 |
| -def initialize_csv(): |
7 |
| - if not os.path.exists(CSV_FILE): |
8 |
| - with open(CSV_FILE, "w", newline="") as file: |
9 |
| - writer = csv.writer(file) |
10 |
| - writer.writerow(["Date", "Description", "Amount"]) |
| 12 | + table.delete(*table.get_children()) |
11 | 13 |
|
12 |
| -def add_expense(date, description, amount): |
13 |
| - with open(CSV_FILE, "a", newline="") as file: |
14 |
| - writer = csv.writer(file) |
15 |
| - writer.writerow([date, description, amount]) |
| 14 | + all_data = connector.execute('SELECT * FROM ExpenseTracker') |
| 15 | + data = all_data.fetchall() |
16 | 16 |
|
17 |
| -def view_expenses(): |
18 |
| - with open(CSV_FILE, "r") as file: |
19 |
| - reader = csv.reader(file) |
20 |
| - for row in reader: |
21 |
| - print(", ".join(row)) |
| 17 | + for values in data: |
| 18 | + table.insert('', END, values=values) |
22 | 19 |
|
23 |
| -if __name__ == "__main__": |
24 |
| - initialize_csv() |
| 20 | +def clear_fields(): |
| 21 | + global Desc, payee, amnt, MoP, date, table |
25 | 22 |
|
26 |
| - while True: |
27 |
| - print("\nExpense Tracker Menu:") |
28 |
| - print("1. Add Expense") |
29 |
| - print("2. View Expenses") |
30 |
| - print("3. Exit") |
| 23 | + today_date = datetime.datetime.now().date() |
31 | 24 |
|
32 |
| - choice = input("Enter your choice: ") |
| 25 | + Desc.set('') ; payee.set('') ; amnt.set(0.0) ; MoP.set('Cash'), date.set_date(today_date) |
| 26 | + table.selection_remove(*table.selection()) |
33 | 27 |
|
34 |
| - if choice == "1": |
35 |
| - date = input("Enter the date (YYYY-MM-DD): ") |
36 |
| - description = input("Enter the description: ") |
37 |
| - amount = input("Enter the amount: ") |
| 28 | +def remove_expense(): |
| 29 | + if not table.selection(): |
| 30 | + tb.showerror('No record selected!', 'Please select a record to delete!') |
| 31 | + return |
38 | 32 |
|
39 |
| - add_expense(date, description, amount) |
40 |
| - print("Expense added successfully!") |
| 33 | + current_selected_expense = table.item(table.focus()) |
| 34 | + values_selected = current_selected_expense['values'] |
41 | 35 |
|
42 |
| - elif choice == "2": |
43 |
| - print("Expenses:") |
44 |
| - view_expenses() |
| 36 | + surety = tb.askyesno('Are you sure?', f'Are you sure that you want to delete the record of {values_selected[2]}') |
45 | 37 |
|
46 |
| - elif choice == "3": |
47 |
| - break |
| 38 | + if surety: |
| 39 | + connector.execute('DELETE FROM ExpenseTracker WHERE ID=%d' % values_selected[0]) |
| 40 | + connector.commit() |
48 | 41 |
|
49 |
| - else: |
50 |
| - print("Invalid choice. Please try again.") |
| 42 | + list_all_expenses() |
| 43 | + tb.showinfo('Record deleted successfully!', 'The record you wanted to delete has been deleted successfully') |
| 44 | + |
| 45 | +def remove_all_expenses(): |
| 46 | + surety = tb.askyesno('Are you sure?', 'Are you sure that you want to delete all the expense items from the database?', icon='warning') |
| 47 | + |
| 48 | + if surety: |
| 49 | + table.delete(*table.get_children()) |
| 50 | + |
| 51 | + connector.execute('DELETE FROM ExpenseTracker') |
| 52 | + connector.commit() |
| 53 | + |
| 54 | + clear_fields() |
| 55 | + list_all_expenses() |
| 56 | + tb.showinfo('All Expenses deleted', 'All the expenses were successfully deleted') |
| 57 | + else: |
| 58 | + tb.showinfo('Ok then', 'The task was aborted and no expense was deleted!') |
| 59 | + |
| 60 | +def add_another_expense(): |
| 61 | + global date, payee, Desc, amnt, MoP |
| 62 | + global connector |
| 63 | + |
| 64 | + if not date.get() or not payee.get() or not Desc.get() or not amnt.get() or not MoP.get(): |
| 65 | + tb.showerror('Fields empty!', "Please fill all the missing fields before pressing the add button!") |
| 66 | + else: |
| 67 | + connector.execute( |
| 68 | + 'INSERT INTO ExpenseTracker (Date, Payee, Description, Amount, ModeOfPayment) VALUES (?, ?, ?, ?, ?)', |
| 69 | + (date.get_date(), payee.get(), Desc.get(), amnt.get(), MoP.get()) |
| 70 | + ) |
| 71 | + connector.commit() |
| 72 | + |
| 73 | + clear_fields() |
| 74 | + list_all_expenses() |
| 75 | + tb.showinfo('Expense added', 'The expense whose details you just entered has been added to the database') |
| 76 | + |
| 77 | +def expense_to_words_before_adding(): |
| 78 | + global date, Desc, amnt, payee, MoP |
| 79 | + |
| 80 | + if not date or not Desc or not amnt or not payee or not MoP: |
| 81 | + tb.showerror('Incomplete data', 'The data is incomplete, meaning fill all the fields first!') |
| 82 | + |
| 83 | + message = f'Your expense can be read like: \n"You paid {amnt.get()} to {payee.get()} for {Desc.get()} on {date.get_date()} via {MoP.get()}"' |
| 84 | + |
| 85 | + add_question = tb.askyesno('Read your record like: ', f'{message}\n\nShould I add it to the database?') |
| 86 | + |
| 87 | + if add_question: |
| 88 | + add_another_expense() |
| 89 | + else: |
| 90 | + tb.showinfo('Ok', 'Please take your time to add this record') |
| 91 | + |
| 92 | +# Connecting to the Database |
| 93 | +connector = sqlite3.connect("Expense Tracker.db") |
| 94 | +cursor = connector.cursor() |
| 95 | + |
| 96 | +connector.execute( |
| 97 | + 'CREATE TABLE IF NOT EXISTS ExpenseTracker (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, Date DATETIME, Payee TEXT, Description TEXT, Amount FLOAT, ModeOfPayment TEXT)' |
| 98 | +) |
| 99 | +connector.commit() |
| 100 | + |
| 101 | +# Backgrounds and Fonts |
| 102 | +dataentery_frame_bg = 'light blue' |
| 103 | +buttons_frame_bg = 'tomato' |
| 104 | +hlb_btn_bg = 'Indianred' |
| 105 | + |
| 106 | +lbl_font = ('Georgia', 13) |
| 107 | +entry_font = 'Times 13 bold' |
| 108 | +btn_font = ('Gill Sans MT', 13) |
| 109 | + |
| 110 | +# Initializing the GUI window |
| 111 | +root = Tk() |
| 112 | +root.title('DebEx') |
| 113 | +root.geometry('1200x550') |
| 114 | +root.resizable(0, 0) |
| 115 | + |
| 116 | +Label(root, text='DebEx', font=('white', 21, 'bold'), bg=hlb_btn_bg).pack(side=TOP, fill=X) |
| 117 | + |
| 118 | +# StringVar and DoubleVar variables |
| 119 | +Desc = StringVar() |
| 120 | +amnt = DoubleVar() |
| 121 | +payee = StringVar() |
| 122 | +MoP = StringVar(value='Cash') |
| 123 | + |
| 124 | +# Frames |
| 125 | +data_entry_frame = Frame(root, bg=dataentery_frame_bg) |
| 126 | +data_entry_frame.place(x=0, y=35, relheight=0.95, relwidth=0.25) |
| 127 | + |
| 128 | +buttons_frame = Frame(root, bg=buttons_frame_bg) |
| 129 | +buttons_frame.place(relx=0.25, rely=0.063, relwidth=0.75, relheight=0.12) |
| 130 | + |
| 131 | +tree_frame = Frame(root) |
| 132 | +tree_frame.place(relx=0.25, rely=0.18, relwidth=0.75, relheight=0.8) |
| 133 | + |
| 134 | +# Data Entry Frame |
| 135 | +Label(data_entry_frame, text='Date (M/DD/YY) :', font=lbl_font, bg=dataentery_frame_bg).place(x=10, y=50) |
| 136 | +date = DateEntry(data_entry_frame, date=datetime.datetime.now().date(), font=entry_font) |
| 137 | +date.place(x=160, y=50) |
| 138 | + |
| 139 | +Label(data_entry_frame, text='Payee\t :', font=lbl_font, bg=dataentery_frame_bg).place(x=10, y=230) |
| 140 | +Entry(data_entry_frame, font=entry_font, width=31, text=payee).place(x=10, y=260) |
| 141 | + |
| 142 | +Label(data_entry_frame, text='Description :', font=lbl_font, bg=dataentery_frame_bg).place(x=10, y=100) |
| 143 | +Entry(data_entry_frame, font=entry_font, width=31, text=Desc).place(x=10, y=130) |
| 144 | + |
| 145 | +Label(data_entry_frame, text='Amount\t :', font=lbl_font, bg=dataentery_frame_bg).place(x=10, y=180) |
| 146 | +Entry(data_entry_frame, font=entry_font, width=14, text=amnt).place(x=160, y=180) |
| 147 | + |
| 148 | +Label(data_entry_frame, text='Mode of Payment:', font=lbl_font, bg=dataentery_frame_bg).place(x=10, y=310) |
| 149 | +dd1 = OptionMenu(data_entry_frame, MoP, *['Cash', 'Cheque', 'Credit Card', 'Debit Card', 'Paytm', 'Google Pay', 'Razorpay']) |
| 150 | +dd1.place(x=160, y=305) ; dd1.configure(width=10, font=entry_font) |
| 151 | + |
| 152 | +Button(data_entry_frame, text='Add expense', command=add_another_expense, font=btn_font, width=30, |
| 153 | + bg=hlb_btn_bg).place(x=10, y=395) |
| 154 | + |
| 155 | +# Buttons' Frame |
| 156 | +Button(buttons_frame, text='Delete Expense', font=btn_font, width=25, bg=hlb_btn_bg, command=remove_expense).place(x=30, y=5) |
| 157 | + |
| 158 | +Button(buttons_frame, text='Clear Fields in DataEntry Frame', font=btn_font, width=25, bg=hlb_btn_bg, |
| 159 | + command=clear_fields).place(x=335, y=5) |
| 160 | + |
| 161 | +Button(buttons_frame, text='Delete All Expenses', font=btn_font, width=25, bg=hlb_btn_bg, command=remove_all_expenses).place(x=640, y=5) |
| 162 | + |
| 163 | +# Treeview Frame |
| 164 | +table = ttk.Treeview(tree_frame, selectmode=BROWSE, columns=('ID', 'Date', 'Payee', 'Description', 'Amount', 'Mode of Payment')) |
| 165 | + |
| 166 | +X_Scroller = Scrollbar(table, orient=HORIZONTAL, command=table.xview) |
| 167 | +Y_Scroller = Scrollbar(table, orient=VERTICAL, command=table.yview) |
| 168 | +X_Scroller.pack(side=BOTTOM, fill=X) |
| 169 | +Y_Scroller.pack(side=RIGHT, fill=Y) |
| 170 | + |
| 171 | +table.config(yscrollcommand=Y_Scroller.set, xscrollcommand=X_Scroller.set) |
| 172 | + |
| 173 | +table.heading('ID', text='S No.', anchor=CENTER) |
| 174 | +table.heading('Date', text='Date', anchor=CENTER) |
| 175 | +table.heading('Payee', text='Payee', anchor=CENTER) |
| 176 | +table.heading('Description', text='Description', anchor=CENTER) |
| 177 | +table.heading('Amount', text='Amount', anchor=CENTER) |
| 178 | +table.heading('Mode of Payment', text='Mode of Payment', anchor=CENTER) |
| 179 | + |
| 180 | +table.column('#0', width=0, stretch=NO) |
| 181 | +table.column('#1', width=50, stretch=NO) |
| 182 | +table.column('#2', width=95, stretch=NO) # Date column |
| 183 | +table.column('#3', width=150, stretch=NO) # Payee column |
| 184 | +table.column('#4', width=325, stretch=NO) # Title column |
| 185 | +table.column('#5', width=135, stretch=NO) # Amount column |
| 186 | +table.column('#6', width=125, stretch=NO) # Mode of Payment column |
| 187 | + |
| 188 | +table.place(relx=0, y=0, relheight=1, relwidth=1) |
| 189 | + |
| 190 | +list_all_expenses() |
| 191 | +# Finalizing the GUI window |
| 192 | +root.update() |
| 193 | +root.mainloop() |
0 commit comments