Гид по технологиям

Как создать трекер расходов с кроссплатформенным GUI на Python

6 min read Python GUI Обновлено 12 Apr 2026
Как создать трекер расходов на Python
Как создать трекер расходов на Python

Банка с монетами и три стопки монет, из каждой растёт саженец

Трекер расходов помогает записывать транзакции, составлять бюджеты, категоризировать траты и анализировать паттерны расходов. В этом руководстве вы узнаете, как шаг за шагом построить простое кроссплатформенное приложение на Python с графическим интерфейсом, экспортом в CSV и круговой диаграммой расходов.

Кому это полезно

  • Новичкам в разработке настольных приложений на Python.
  • Тем, кто хочет быстрый рабочий прототип учёта расходов.
  • Разработчикам, которые планируют расширять функционал (база данных, синхронизация, аутентификация).

Что понадобится

Ключевые модули:

  • Tkinter — стандартный GUI-инструментарий Python для настольных приложений.
  • CSV — стандартная библиотека для чтения/записи CSV-файлов.
  • Matplotlib — библиотека для визуализации (построение круговой диаграммы).

(Если потребуется хранение транзакций в базе — рассмотрите SQLite или PostgreSQL как альтернативу CSV.)

Установка (если Matplotlib не установлен):

pip install tk matplotlib 

Примечание: Tk обычно поставляется с большинством дистрибутивов Python; на некоторых системах (особенно Linux) может потребоваться отдельный пакет OS (например, python3-tk).

Структура приложения (кратко)

Архитектура приложения простая и содержит:

  • Класс приложения ExpenseTrackerApp (наследник tk.Tk).
  • Внутренние структуры: список expenses (список кортежей) и список категорий.
  • Виджеты ввода: поля суммы, описания, комбобокс категории, поле даты.
  • Список (Listbox) для отображения записей и набор кнопок: добавить, редактировать, удалить, сохранить, показать диаграмму.
  • Экспорт в CSV и построение круговой диаграммы по категориям с помощью Matplotlib.

Ниже — исходные фрагменты кода из примера (сохранены без изменений для точности).

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

Виджеты и компоновка

Функция create_widgets добавляет на окно метки, поля ввода, комбобокс, кнопки и listbox. В коде задаются шрифты, ширины и базовая компоновка через grid/pack. Автор использует StringVar для привязки выбранной категории.

    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)

Далее — кнопки, listbox и метод обновления общего итога:

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

Основной функционал (логика)

Программа использует ряд методов, которые реализуют добавление, редактирование, удаление, обновление списка, сохранение в CSV и построение диаграммы.

Добавление расхода (add_expense): проверяет поля, добавляет запись в self.expenses и в Listbox, очищает поля:

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

Редактирование (edit_expense) — открывает диалог и заменяет сумму выбранной записи:

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

Удаление (delete_expense): удаляет выбранную запись:

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

Обновление списка (refresh_list) и подсчёт общего итога (update_total_label):

    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})"  
            )

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

Визуализация по категориям (show_expenses_chart): собирает суммы по категориям и строит круговую диаграмму:

    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

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

Запуск приложения:

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

Скриншоты интерфейса

Добавление записей в трекер расходов

Выбор и редактирование записи расходов

Выбор и удаление записи расходов

Круговая диаграмма расходов по категориям

Улучшения и проверочные практики (рекомендации)

Ниже собраны практические рекомендации по повышению надёжности, удобства и безопасности приложения.

Валидация и формат данных

  • Проверяйте, что сумма — валидное положительное число. Используйте try/except при преобразовании в float.
  • Для даты применяйте модуль datetime и строгий формат (например, YYYY-MM-DD). Пример: datetime.datetime.strptime(date_str, “%Y-%m-%d”).
  • Проверяйте длину и содержимое полей описания (защита от некорректного ввода).

Хранение данных: CSV vs БД

  • CSV удобен для простых прототипов и совместимости с Excel.
  • Для многопользовательских или транзакционных сценариев используйте SQLite (local) или PostgreSQL (серверно) — это даст целостность данных, запросы и фильтрацию по времени.

Локализация и валюты

  • UI-строки в коде можно локализовать, например, через gettext или собственную таблицу переводов.
  • Для формата суммы используйте локализованные форматеры (locale.currency) или babel.numbers для контроля группировки разрядов и символов валют.
  • Если приложение работает в разных регионах, храните суммы и валюту как отдельные поля (amount, currency).

Безопасность и приватность

  • CSV-файл не шифруется — если данные приватны, храните их в зашифрованном контейнере или используйте защищённую БД.
  • Не храните чувствительные данные (например, номера банковских карт) в открытом виде.
  • Для синхронизации с облаком применяйте авторизацию и TLS.
  • Примечание по GDPR/локальным законам: если приложение обрабатывает персональные данные пользователей, необходимо обеспечить возможность удаления и экспорта персональных данных по запросу.

UX и дополнительные функции

  • Добавьте поиск и фильтрацию (по описанию, сумме, категории, дате).
  • Возможность сортировки по дате/сумме/категории.
  • Поддержка регулярных транзакций и напоминаний (уведомления).
  • Импорт CSV (чтобы подтягивать данные из банков или других приложений).

Альтернативные подходы

  1. GUI-фреймворки: PyQt / PySide / Kivy — если нужен современный интерфейс и более сложные виджеты.
  2. Web-приложение: Flask / FastAPI + React/Vue — если нужна мультиплатформенная веб-версия и удалённый доступ.
  3. Хранилище: SQLite для локальных пользователей; ORM (SQLAlchemy) упрощает миграции.

Контроль качества: тесты и критерии приёмки

Критерии приёмки:

  • Приложение запускается без ошибок в целевой среде.
  • Можно добавить запись с суммой и датой; запись появляется в списке.
  • Редактирование изменяет данные и обновляет итог.
  • Удаление убирает запись из списка и из внутренних данных.
  • Экспорт в CSV создаёт файл expenses.csv с корректными заголовками и значениями.
  • Построение диаграммы не падает при нечисловых суммах (такие записи игнорируются).

Тестовые сценарии (ручные / автоматизированные):

  • Добавление нескольких записей с разными категориями и проверка суммы.
  • Попытка добавить запись с пустыми полями — ожидается предупреждение.
  • Редактирование записи с вводом некорректной суммы — ожидается откат или валидация.
  • Сохранение/загрузка CSV: проверить соответствие строк и заголовков.
  • Построение диаграммы при отсутствии записей — должно корректно показывать сообщение об отсутствии данных или пустую диаграмму.

Чек-листы по ролям

Разработчик:

  • Добавить валидацию суммы и даты.
  • Сделать сохранение атомарным (временный файл + переименование).
  • Добавить логирование ошибок.

QA:

  • Покрыть тестами основные сценарии: add/edit/delete/save/chart.
  • Тестировать на разных ОС: Windows, macOS, Linux.

Пользователь:

  • Перевести интерфейс на предпочитаемый язык.
  • Проверить экспорт/импорт CSV в своей локали (разделитель, формат даты).

Миграция и совместимость

  • Файловая структура: expenses.csv — простая таблица с четырьмя колонками (сумма, описание, категория, дата). При переходе на базу данных создайте соответствующие колонки и мигрируйте данные одним запросом.
  • Версии Python: код совместим с Python 3.6+ (f-строки, модуль datetime и т.д.). Тестируйте на целевых версиях.

Примеры улучшений: краткий чек-лист внедрения

  • Валидация полей (числа и даты).
  • Локализация UI-строк.
  • Импорт/экспорт CSV с выбором разделителя.
  • Переход на SQLite при большом объёме данных.
  • Шифрование файлов/базы при необходимости приватности.

Частые ошибки и как их избежать

  • Ошибка: ValueError при суммировании — решение: фильтровать записи с нечисловыми суммами и логировать некорректные строки.
  • Ошибка: повреждённый CSV — решение: писать через временный файл и атомарное переименование.
  • Проблема UX: длинные описания обрезаются в Listbox — решение: добавлять превью и отдельное окно с полными деталями.

Шаблон CSV (заголовки)

Первая строка CSV должна быть:

Expense Amount,Item Description,Category,Date

Каждая последующая — одна запись с соответствующими значениями.

Превью для социальных сетей

  • OG Title: Как создать трекер расходов на Python
  • OG Description: Пошаговое руководство по созданию настольного трекера расходов на Python с Tkinter, экспортом в CSV и визуализацией через Matplotlib.

Итог

Этот проект — отличный старт для практики GUI-разработки на Python и быстрой итерации продукта. Для простых личных нужд CSV + Tkinter достаточно; для надёжных бизнес-решений стоит перейти на базу данных, добавить аутентификацию и защищённое хранение данных.

Ключевые направления дальнейшей работы: валидация и локализация, импорт банковских выписок, уведомления о превышении бюджета и синхронизация с облаком.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как скрыть папку или файл в Windows и Mac
Инструкции

Как скрыть папку или файл в Windows и Mac

Как отменить подписку YouTube Premium
How-to

Как отменить подписку YouTube Premium

Google Photos: руководство по использованию
Фотография

Google Photos: руководство по использованию

Видеоняня через Skype — дешёвый и простой способ
Родительство

Видеоняня через Skype — дешёвый и простой способ

Как пользоваться встроенным словарём iPhone
iPhone

Как пользоваться встроенным словарём iPhone

Как проверить безопасность сайта — чеклист
Безопасность

Как проверить безопасность сайта — чеклист