2024秋软件工程现场编程作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/SE2024 |
---|---|
作业要求 | https://edu.cnblogs.com/campus/fzu/SE2024/homework/13302 |
作业的目标 | 团队合作开发一款个人记账本软件 |
团队名称 | 福气满满 |
团队成员学号-名字 | 052203132童潇剑,102201226陈潇健,102201235曾炜坤,102201234洪庆杨,102201224 陈博涵,182200311洪闽南,102202156高涛,042201520舒锦城,102201335董雯莉 |
一、组员职责分工
姓名 | 负责模块 |
---|---|
童潇剑 | 账本切换模块,用户名,密码本地保存模块 |
陈潇健 | 个人信息模块 |
曾炜坤 | 主页和记录模块 |
洪庆杨 | 删除记录模块 |
陈博涵 | 分类选择模块 |
洪闽南 | 日期检测模块 |
高涛 | 收支趋势分析功能 |
舒锦城 | 登录模块、支出收入可视化 |
董雯莉 | 登录注册模块 |
二、程序运行环境
操作系统:Windows11
编程语言:Python
编程工具:VScode,PyCharm
三、软件运行截图和视频
登录界面
主界面
统计信息界面
收支趋势图界面
删除记录界面
个人信息界面
演示视频链接:https://www.bilibili.com/video/BV1qkS6YgEo1/
四、关键代码
def show_login_window(self):
# 设置字体
title_font = font.Font(family='Arial', size=16, weight='bold')
label_font = font.Font(family='Arial', size=12)
"""显示登录窗口."""
login_window = tk.Toplevel(self.root)
login_window.title("登录")
login_window.geometry("300x300")
login_window.configure(bg='#FFF8F0')
tk.Label(login_window, text="用户名:", font=label_font, bg='#FFF8F0').pack(pady=10)
username_entry = tk.Entry(login_window, font=label_font)
username_entry.pack(pady=10)
tk.Label(login_window, text="密码:", font=label_font, bg='#FFF8F0').pack(pady=10)
password_entry = tk.Entry(login_window, show="*", font=label_font)
password_entry.pack(pady=10)
def login():
username = username_entry.get()
password = password_entry.get()
if username in self.userf and self.userf[username] == password:
messagebox.showinfo("登录成功", "欢迎回来!")
login_window.destroy()
self.root.deiconify() # 显示主窗口
else:
messagebox.showerror("登录失败", "用户名或密码错误。")
login_button = tk.Button(login_window, text="登录", command=login, bg="#4CAF50", fg="white", font=label_font, width=20)
login_button.pack(pady=10)
# 添加注册按钮
register_button = tk.Button(login_window, text="注册", command=self.show_register_window, bg="#4CAF50",
fg="white", font=label_font, width=20)
register_button.pack(pady=10)
def show_register_window(self):
# 设置字体
title_font = font.Font(family='Arial', size=16, weight='bold')
label_font = font.Font(family='Arial', size=12)
"""显示注册窗口."""
register_window = tk.Toplevel(self.root)
register_window.title("注册")
register_window.geometry("300x250")
register_window.configure(bg='#FFF8F0')
tk.Label(register_window, text="用户名:", font=label_font, bg='#FFF8F0').pack(pady=10)
username_entry = tk.Entry(register_window, font=label_font)
username_entry.pack(pady=10)
tk.Label(register_window, text="密码:", font=label_font, bg='#FFF8F0').pack(pady=10)
password_entry = tk.Entry(register_window, show="*", font=label_font)
password_entry.pack(pady=10)
def register():
username = username_entry.get()
password = password_entry.get()
if username in self.userf:
messagebox.showerror("注册失败", "用户名已存在。")
else:
users = {}
users[username] = password
self.userf.update(users)
messagebox.showinfo("注册成功", "注册成功!")
with open('data/un_pw.json', 'w') as f:
json.dump(self.userf, f)
register_window.destroy() # 关闭注册窗口
register_button = tk.Button(register_window, text="注册", command=register, bg="#4CAF50", fg="white",
font=label_font, width=20)
register_button.pack(pady=10)
def submit_record(self):
date = self.date_entry.get()
amount = float(self.amount_entry.get())
category = self.category_var.get() # 获取选择的类别
note = self.note_entry.get()
def is_valid_date(date_str):
try:
datetime.strptime(date_str, '%Y-%m-%d')
return True
except ValueError:
return False
if not is_valid_date(date):
messagebox.showinfo('请正确输入日期!')
return
add_record(date, amount, category, note)
self.date_entry.delete(0, tk.END)
self.amount_entry.delete(0, tk.END)
self.category_var.set("选择类别") # 重置类别选择
self.note_entry.delete(0, tk.END)
messagebox.showinfo("成功", "记录已添加!")
def create_view_window(self):
view_window = tk.Toplevel()
view_window.title("最近记账记录")
view_window.geometry("500x300")
view_window.configure(bg='#FFF8F0')
records = get_recent_records()
if not records:
tk.Label(view_window, text="没有记录可显示。", bg='#FFF8F0', font=('Arial', 12)).pack(pady=10)
else:
tk.Label(view_window, text="最近的记录:", font=('Arial', 14), bg='#FFF8F0').pack(pady=10)
for record in records:
record_text = f"日期: {record['date']}, 金额: {record['amount']}, 类别: {record['category']}, 备注: {record['note']}"
tk.Label(view_window, text=record_text, bg='#FFF8F0').pack()
def create_delete_window(self):
delete_window = tk.Toplevel()
delete_window.title("删除记账记录")
delete_window.geometry("500x300")
delete_window.configure(bg='#FFF8F0')
tk.Label(delete_window, text="选择要删除的记录:", bg='#FFF8F0', font=('Arial', 12)).pack(pady=10)
self.record_listbox = tk.Listbox(delete_window, width=60, height=10)
records = load_records()
for record in records:
record_text = f"日期: {record['date']}, 金额: {record['amount']}, 类别: {record['category']}, 备注: {record['note']}"
self.record_listbox.insert(tk.END, record_text)
self.record_listbox.pack(pady=10)
delete_button = tk.Button(delete_window, text="删除选中记录", command=self.delete_selected_record, bg="#F44336", fg="white")
delete_button.pack(pady=10)
def delete_selected_record(self):
selected_index = self.record_listbox.curselection()
if not selected_index:
messagebox.showwarning("警告", "请先选择一条记录。")
return
records = load_records()
selected_record_text = self.record_listbox.get(selected_index)
selected_date = selected_record_text.split(",")[0].split(": ")[1]
success = delete_record(date=selected_date)
if success:
messagebox.showinfo("成功", "记录已删除!")
self.record_listbox.delete(selected_index)
else:
messagebox.showerror("错误", "未找到指定的记录!")
def show_statistics(self):
total_income, total_expense, income_categories, expense_categories = calculate_statistics()
stats_window = tk.Toplevel()
stats_window.title("统计信息")
stats_window.geometry("1000x600")
stats_window.configure(bg='#FFF8F0')
tk.Label(stats_window, text=f"总收入: {total_income}", font=('Arial', 14), bg='#FFF8F0').pack(pady=10)
tk.Label(stats_window, text=f"总支出: {total_expense}", font=('Arial', 14), bg='#FFF8F0').pack(pady=10)
tk.Label(stats_window, text="各类别收入:", font=('Arial', 14), bg='#FFF8F0').pack(pady=10)
for category, amount in income_categories.items():
tk.Label(stats_window, text=f" {category}: {amount}", bg='#FFF8F0').pack()
tk.Label(stats_window, text="各类别支出:", font=('Arial', 14), bg='#FFF8F0').pack(pady=10)
for category, amount in expense_categories.items():
tk.Label(stats_window, text=f" {category}: {amount}", bg='#FFF8F0').pack()
fig, axs = plt.subplots(1, 3, figsize=(15, 5))
if income_categories:
labels = list(income_categories.keys())
sizes = list(income_categories.values())
axs[0].pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
axs[0].set_title('各类收入占比')
if expense_categories:
labels = list(expense_categories.keys())
sizes = list(expense_categories.values())
axs[1].pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
axs[1].set_title('各类支出占比')
if total_income > 0 or total_expense > 0:
sizes = [total_income, total_expense]
labels = ['收入', '支出']
axs[2].pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
axs[2].set_title('收入和支出占比')
canvas = FigureCanvasTkAgg(fig, master=stats_window)
canvas.draw()
canvas.get_tk_widget().pack(pady=20)
def create_info_window(self):
info_window = tk.Toplevel()
info_window.title("个人信息")
info_window.geometry("400x350") # 增加窗口高度以容纳新信息
info_window.configure(bg='#FFF8F0')
label_font = font.Font(family='Arial', size=12)
tk.Label(info_window, text="姓名:", bg='#FFF8F0', font=label_font).grid(row=0, column=0, padx=5, pady=5)
self.name_entry = tk.Entry(info_window, width=30, font=label_font)
self.name_entry.grid(row=0, column=1, padx=5, pady=5)
tk.Label(info_window, text="邮箱:", bg='#FFF8F0', font=label_font).grid(row=1, column=0, padx=5, pady=5)
self.email_entry = tk.Entry(info_window, width=30, font=label_font)
self.email_entry.grid(row=1, column=1, padx=5, pady=5)
tk.Label(info_window, text="电话:", bg='#FFF8F0', font=label_font).grid(row=2, column=0, padx=5, pady=5)
self.phone_entry = tk.Entry(info_window, width=30, font=label_font)
self.phone_entry.grid(row=2, column=1, padx=5, pady=5)
# 显示记账总笔数
total_records = len(load_records()) # 获取总记录数
tk.Label(info_window, text="记账总笔数:", bg='#FFF8F0', font=label_font).grid(row=3, column=0, padx=5, pady=5)
tk.Label(info_window, text=str(total_records), bg='#FFF8F0', font=label_font).grid(row=3, column=1, padx=5, pady=5)
save_button = tk.Button(info_window, text="保存信息", command=self.save_info, bg="#4CAF50", fg="white", font=label_font)
save_button.grid(row=4, column=1, padx=5, pady=10)
cj_button = tk.Button(info_window, text="更改账本", command=self.change_json_window, bg="#4CAF50", fg="white", font=label_font)
cj_button.grid(row=5, column=1, padx=5, pady=10)
def save_info(self):
name = self.name_entry.get()
email = self.email_entry.get()
phone = self.phone_entry.get()
# 这里可以添加保存信息的逻辑,例如保存到文件或数据库
messagebox.showinfo("成功", "个人信息已保存!")
在这段代码中,我们实现了应用的登录,注册功能,和基本的记账,将数据保存到本地的json,读取账目等功能。
五、软件亮点
收支趋势分析功能
输入给定的日期范围后,软件将给出这个日期区间内的收支趋势分析,便于我们更好地规划消费。
支出收入可视化
根据支持/收入的分类生成饼图,让我们更直观地看到我们的收入/支出构成。
六、较大收获的事件
1.我们最初在讨论采用什么编程语言来编写程序时发生了分歧,这耗费了我们不少时间,最后达成一致使用py语言来编写程序。
2.在编写过程中,我们因为沟通问题导致两个不同的组员先后上传了各自的版本到仓库中,并花了一些时间来把功能整合起来,这也让我们意识到了相互沟通的重要性
3.在复审过程中,我们对于是否要保留登录系统进行了讨论,最后我们认为为了保证数据的隐私性,保留这个部分。
七、组员编程体验
童潇剑:相互沟通是这次编程中重要的一环,经过这次的磨合历练,我们后续的团队协作会更加顺利
陈潇健:现场编程对大家进行了磨合,之后的协作编程会越来越顺利。
董雯莉:谁说这编程难的,这编程太棒了。同时也感谢队友的辛勤付出!大家都很棒!
陈博涵:软件工程真是一门美妙的课程,让我享受到编程的乐趣。
洪庆杨:通过实践,让我锻炼思维,提升技能,增强协作能力。
曾炜坤:一下午的团队编程,在与队友的携手共进中,深感团队力量于代码世界的奇妙展现。
洪闽南:团队互相协作,沟通交流的氛围很好。
高涛:团队编程很有意思,我很享受团队相互交流共同推进项目开发这一过程。
舒锦城:从一开始的迷茫到最后的小有成果,感觉很不错。
八、组长打分
学号姓名 | 分数 |
---|---|
102201226陈潇健 | 96 |
102201235曾炜坤 | 97 |
102201234洪庆杨 | 95 |
102201224 陈博涵 | 96 |
182200311洪闽南 | 95 |
102202156高涛 | 96 |
042201520舒锦城 | 95 |
102201335董雯莉 | 96 |
九、github仓库地址和commit记录
仓库地址
github仓库地址:https://github.com/GoldenglowBF/CashBook
commit记录
部分组员与其他组员协作时在其他组员电脑中进行测试上传,故没有commit记录