admin管理员组文章数量:1438907
Python实用开发项目案例03
设置星标 了解更多资讯
在Python学习之旅中,实践项目是巩固知识与提升技能的最佳途径。
今天我想与大家分享另一个实用的Python项目 —— 个人财务记账本应用。这个项目结合了数据库操作、数据分析和图形界面编程。
项目概述
- 支持添加、删除和查看个人收支记录
- 提供按年月筛选功能,灵活查看特定时期的财务状况
- 内置数据统计分析,自动计算总收入、总支出和结余
- 可视化展示各类别收支情况,直观了解资金流向
- 支持生成Excel财务报表,便于长期财务规划
技术要点解析
SQLite、Tkinter与数据分析库
SQLite是一款轻量级数据库,非常适合个人应用使用。它无需独立服务器进程,可直接将整个数据库存储在单个文件中,便于部署和携带。
Tkinter作为Python标准GUI库,提供了创建桌面应用界面的简便方法,无需额外安装即可使用。
Pandas和Matplotlib则分别提供了强大的数据处理和可视化能力,是数据分析的理想工具组合。
面向对象编程设计
该项目采用面向对象方式构建,通过创建FinanceTracker
类封装所有功能:
- 数据库管理:初始化和维护SQLite数据库,处理数据存储和查询
- 界面构建:创建直观的用户交互界面,包括输入表单、记录表格和统计图表
- 数据处理:实现记录筛选、统计计算和报表生成等核心功能
功能模块详解
- 数据库初始化:创建交易记录表,定义字段类型和关系
- 用户界面设计:构建分区域布局,包括记录输入区、数据显示区和统计图表区
- 交易记录管理:实现添加和删除记录的功能方法
- 数据筛选与统计:按时间段筛选数据并计算关键财务指标
- 图表可视化:根据筛选结果生成分类统计图表
- 报表导出:将数据导出为Excel格式的详细报表
代码剖析
数据库初始化
数据库初始化方法创建了存储交易记录的表结构:
代码语言:javascript代码运行次数:0运行复制def init_db(self):
self.conn = sqlite3.connect('finance.db')
self.cursor = self.conn.cursor()
# 创建表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
type TEXT NOT NULL,
category TEXT NOT NULL,
amount REAL NOT NULL,
description TEXT
)
''')
self.connmit()
界面构建
界面构建方法采用左右分区设计,左侧为输入区,右侧为数据显示和统计区:
代码语言:javascript代码运行次数:0运行复制def create_widgets(self):
# 主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧输入面板
input_frame = ttk.LabelFrame(main_frame, text="新增记录")
input_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
# 记录类型
ttk.Label(input_frame, text="类型:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
self.type_var = tk.StringVar(value="支出")
ttk.OptionMenu(input_frame, self.type_var, "支出", "收入", "支出").grid(row=0, column=1, padx=5, pady=5,
sticky="w")
# ...其他输入控件...
# 右侧显示面板
display_frame = ttk.Frame(main_frame)
display_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 交易记录表格
self.tree = ttk.Treeview(display_frame, columns=("ID", "Date", "Type", "Category", "Amount", "Description"),
show="headings")
# ...表格设置...
# 统计和图表区域
stats_frame = ttk.LabelFrame(display_frame, text="月度统计")
# ...统计和图表组件...
数据处理与统计
数据筛选和统计方法实现了按月份年份查询数据并更新统计信息:
代码语言:javascript代码运行次数:0运行复制def filter_data(self):
month = self.month_var.get().replace("月", "")
year = self.year_var.get()
try:
month = int(month)
year = int(year)
start_date = f"{year}-{month:02d}-01"
if month == 12:
end_date = f"{year + 1}-01-01"
else:
end_date = f"{year}-{month + 1:02d}-01"
self.cursor.execute(
"SELECT * FROM transactions WHERE date >= ? AND date < ? ORDER BY date DESC",
(start_date, end_date)
)
# 清空表格并加载筛选数据
# ...代码省略...
# 更新统计和图表
self.update_stats()
except Exception as e:
messagebox.showerror("错误", f"筛选失败: {str(e)}")
def update_stats(self):
# 获取当前显示的数据
data = []
for item in self.tree.get_children():
data.append(self.tree.item(item)['values'])
ifnot data:
self.stats_text.delete(1.0, tk.END)
self.stats_text.insert(tk.END, "没有数据")
return
df = pd.DataFrame(data, columns=["ID", "Date", "Type", "Category", "Amount", "Description"])
# 计算统计信息和更新图表
# ...代码省略...
报表生成
报表生成功能将数据导出为Excel格式,包含详细记录和月度汇总两个工作表:
代码语言:javascript代码运行次数:0运行复制def generate_report(self):
try:
# 获取所有数据
self.cursor.execute("SELECT * FROM transactions ORDER BY date")
data = self.cursor.fetchall()
ifnot data:
messagebox.showinfo("提示", "没有数据可生成报表")
return
df = pd.DataFrame(data, columns=["ID", "Date", "Type", "Category", "Amount", "Description"])
# 生成月度报表
df['Date'] = pd.to_datetime(df['Date'])
df['Month'] = df['Date'].dt.to_period('M')
monthly = df.groupby(['Month', 'Type'])['Amount'].sum().unstack()
# 保存报表
report_filename = f"finance_report_{datetime.now().strftime('%Y%m%d')}.xlsx"
with pd.ExcelWriter(report_filename) as writer:
df.to_excel(writer, sheet_name="详细记录", index=False)
monthly.to_excel(writer, sheet_name="月度汇总")
messagebox.showinfo("成功", f"报表已生成: {report_filename}")
except Exception as e:
messagebox.showerror("错误", f"生成报表失败: {str(e)}")
应用界面展示
完整代码
代码语言:javascript代码运行次数:0运行复制# -*- coding: utf-8 -*-
import sqlite3
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
class FinanceTracker:
def __init__(self, root):
self.root = root
self.root.title("个人记账本")
self.root.geometry("900x600")
# 初始化数据库
self.init_db()
# 创建UI
self.create_widgets()
# 加载数据
self.load_data()
def init_db(self):
self.conn = sqlite3.connect('finance.db')
self.cursor = self.conn.cursor()
# 创建表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
type TEXT NOT NULL,
category TEXT NOT NULL,
amount REAL NOT NULL,
description TEXT
)
''')
self.connmit()
def create_widgets(self):
# 主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧输入面板
input_frame = ttk.LabelFrame(main_frame, text="新增记录")
input_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
# 记录类型
ttk.Label(input_frame, text="类型:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
self.type_var = tk.StringVar(value="支出")
ttk.OptionMenu(input_frame, self.type_var, "支出", "收入", "支出").grid(row=0, column=1, padx=5, pady=5, sticky="w")
# 日期
ttk.Label(input_frame, text="日期:").grid(row=1, column=0, padx=5, pady=5, sticky="e")
self.date_entry = ttk.Entry(input_frame)
self.date_entry.grid(row=1, column=1, padx=5, pady=5)
self.date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))
# 类别
ttk.Label(input_frame, text="类别:").grid(row=2, column=0, padx=5, pady=5, sticky="e")
self.category_entry = ttk.Entry(input_frame)
self.category_entry.grid(row=2, column=1, padx=5, pady=5)
# 金额
ttk.Label(input_frame, text="金额:").grid(row=3, column=0, padx=5, pady=5, sticky="e")
self.amount_entry = ttk.Entry(input_frame)
self.amount_entry.grid(row=3, column=1, padx=5, pady=5)
# 描述
ttk.Label(input_frame, text="描述:").grid(row=4, column=0, padx=5, pady=5, sticky="e")
self.desc_entry = ttk.Entry(input_frame)
self.desc_entry.grid(row=4, column=1, padx=5, pady=5)
# 按钮
ttk.Button(input_frame, text="添加记录", command=self.add_transaction).grid(row=5, column=0, columnspan=2, pady=10)
ttk.Button(input_frame, text="删除记录", command=self.delete_transaction).grid(row=6, column=0, columnspan=2, pady=5)
# 右侧显示面板
display_frame = ttk.Frame(main_frame)
display_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 交易记录表格
self.tree = ttk.Treeview(display_frame, columns=("ID", "Date", "Type", "Category", "Amount", "Description"), show="headings")
self.tree.heading("ID", text="ID")
self.tree.heading("Date", text="日期")
self.tree.heading("Type", text="类型")
self.tree.heading("Category", text="类别")
self.tree.heading("Amount", text="金额")
self.tree.heading("Description", text="描述")
self.tree.column("ID", width=40)
self.tree.column("Date", width=80)
self.tree.column("Type", width=60)
self.tree.column("Category", width=80)
self.tree.column("Amount", width=80)
self.tree.pack(fill=tk.BOTH, expand=True)
# 统计和图表
stats_frame = ttk.LabelFrame(display_frame, text="月度统计")
stats_frame.pack(fill=tk.X, pady=5)
self.stats_text = tk.Text(stats_frame, height=5)
self.stats_text.pack(fill=tk.X)
self.figure = plt.Figure(figsize=(5, 3), dpi=100)
self.canvas = FigureCanvasTkAgg(self.figure, master=display_frame)
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
# 筛选控制
filter_frame = ttk.Frame(display_frame)
filter_frame.pack(fill=tk.X, pady=5)
ttk.Label(filter_frame, text="月份:").pack(side=tk.LEFT, padx=5)
self.month_var = tk.StringVar()
months = [f"{i}月"for i in range(1, 13)]
self.month_menu = ttk.OptionMenu(filter_frame, self.month_var, *months)
self.month_menu.pack(side=tk.LEFT, padx=5)
ttk.Label(filter_frame, text="年份:").pack(side=tk.LEFT, padx=5)
self.year_var = tk.StringVar(value=str(datetime.now().year))
self.year_entry = ttk.Entry(filter_frame, width=6, textvariable=self.year_var)
self.year_entry.pack(side=tk.LEFT, padx=5)
ttk.Button(filter_frame, text="筛选", command=self.filter_data).pack(side=tk.LEFT, padx=5)
ttk.Button(filter_frame, text="生成报表", command=self.generate_report).pack(side=tk.LEFT, padx=5)
def add_transaction(self):
try:
date = self.date_entry.get()
trans_type = self.type_var.get()
category = self.category_entry.get()
amount = float(self.amount_entry.get())
description = self.desc_entry.get()
self.cursor.execute(
"INSERT INTO transactions (date, type, category, amount, description) VALUES (?, ?, ?, ?, ?)",
(date, trans_type, category, amount, description)
)
self.connmit()
self.load_data()
self.clear_inputs()
except Exception as e:
messagebox.showerror("错误", f"添加记录失败: {str(e)}")
def delete_transaction(self):
selected = self.tree.selection()
if not selected:
messagebox.showwarning("警告", "请先选择要删除的记录")
return
trans_id = self.tree.item(selected[0])['values'][0]
try:
self.cursor.execute("DELETE FROM transactions WHERE id=?", (trans_id,))
self.connmit()
self.load_data()
except Exception as e:
messagebox.showerror("错误", f"删除记录失败: {str(e)}")
def load_data(self):
# 清空表格
for row in self.tree.get_children():
self.tree.delete(row)
# 加载数据
self.cursor.execute("SELECT * FROM transactions ORDER BY date DESC")
for row in self.cursor.fetchall():
self.tree.insert("", tk.END, values=row)
# 更新统计和图表
self.update_stats()
def filter_data(self):
month = self.month_var.get().replace("月", "")
year = self.year_var.get()
try:
month = int(month)
year = int(year)
start_date = f"{year}-{month:02d}-01"
if month == 12:
end_date = f"{year+1}-01-01"
else:
end_date = f"{year}-{month+1:02d}-01"
self.cursor.execute(
"SELECT * FROM transactions WHERE date >= ? AND date < ? ORDER BY date DESC",
(start_date, end_date)
)
# 清空表格
for row in self.tree.get_children():
self.tree.delete(row)
# 加载筛选数据
for row in self.cursor.fetchall():
self.tree.insert("", tk.END, values=row)
# 更新统计和图表
self.update_stats()
except Exception as e:
messagebox.showerror("错误", f"筛选失败: {str(e)}")
def update_stats(self):
# 获取当前显示的数据
data = []
for item in self.tree.get_children():
data.append(self.tree.item(item)['values'])
if not data:
self.stats_text.delete(1.0, tk.END)
self.stats_text.insert(tk.END, "没有数据")
return
df = pd.DataFrame(data, columns=["ID", "Date", "Type", "Category", "Amount", "Description"])
# 计算统计信息
income = df[df['Type'] == '收入']['Amount'].sum()
expense = df[df['Type'] == '支出']['Amount'].sum()
balance = income - expense
# 显示统计信息
self.stats_text.delete(1.0, tk.END)
self.stats_text.insert(tk.END, f"总收入: {income:.2f}\n")
self.stats_text.insert(tk.END, f"总支出: {expense:.2f}\n")
self.stats_text.insert(tk.END, f"结余: {balance:.2f}\n")
# 更新图表
self.figure.clear()
ax = self.figure.add_subplot(111)
# 按类别分组
if not df.empty:
grouped = df.groupby(['Type', 'Category'])['Amount'].sum().unstack()
grouped.plot(kind='bar', stacked=True, ax=ax)
ax.set_title("收支分类统计")
ax.set_ylabel("金额")
ax.legend(title="类别")
self.canvas.draw()
def generate_report(self):
try:
# 获取所有数据
self.cursor.execute("SELECT * FROM transactions ORDER BY date")
data = self.cursor.fetchall()
if not data:
messagebox.showinfo("提示", "没有数据可生成报表")
return
df = pd.DataFrame(data, columns=["ID", "Date", "Type", "Category", "Amount", "Description"])
# 生成月度报表
df['Date'] = pd.to_datetime(df['Date'])
df['Month'] = df['Date'].dt.to_period('M')
monthly = df.groupby(['Month', 'Type'])['Amount'].sum().unstack()
# 保存报表
report_filename = f"finance_report_{datetime.now().strftime('%Y%m%d')}.xlsx"
with pd.ExcelWriter(report_filename) as writer:
df.to_excel(writer, sheet_name="详细记录", index=False)
monthly.to_excel(writer, sheet_name="月度汇总")
messagebox.showinfo("成功", f"报表已生成: {report_filename}")
except Exception as e:
messagebox.showerror("错误", f"生成报表失败: {str(e)}")
def clear_inputs(self):
self.date_entry.delete(0, tk.END)
self.date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))
self.category_entry.delete(0, tk.END)
self.amount_entry.delete(0, tk.END)
self.desc_entry.delete(0, tk.END)
def __del__(self):
self.conn.close()
if __name__ == "__main__":
root = tk.Tk()
app = FinanceTracker(root)
root.mainloop()
总结
个人财务记账本项目虽然看似简单,却涵盖了SQLite数据库操作、Tkinter界面设计、Pandas数据分析和Matplotlib可视化等多种Python技术。通过开发这个应用,不仅能解决个人财务管理的实际问题,还能全面提升Python开发技能。这个项目展示了如何将多个技术模块无缝整合,构建一个实用的桌面应用程序。
在下一篇文章中,我们将继续探索更多Python实用小项目,不断丰富我们的编程技能库。敬请期待!
如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-17,如有侵权请联系 cloudcommunity@tencent 删除数据统计pythonself开发本文标签: Python实用开发项目案例03
版权声明:本文标题:Python实用开发项目案例03 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1747604267a2727611.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论