Contents

Como criar um rastreador de despesas usando Python

Um controlador de despesas é uma ferramenta essencial que ajuda as pessoas e as empresas a gerir as suas transacções financeiras. Com um registador de despesas, é possível criar orçamentos, categorizar despesas e analisar padrões de gastos.

Explorar métodos de construção de uma aplicação de interface gráfica do utilizador (GUI) multiplataforma para controlar despesas utilizando a linguagem de programação Python.

Os módulos Tkinter, CSV e Matplotlib

Para construir esta aplicação de controlo de despesas, é necessário utilizar as bibliotecas Tkinter, CSV e Matplotlib.

O Tkinter permite aos programadores criar aplicações de ambiente de trabalho cativantes, fornecendo uma série de componentes gráficos versáteis, como botões, etiquetas e campos de introdução de texto. A sua interface de fácil utilização simplifica o processo de criação de aplicações para vários fins.

O módulo CSV é um pacote Python inerente que oferece funcionalidades relativas à interpretação e composição de ficheiros Comma-Separated Value (CSV), que são amplamente utilizados para a troca de dados entre diversas aplicações e sistemas devido à sua simplicidade e compatibilidade com várias plataformas.

O Matplotlib é uma ferramenta poderosa para criar representações visuais dinâmicas, incluindo gráficos, diagramas e quadros, que podem ser melhorados incorporando-o com o OpenCV para ganhar experiência em técnicas avançadas de manipulação de imagens.

Para instalar estes módulos, execute:

pip install tk matplotlib 

Definir a estrutura da aplicação Expense Tracker

A origem do código-fonte deste projeto reside no seu repositório GitHub designado, que serve de depósito digital para todos os ficheiros e documentação associados.

Para começar, importamos módulos imperativos. De seguida, criamos uma classe chamada ExpenseTrackerApp , que define o título e as dimensões da aplicação. Em seguida, definimos um par ordenado de listas; uma contém todas as despesas registadas, enquanto a outra engloba várias categorias monetárias. É também criada uma variável string, denominada categoria_var , cuja configuração inicial corresponde à primeira categoria da nossa lista. Por fim, invocamos o procedimento create_widgets para criar a interface gráfica do utilizador.

 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()

O método create_widgets é um componente essencial na construção de interfaces de utilizador nas aplicações. Para estabelecer uma estrutura de visualização das informações relativas às despesas, é necessário gerar elementos visuais constituídos por molduras que contenham detalhes específicos associados a esses registos.Especificamente, vamos desenvolver seis widgets de etiquetas separados, cada um concebido para apresentar dados relevantes, como títulos, valores monetários, descrições, categorias, datas e totais gerais. É crucial atribuir um elemento principal a cada etiqueta e, ao mesmo tempo, especificar os respectivos textos e características tipográficas.

Para criar uma interface com três entradas de texto e um menu pendente, estabeleça uma ligação entre este último e uma variável chamada category_var . Comece por definir a disposição de cada um destes elementos; especifique os respectivos estilos de letra e dimensões da seguinte forma:1. Crie três widgets Entry e atribua-lhes contentores principais individuais, opções de estilo e restrições de largura.2. Construa um widget Combobox, defina o seu contentor principal, a lista de selecções disponíveis, o estilo do tipo de letra e as especificações dimensionais.3. Associe a Combobox à variável category_var , assegurando assim que a seleção no menu pendente é actualizada em tempo real com base na interação do utilizador.

     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) 

Crie uma interface de utilizador com os seguintes componentes:Um botão com o nome “Adicionar despesa” que adiciona uma despesa a uma lista;Um botão com o nome “Editar despesa” que edita uma despesa existente na lista;Um botão com o nome “Eliminar despesa” que elimina uma despesa da lista;Um botão com o nome “Guardar despesas” que guarda todas as despesas na lista;Um botão com o nome “Mostrar gráfico de despesas” que apresenta um gráfico das despesas na lista;Uma moldura à volta destes botões com o título “Gestor de despesas”.

Incorpore a funcionalidade de criação de uma barra de deslocação vertical posicionada no lado direito da área de conteúdo principal da GUI, permitindo uma navegação sem problemas do conteúdo da caixa de listagem. Utilizando técnicas de preenchimento adequadas, assegure uma apresentação visual óptima. Além disso, ao atualizar a etiqueta total com novas informações, efectue os ajustes necessários para manter a consistência em toda a interface.

         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() 

Definir a funcionalidade do registo de despesas

Incorporar o fragmento de código dado na interface de uma aplicação existente como forma de registar despesas com categorias e datas especificadas, após o que os dados introduzidos são adicionados a uma lista dinâmica apresentada numa interface gráfica do utilizador, eliminando subsequentemente qualquer obrigação de introdução manual de dados por parte do utilizador final.

Caso contrário, é necessário mostrar uma notificação indicando que os valores de “expense” e “date” não podem estar em branco. Em seguida, deve ser executada a chamada de função para update\_total\_label.

     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() 

Para modificar uma despesa existente numa base de dados de registos, é possível implementar um método chamado “edit\_expense”.Este método recupera o índice do registo selecionado e obtém a despesa correspondente. Em seguida, solicita ao utilizador que introduza um novo valor para a despesa através de uma caixa de diálogo. Se o utilizador introduzir uma nova despesa, o método actualiza a lista de despesas com essa informação. Por fim, o método chama a função “refresh\_list” e a sub-rotina “update\_total\_label” para garantir que todas as alterações são reflectidas com precisão na aplicação.

     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() 

Para implementar a funcionalidade descrita no excerto de código dado, vamos definir um método chamado delete_expense que recupera o índice do registo selecionado e obtém a despesa correspondente. De seguida, passamos este índice para a função, para que possa ser eliminado da caixa de listagem. Em seguida, chamamos a função update_total_label para garantir que a etiqueta total é actualizada em conformidade.

     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() 

Claro! Eis um exemplo de como poderia implementar isto em Python utilizando Flask e 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 = Coluna(‘id’, Integer, primary_key=True)name = Coluna(’name’, String)email = Coluna(’email’, String)created_at = Coluna(‘created_at’, DateTime, default=datetime.utcnow)updated_at = Coluna(‘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})"
            ) 

Para refinar o texto fornecido, precisarei de mais contexto sobre o que pretende que eu faça com este fragmento de código. No entanto, aqui está a minha tentativa de tornar o texto fornecido mais sofisticado:pythondef update_total_label(expenses):total = sum(expenses)return totaldef save_expenses(filename=“expenses.csv”, expenses=None):if not expenses:expenses = []# Abre o ficheiro CSV especificado ou cria um se não existirtry:with open(filename, “w”) as f:writer = csv.writer(f)# Escreve o cabeçalho rowwriter.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)) 

Para visualizar a distribuição das despesas mensais pelas várias categorias, podemos implementar um método chamado show_expenses_chart que recebe como entrada a lista expenses . Primeiro, vamos definir um dicionário chamado category_totals , que servirá de contador para acumular o total de despesas de cada categoria de despesas. Vamos iterar sobre as entradas na lista despesas , convertendo o valor de cada despesa de string para float.Para cada categoria, se estiver presente no dicionário category_totals , adicionamos o seu valor-chave ao total existente; caso contrário, criamos um novo par chave-valor que representa o montante da despesa atual.

     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 

Para criar uma representação visual da distribuição categórica das despesas num conjunto de dados, pode utilizar-se a função pie do matplotlib. Esta função permite gerar um gráfico de pizza com base nos montantes de despesas fornecidos e nas categorias correspondentes. Ao definir vários parâmetros, como autopct , equal e o título do gráfico, os utilizadores podem personalizar o aspeto do resultado final.

         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() 

Utilize as capacidades da classe ExpenseTrackerApp instanciando a sua instância, activando subsequentemente o ciclo de eventos Tkinter com o método mainloop(), que mantém uma monitorização contínua das interacções do utilizador até ao momento em que a interface gráfica é fechada.

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

Testar diferentes funcionalidades do rastreador de despesas Python

Após a execução do programa, será iniciada uma interface gráfica do utilizador (GUI), com campos de entrada para registar detalhes pertinentes relativos a uma despesa, como a descrição do item, a categoria e a data, para além de caixas de texto para introduzir valores numéricos. Ao introduzir estas informações e ao clicar no botão “Add Expense” (Adicionar despesa), pode esperar-se que uma nova entrada seja anexada à caixa de listagem. Além disso, o programa actualiza continuamente a soma agregada de todas as despesas registadas para sua conveniência.

/pt/images/adding-entries-to-expense-tracker.jpg

Depois de selecionar uma entrada de despesa específica, clique no botão “Editar despesas” para iniciar o processo de edição. Isto irá abrir uma caixa de diálogo que lhe permite modificar os detalhes do item de despesa individual selecionado no contexto existente.

/pt/images/selecting-and-editing-expense.jpg

Para eliminar um registo de despesas específico, clique no botão “Eliminar despesas”, que remove o item selecionado da lista de despesas.

/pt/images/selecting-and-deleting-expense.jpg

Ao clicar no botão “Show Expenses Chart” (Mostrar gráfico de despesas), o software gera uma representação gráfica sob a forma de um gráfico circular que ilustra a distribuição das despesas pelas várias categorias, acompanhada dos respectivos nomes e percentagens.

/pt/images/pie-chart-of-expenses.jpg

Melhorar o registo de despesas

Para melhorar a experiência do utilizador, é possível incorporar uma função de pesquisa que permite localizar determinadas despesas de acordo com a sua descrição, valor, classificação ou datas.Além disso, pode fornecer opções de ordenação e filtragem de registos de dados. Seria vantajoso adaptar a aplicação para acomodar vários idiomas e estilos de moeda.

Considere a possibilidade de incorporar a funcionalidade de notificação na aplicação, permitindo que os utilizadores estabeleçam limiares que desencadeiem alertas quando ultrapassados. Estes alertas podem servir de aviso contra gastos excessivos e ajudar a identificar despesas irregulares.