Naučte sa základy jazyka Python: Postavme si jednoduchý nástroj na sledovanie výdavkov


Ak ste sa niekedy pokúšali zostaviť rozpočet, viete, aké ťažké môže byť získať nástroj na sledovanie výdavkov, ktorý robí to, čo chcete. Ale čo tak si ho postaviť sami? Poďme sa naučiť základy Pythonu vytvorením jednoduchého nástroja na sledovanie výdavkov, ktorý môžete skutočne používať.

Definovanie požiadaviek našej aplikácie na sledovanie výdavkov

Keď som prišiel s týmto nápadom, chcel som vytvoriť aplikáciu, ktorá je viac než len ďalšou aplikáciou príkazového riadku. Vytvorili sme niekoľko z nich (napríklad jednoduchý zoznam úloh pomocou Pythonu). Na tento účel som chcel použiť nejaké GUI, tak som sa rozhodol, že budeme importovať knižnicu Tkinter, aby sme mali nejaké použiteľné prvky používateľského rozhrania.

Knižnice nám umožňujú opätovne použiť kód. Zvyčajne existuje knižnica pre väčšinu vecí, ktoré by ste mohli chcieť robiť v Pythone. Ich importom sa vyhnete prepisovaniu celého kódu, ktorý obsahujú.

V tejto aplikácii implementujeme:

  • Vstup do nákladov
  • Definovanie rozpočtu
  • Porovnanie našich výdavkov s naším rozpočtom
  • Zobraziť históriu výdavkov

Na konci tohto budeme mať slušnú predstavu o tom, ako Python funguje, a budeme môcť dokončiť našu prvú aplikáciu Python založenú na GUI.

Nastavenie nášho projektu

Overením verzie Pythonu by ste mali skontrolovať, či má vaše zariadenie nainštalovaný Python. Už som popísal, ako prepojiť moje obľúbené IDE (Visual Studio) s Pythonom. Keď zvládnete réžiu inštalácie Pythonu na svoje zariadenie a jeho aktualizáciu na aktuálnu verziu, môžeme začať vytvorením nového projektu.

Pre tento projekt som si vybral Python, pretože je to jeden z najjednoduchších jazykov pre začiatočníkov. Poďme sa ponoriť do budovania nášho sledovania výdavkov!

Vytvorenie hlavného okna aplikácie

Keďže sa v tejto aplikácii budeme vzďaľovať od terminálu, namiesto toho budeme musieť nastaviť Tkinter, aby vytvoril grafické používateľské rozhranie (GUI) našej aplikácie. Dizajn používateľského rozhrania v Pythone môže byť zložitý, takže to urobím jednoducho a poviem vám, čo robí kód, ktorý vám dávam. Tu je návod, ako bude definované moje okno používateľského rozhrania. Začneme importovaním Tkinter:

import tkinter as tk
from tkinter import ttk, messagebox, simpledialog

Tkinter obsahuje všetky funkcie, ktoré potrebujeme na vytvorenie základného používateľského rozhrania. Všimnite si, že importujeme aj Ttk, nástroj na vytváranie tém v Tkinter. To nám umožňuje kontrolovať, ako vyzerá naše používateľské rozhranie, ak si to želáme. Tiež použijeme okno správ a jednoduché dialógové okno, ktoré používateľa vyzve na počiatočné nastavenie rozpočtu. Chceme mať tiež možnosť ukladať mesačné údaje pre prípad, že by sme k nim chceli v budúcnosti pristupovať, takže na začiatok súboru pridáme:

import json
from datetime import datetime

Okrem toho, že som veľmi blízko k môjmu vlastnému menu, JSON je vlastne celkom cool ako serializačný systém. Našťastie sa štandardne dodáva s Pythonom, takže ho môžeme importovať priamo. Import dátumu a času nám pomáha udržiavať správne formátovanie dátumov v celej aplikácii.

Teraz, keď sme prišli na import, poďme v skutočnosti vytvoriť okno:

def create_widgets(self):
    # Main Frame
    main_frame = ttk.Frame(self.root, padding="10")
    main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
    # Expense Input Frame
    input_frame = ttk.LabelFrame(main_frame, text="Add Expense", padding="10")
    input_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
    ttk.Label(input_frame, text="Date (YYYY-MM-DD):").grid(row=0, column=0, sticky=tk.W)
    self.date_entry = ttk.Entry(input_frame)
    self.date_entry.grid(row=0, column=1, padx=5, pady=2)
    ttk.Label(input_frame, text="Category:").grid(row=1, column=0, sticky=tk.W)
    self.category_combobox = ttk.Combobox(input_frame, values=self.categories)
    self.category_combobox.grid(row=1, column=1, padx=5, pady=2)
    ttk.Label(input_frame, text="Amount:").grid(row=2, column=0, sticky=tk.W)
    self.amount_entry = ttk.Entry(input_frame)
    self.amount_entry.grid(row=2, column=1, padx=5, pady=2)
    ttk.Button(input_frame, text="Add Expense", command=self.add_expense).grid(row=3, column=0, columnspan=2, pady=5)
    # Category Management Frame
    category_frame = ttk.LabelFrame(main_frame, text="Manage Categories", padding="10")
    category_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
    self.new_category_entry = ttk.Entry(category_frame)
    self.new_category_entry.grid(row=0, column=0, padx=5, pady=2)
    ttk.Button(category_frame, text="Add Category", command=self.add_category).grid(row=0, column=1, padx=5, pady=2)

Tieto položky len upravujú štýl nášho okna. Ak chcete, môžete sa pohrať s číslami a uvidíte, čo dostanete, keďže hranie sa s nimi je neškodné (okrem toho, že posledné okno vyzerá hrozne). Musíme tiež zobraziť rám a tlačidlo uloženia a nakonfigurovať mriežku tak, aby vyzerala pekne:

 # Display Frame
 display_frame = ttk.LabelFrame(main_frame, text="Expenses and Budget", padding="10")
 display_frame.grid(row=0, column=1, rowspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
 self.expense_tree = ttk.Treeview(display_frame, columns=('Date', 'Category', 'Amount'), show='headings')
 self.expense_tree.heading('Date', text='Date')
 self.expense_tree.heading('Category', text='Category')
 self.expense_tree.heading('Amount', text='Amount')
 self.expense_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
 scrollbar = ttk.Scrollbar(display_frame, orient=tk.VERTICAL, command=self.expense_tree.yview)
 scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
 self.expense_tree.configure(yscrollcommand=scrollbar.set)
 self.budget_label = ttk.Label(display_frame, text=f"Monthly Budget: ${self.monthly_budget:.2f}")
 self.budget_label.grid(row=1, column=0, sticky=tk.W, pady=2)
 self.total_expenses_label = ttk.Label(display_frame, text="Total Expenses: $0.00")
 self.total_expenses_label.grid(row=2, column=0, sticky=tk.W, pady=2)
 self.remaining_budget_label = ttk.Label(display_frame, text=f"Remaining Budget: ${self.monthly_budget:.2f}")
 self.remaining_budget_label.grid(row=3, column=0, sticky=tk.W, pady=2)
 # Save Button
 ttk.Button(display_frame, text="Save Data", command=self.save_data).grid(row=4, column=0, pady=10)
 # Configure grid
 self.root.columnconfigure(0, weight=1)
 self.root.rowconfigure(0, weight=1)
 main_frame.columnconfigure(1, weight=1)
 main_frame.rowconfigure(0, weight=1)
 display_frame.columnconfigure(0, weight=1)
 display_frame.rowconfigure(0, weight=1)

Ako vidíte, používateľské rozhranie si vyžaduje veľa kódovania, aby bolo správne. Keďže sa však plánujete naučiť základy jazyka Python, pochopenie dizajnu používateľského rozhrania je súčasťou celku. Teraz, keď máme používateľské rozhranie spustené, poďme pracovať na funkčnosti.

Definovanie kategórií a pridávanie výdavkov

Aby som našej aplikácii dodal trochu „tela“, preddefinoval som niekoľko kategórií výdavkov. Zahrnul som ich ako hlavičky spolu so zvyškom našich definícií:

class ExpenseTrackerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Expense Tracker & Budget Planner")
        self.root.geometry("1100x500")
        self.expenses = []
        self.categories = ["Rent", "Food", "Entertainment", "Car", "Credit Cards"]
        
        self.monthly_budget = self.get_initial_budget()
        
        self.create_widgets()
    def get_initial_budget(self):
        while True:
            budget = simpledialog.askfloat("Monthly Budget", "Enter your monthly budget:", minvalue=0.01)
            if budget is not None:
                return budget
            else:
                if messagebox.askyesno("No Budget", "You haven't entered a budget. Do you want to exit?"):
                    self.root.quit()
                    return 0

Definície ako tieto by mali byť na prvom mieste nášho programu. Na tento účel by mal byť tento úryvok pred vytvorením kódu widgetu, ktorý sme už vyvinuli.

Funkcia get_initial_budget(self) vytvorí na začiatku spustenia aplikácie kontextové okno na zhromažďovanie údajov určujúcich množstvo peňazí, ktoré máte k dispozícii. Ak nezadáte hodnotu, aplikácia sa okamžite vypne.

Potrebujeme tiež funkčnosť na pridávanie výdavkov a aktualizáciu kategórií o nové, vlastné. Našťastie je to tiež jednoduché na implementáciu:

def add_expense(self):
    try:
        date = self.date_entry.get()
        category = self.category_combobox.get()
        amount = float(self.amount_entry.get())
        self.expenses.append({
            'date': date,
            'category': category,
            'amount': amount
        })
        self.update_expense_list()
        self.update_budget_info()
        self.clear_input_fields()
    except ValueError:
        messagebox.showerror("Error", "Invalid input. Please check your entries.")
def add_category(self):
    new_category = self.new_category_entry.get().strip()
    if new_category and new_category not in self.categories:
        self.categories.append(new_category)
        self.category_combobox['values'] = self.categories
        self.new_category_entry.delete(0, tk.END)
        messagebox.showinfo("Category Added", f"'{new_category}' has been added to the categories.")
    else:
        messagebox.showerror("Error", "Invalid category name or category already exists.")

Na doplnenie našich základných funkcií budeme potrebovať funkcie na aktualizáciu zoznamu výdavkov, keď zadáme nový výdavok. Po zadaní údajov budeme potrebovať pomocnú funkciu na vymazanie rozbaľovacieho poľa. A nakoniec si budeme musieť vypočítať, koľko z nášho rozpočtu máme ešte k dispozícii. Tento jednoduchý útržok kódu nám umožní urobiť práve toto:

def update_expense_list(self):
    for item in self.expense_tree.get_children():
        self.expense_tree.delete(item)
    for expense in self.expenses:
        self.expense_tree.insert('', 'end', values=(
            expense['date'],
            expense['category'],
            f"${expense['amount']:.2f}"
        ))
def update_budget_info(self):
    total_expenses = sum(expense['amount'] for expense in self.expenses)
    remaining_budget = self.monthly_budget - total_expenses
    self.total_expenses_label.config(text=f"Total Expenses: ${total_expenses:.2f}")
    self.remaining_budget_label.config(text=f"Remaining Budget: ${remaining_budget:.2f}")
def clear_input_fields(self):
    self.date_entry.delete(0, tk.END)
    self.category_combobox.set('')
    self.amount_entry.delete(0, tk.END)

Ako teda po zadaní všetkých týchto údajov zabezpečíme, aby sme ich nestratili? Implementoval som jednoduchú funkciu ukladania pomocou formátu JSON, ktorý sme predtým importovali:

def save_data(self):
    data = {
        'budget': self.monthly_budget,
        'categories': self.categories,
        'expenses': self.expenses
    }
    
    # Get the current date to use in the filename
    current_date = datetime.now().strftime("%Y-%m")
    default_filename = f"expense_data_{current_date}.json"
    
    file_path = filedialog.asksaveasfilename(
        defaultextension=".json",
        filetypes=[("JSON files", "*.json")],
        initialfile=default_filename
    )
    
    if file_path:
        with open(file_path, 'w') as f:
            json.dump(data, f, indent=2)
        messagebox.showinfo("Save Successful", f"Data saved to {file_path}")

Táto funkcia serializuje údaje a ukladá ich do konkrétneho umiestnenia súboru s aktuálnym dátumom pripojeným k názvu súboru. Posledná vec, ktorú musíme urobiť, je zostaviť funkčnú slučku main():

if __name__ == "__main__":
    root = tk.Tk()
    app = ExpenseTrackerApp(root)
    root.mainloop()

To by malo umožniť, aby náš nástroj na sledovanie výdavkov fungoval bez problémov. Gratulujeme k dokončeniu vašej prvej aplikácie založenej na GUI v Pythone!

Doplnky, nedostatky a prispôsobenie

Po dokončení tohto budete trochu rozumieť tomu, ako Tkinter funguje a ako ho môžete použiť na vytváranie GUI. Ak viete trochu CSS alebo nejaký grafický dizajn, mali by ste byť schopní rýchlo zistiť, ako používať Tkinter. Ak by ste chceli niekoľko výziev, môžete vyskúšať tieto veci:

  • Vytvorte alebo použite nový motív pre svoju aplikáciu.
  • Vytvorte pre ňu funkciu „načítať“ a tlačidlo. Špeciálne som vynechal funkciu načítania, aby som vám dal viac priestoru na preskúmanie.
  • Preskúmajte, ako by ste mohli graficky rozčleniť svoj rozpočet vo forme koláčového grafu.

Konečná aplikácia by mala aktuálne vyzerať asi takto:

Kam ísť odtiaľto?

Tento vývojový tutoriál vám poskytol iba široký prehľad o Tkinter, ale ak plánujete robiť viac aplikácií založených na GUI, budete musieť lepšie pochopiť, čo dokáže. RealPython vám môže poskytnúť predstavu o tom, ako používať Tkinter alebo ktorýkoľvek z iných dostupných rámcov GUI alebo súprav nástrojov. Python je na základnej úrovni celkom ľahko pochopiteľný, ale je toho veľa, čo môžete preskúmať, keď sa naučíte základy jazyka Python. Jediná vec, ktorá obmedzuje to, čo môžete vytvoriť, je vaša predstavivosť.

Ak sa zaseknete na kóde a chcete, aby sa môj úplný skript Python porovnal s tým vaším, je k dispozícii na mojom GitHub.