Een onkostentracker maken met Python
Een onkostentracker is een essentieel hulpmiddel voor particulieren en bedrijven om hun financiële transacties te beheren. Met een onkostentracker kun je budgetten maken, uitgaven categoriseren en uitgavenpatronen analyseren.
Methodes verkennen voor het bouwen van een cross-platform grafische gebruikersinterface (GUI) toepassing voor het bijhouden van uitgaven met behulp van de programmeertaal Python.
De modules Tkinter, CSV en Matplotlib
Om deze toepassing voor het bijhouden van uitgaven te bouwen, is het nodig om de bibliotheken Tkinter, CSV en Matplotlib te gebruiken.
Met Tkinter kunnen ontwikkelaars boeiende desktoptoepassingen maken door een reeks veelzijdige grafische componenten te bieden, zoals knoppen, labels en tekstinvoervelden. De gebruiksvriendelijke interface vereenvoudigt het proces van het maken van toepassingen voor verschillende doeleinden.
De CSV-module is een Python-pakket dat functies biedt voor de interpretatie en samenstelling van CSV-bestanden (Comma-Separated Value), die veel worden gebruikt voor het uitwisselen van gegevens tussen verschillende toepassingen en systemen vanwege hun eenvoud en compatibiliteit met verschillende platforms.
Matplotlib is een krachtige tool voor het maken van dynamische visuele weergaven, waaronder grafieken, plots en diagrammen, die verder kan worden verbeterd door het te integreren met OpenCV om expertise op te doen in geavanceerde beeldmanipulatietechnieken.
Voer het volgende uit om deze modules te installeren:
pip install tk matplotlib
Definieer de structuur van de app
De broncode van dit project bevindt zich in de daarvoor bestemde GitHub repository, die dient als digitale opslagplaats voor alle bijbehorende bestanden en documentatie.
Om te beginnen importeren we imperatieve modules. Vervolgens maken we een klasse genaamd ExpenseTrackerApp
, waarin de titel en afmetingen van de applicatie worden vastgelegd. Vervolgens definiëren we een geordend paar lijsten; de ene bevat alle geregistreerde uitgaven, terwijl de andere verschillende monetaire categorieën omvat. Een tekenreeksvariabele, genaamd category_var
, wordt ook ingesteld, met een begininstelling die overeenkomt met de belangrijkste categorie op onze lijst. Tot slot roepen we de procedure create_widgets
op om de grafische gebruikersinterface te realiseren.
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import csv
import matplotlib.pyplot as plt
class ExpenseTrackerApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("Expense Tracker")
self.geometry("1300x600")
self.expenses = []
self.categories = [
"Food",
"Transportation",
"Utilities",
"Entertainment",
"Other",
]
self.category_var = tk.StringVar(self)
self.category_var.set(self.categories[0])
self.create_widgets()
De create_widgets
methode dient als een essentieel onderdeel bij het bouwen van gebruikersinterfaces binnen applicaties. Om een kader te creëren voor het weergeven van informatie met betrekking tot uitgaven, moeten we visuele elementen genereren die bestaan uit frames waarin specifieke details met betrekking tot deze records worden weergegeven.We zullen zes aparte labelwidgets ontwikkelen, elk ontworpen om relevante gegevens te presenteren zoals kopjes, geldbedragen, beschrijvingen, categorieën, datums en totalen. Het is cruciaal om aan elk label een bovenliggend element toe te wijzen en tegelijkertijd hun respectievelijke teksten en typografische kenmerken te specificeren.
Om een interface te maken met drie tekstingangen en een vervolgkeuzemenu, maak je een verbinding tussen de laatste en een variabele met de naam category_var
. Begin met het definiëren van de lay-out voor elk van deze elementen; geef hun respectieve lettertypestijlen en afmetingen als volgt op:1. Maak drie widgets Entry en wijs ze individuele bovenliggende containers, stylingopties en breedtebeperkingen toe.2. Construeer een widget Combobox, definieer de bovenliggende container, lijst met beschikbare selecties, lettertypestijl en dimensionale specificaties.3. Koppel de Combobox aan de variabele category_var
en zorg er zo voor dat de selectie in het vervolgkeuzemenu in realtime wordt bijgewerkt op basis van gebruikersinteractie.
def create_widgets(self):
self.label = tk.Label(
self, text="Expense Tracker", font=("Helvetica", 20, "bold")
)
self.label.pack(pady=10)
self.frame_input = tk.Frame(self)
self.frame_input.pack(pady=10)
self.expense_label = tk.Label(
self.frame_input, text="Expense Amount:", font=("Helvetica", 12)
)
self.expense_label.grid(row=0, column=0, padx=5)
self.expense_entry = tk.Entry(
self.frame_input, font=("Helvetica", 12), width=15
)
self.expense_entry.grid(row=0, column=1, padx=5)
self.item_label = tk.Label(
self.frame_input, text="Item Description:", font=("Helvetica", 12)
)
self.item_label.grid(row=0, column=2, padx=5)
self.item_entry = tk.Entry(self.frame_input, font=("Helvetica", 12), width=20)
self.item_entry.grid(row=0, column=3, padx=5)
self.category_label = tk.Label(
self.frame_input, text="Category:", font=("Helvetica", 12)
)
self.category_label.grid(row=0, column=4, padx=5)
self.category_dropdown = ttk.Combobox(
self.frame_input,
textvariable=self.category_var,
values=self.categories,
font=("Helvetica", 12),
width=15,
)
self.category_dropdown.grid(row=0, column=5, padx=5)
self.date_label = tk.Label(
self.frame_input, text="Date (YYYY-MM-DD):", font=("Helvetica", 12)
)
self.date_label.grid(row=0, column=6, padx=5)
self.date_entry = tk.Entry(self.frame_input, font=("Helvetica", 12), width=15)
self.date_entry.grid(row=0, column=7, padx=5)
Maak een gebruikersinterface met de volgende onderdelen:Een knop met de naam “Add Expense” die een uitgave toevoegt aan een lijst;Een knop met de naam “Edit Expense” die een bestaande uitgave in de lijst bewerkt;Een knop met de naam “Delete Expense” die een uitgave uit de lijst verwijdert;Een knop met de naam “Save Expenses” die alle uitgaven in de lijst opslaat;Een knop met de naam “Show Expenses Chart” die een grafiek van de uitgaven in de lijst weergeeft;Een kader rond deze knoppen met de titel “Expense Manager”.
Maak een verticale schuifbalk aan de rechterkant van het hoofdinhoudsgebied van de GUI, zodat je naadloos door de inhoud van de lijst kunt navigeren. Gebruik de juiste opvultechnieken voor een optimale visuele presentatie. Wanneer het totale label wordt bijgewerkt met nieuwe informatie, voer dan de nodige aanpassingen uit om de consistentie in de interface te behouden.
self.add_button = tk.Button(self, text="Add Expense", command=self.add_expense)
self.add_button.pack(pady=5)
self.frame_list = tk.Frame(self)
self.frame_list.pack(pady=10)
self.scrollbar = tk.Scrollbar(self.frame_list)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.expense_listbox = tk.Listbox(
self.frame_list,
font=("Helvetica", 12),
width=70,
yscrollcommand=self.scrollbar.set,
)
self.expense_listbox.pack(pady=5)
self.scrollbar.config(command=self.expense_listbox.yview)
self.edit_button = tk.Button(
self, text="Edit Expense", command=self.edit_expense
)
self.edit_button.pack(pady=5)
self.delete_button = tk.Button(
self, text="Delete Expense", command=self.delete_expense
)
self.delete_button.pack(pady=5)
self.save_button = tk.Button(
self, text="Save Expenses", command=self.save_expenses
)
self.save_button.pack(pady=5)
self.total_label = tk.Label(
self, text="Total Expenses:", font=("Helvetica", 12)
)
self.total_label.pack(pady=5)
self.show_chart_button = tk.Button(
self, text="Show Expenses Chart", command=self.show_expenses_chart
)
self.show_chart_button.pack(pady=5)
self.update_total_label()
De functionaliteit van de Expense Tracker definiëren
Het gegeven codefragment opnemen in de interface van een bestaande toepassing om uitgaven met bepaalde categorieën en datums te registreren, waarna de ingevoerde gegevens worden gevalideerd en toegevoegd aan een dynamische lijst die wordt weergegeven in een grafische gebruikersinterface, zodat de eindgebruiker geen gegevens meer handmatig hoeft in te voeren.
Anders is het nodig om een melding te tonen die aangeeft dat de waarden voor “uitgave” en “datum” niet leeg kunnen zijn. Hierna moet de functieoproep update\total_label worden uitgevoerd.
def add_expense(self):
expense = self.expense_entry.get()
item = self.item_entry.get()
category = self.category_var.get()
date = self.date_entry.get()
if expense and date:
self.expenses.append((expense, item, category, date))
self.expense_listbox.insert(
tk.END, f"{expense} - {item} - {category} ({date})"
)
self.expense_entry.delete(0, tk.END)
self.item_entry.delete(0, tk.END)
self.date_entry.delete(0, tk.END)
else:
messagebox.showwarning("Warning", "Expense and Date cannot be empty.")
self.update_total_label()
Om een bestaande uitgave in een recorddatabase te wijzigen, kan een methode genaamd “edit_expense” worden geïmplementeerd.Deze methode zal de index van de geselecteerde record ophalen en de bijbehorende uitgave verkrijgen. Vervolgens wordt de gebruiker gevraagd om een nieuwe waarde voor de uitgave in te voeren via een dialoogvenster. Als de gebruiker een nieuwe uitgave opgeeft, wordt de uitgavenlijst bijgewerkt met deze informatie. Tot slot roept de methode de functie “refresh\_list” en de subroutine “update\_total\_label” op om ervoor te zorgen dat alle wijzigingen nauwkeurig worden weergegeven in de toepassing.
def edit_expense(self):
selected_index = self.expense_listbox.curselection()
if selected_index:
selected_index = selected_index[0]
selected_expense = self.expenses[selected_index]
new_expense = simpledialog.askstring(
"Edit Expense", "Enter new expense:", initialvalue=selected_expense[0]
)
if new_expense:
self.expenses[selected_index] = (
new_expense,
selected_expense[1],
selected_expense[2],
selected_expense[3],
)
self.refresh_list()
self.update_total_label()
Om de functionaliteit beschreven in het gegeven codefragment te implementeren, definiëren we een methode genaamd delete_expense
die de index van het geselecteerde record ophaalt en de bijbehorende uitgave ophaalt. Vervolgens geven we deze index door aan de functie zodat deze kan worden verwijderd uit de lijstbox. Hierna roepen we de functie update_total_label
aan om ervoor te zorgen dat het totaallabel overeenkomstig wordt bijgewerkt.
def delete_expense(self):
selected_index = self.expense_listbox.curselection()
if selected_index:
selected_index = selected_index[0]
del self.expenses[selected_index]
self.expense_listbox.delete(selected_index)
self.update_total_label()
Zeker! Hier is een voorbeeld van hoe je dit in Python zou kunnen implementeren met Flask en SQLAlchemy:pythonfrom flask import gfrom sqlalchemy import create_engine, Column, Integer, String, ForeignKeyfrom sqlalchemy.orm import sessionmaker, relationshipfrom datetime import datetimeclass User(db.Model): tablename = ‘users’id = Column(‘id’, Integer, primary_key=True)name = Column(’name’, String)email = Column(’email’, String)created_at = Column(‘created_at’, DateTime, default=datetime.utcnow)updated_at = Column(‘updated_at’, DateTime, onupdate=datetime.utcnow)
def refresh_list(self):
self.expense_listbox.delete(0, tk.END)
for expense, item, category, date in self.expenses:
self.expense_listbox.insert(
tk.END, f"{expense} - {item} - {category} ({date})"
)
Om de opgegeven tekst te verfijnen, heb ik meer context nodig over wat je wilt dat ik doe met dit codefragment. Hier is echter mijn poging om de gegeven tekst geavanceerder te laten klinken:pythondef update_total_label(expenses):total = sum(expenses)return totaldef save_expenses(filename=“expenses.csv”, expenses=None):if not expenses:expenses = []# Open het opgegeven CSV-bestand of maak er een als het niet bestaat:with open(filename, “w”) as f:writer = csv.writer(f)# Schrijf de koptekst rijwriter.writerow([…])
def update_total_label(self):
total_expenses = sum(float(expense[0]) for expense in self.expenses)
self.total_label.config(text=f"Total Expenses: USD {total_expenses:.2f}")
def save_expenses(self):
with open("expenses.csv", "w", newline="") as csvfile:
writer = csv.writer(csvfile)
column_headers = ["Expense Amount", "Item Description", "Category", "Date"]
writer.writerow(column_headers)
for expense in self.expenses:
writer.writerow(expense))
Om de verdeling van maandelijkse uitgaven over verschillende categorieën te visualiseren, kunnen we een methode genaamd show_expenses_chart
implementeren die de lijst uitgaven
als invoer neemt. Laten we eerst een woordenboek definiëren met de naam category_totals
, dat zal dienen als teller om de totale uitgaven voor elke uitgavencategorie op te tellen. We itereren over de items in de lijst expenses
en converteren elk uitgavenbedrag van string naar float.Als elke categorie voorkomt in het woordenboek category_totals
, voegen we de sleutelwaarde toe aan het bestaande totaal; anders maken we een nieuw sleutelwaardepaar aan dat het huidige onkostenbedrag weergeeft.
def show_expenses_chart(self):
category_totals = {}
for expense, _, category, _ in self.expenses:
try:
amount = float(expense)
except ValueError:
continue
category_totals[category] = category_totals.get(category, 0) \\+ amount
Om een visuele weergave te maken van de categorische verdeling van uitgaven in een dataset, kan de functie pie
van matplotlib worden gebruikt. Met deze functie kan een taartdiagram worden gegenereerd op basis van de opgegeven uitgaven en de bijbehorende categorieën. Door verschillende parameters in te stellen, zoals autopct
, gelijk
en de titel van de grafiek, kunnen gebruikers aanpassen hoe de uiteindelijke uitvoer eruitziet.
categories = list(category_totals.keys())
expenses = list(category_totals.values())
plt.figure(figsize=(8, 6))
plt.pie(
expenses, labels=categories, autopct="%1.1f%%", startangle=140, shadow=True
)
plt.axis("equal")
plt.title(f"Expense Categories Distribution (USD)")
plt.show()
Gebruik de mogelijkheden van de klasse ExpenseTrackerApp door een instantie ervan te instantiëren en vervolgens de Tkinter-gebeurtenislus te activeren met de methode mainloop(), die een voortdurende bewaking van gebruikersinteracties handhaaft totdat de grafische interface wordt gesloten.
if __name__ == "__main__":
app = ExpenseTrackerApp()
app.mainloop()
Verschillende functies van de Python Expense Tracker testen
Na uitvoering van het programma wordt een grafische gebruikersinterface (GUI) gestart, met invoervelden voor het vastleggen van relevante details over een uitgave, zoals de omschrijving, categorie en datum, naast tekstvakken voor het invoeren van numerieke waarden. Door deze informatie in te voeren en op de knop “Uitgave toevoegen” te klikken, kun je verwachten dat er een nieuw item wordt toegevoegd aan de lijstbox. Bovendien werkt het programma voor je gemak continu de som van alle geregistreerde uitgaven bij.
Nadat je een specifieke uitgave hebt geselecteerd, klik je op de knop “Uitgaven bewerken” om het bewerkingsproces te starten. Er verschijnt dan een dialoogvenster waarin je de details van de geselecteerde uitgavenpost binnen de bestaande context kunt wijzigen.
Een specifieke uitgavenrecord verwijderen door op de knop “Uitgaven verwijderen” te klikken, waardoor de geselecteerde post uit de lijst met uitgaven wordt verwijderd.
Als je op de knop “Uitgavengrafiek weergeven” klikt, genereert de software een grafische weergave in de vorm van een taartdiagram dat de verdeling van uitgaven over verschillende categorieën illustreert, vergezeld van hun respectievelijke namen en percentages.
De Expense Tracker verbeteren
Om de gebruikerservaring te verbeteren, is het mogelijk om een zoekfunctie in te bouwen waarmee personen bepaalde uitgaven kunnen vinden op basis van hun beschrijving, waarde, classificatie of datum.Daarnaast kun je opties bieden voor het sorteren en filteren van gegevensrecords. Het zou nuttig zijn om de applicatie aan te passen aan verschillende talen en valutastijlen.
Overweeg om notificatiefunctionaliteit in de applicatie op te nemen, zodat gebruikers drempels kunnen instellen die waarschuwingen triggeren als ze worden overschreden. Deze waarschuwingen kunnen dienen als waarschuwing tegen te hoge uitgaven en kunnen helpen bij het identificeren van onregelmatige uitgaven.