我的创作纪念日 打造高效 Python 日记本应用:从基础搭建到功能优化全解析

ops/2025/3/16 7:17:28/

不知不觉,在 CSDN 写博客已经有 5 年的时间了。这 5 年,就像一场充满惊喜与挑战的奇妙旅程,在我的成长之路上留下了深深浅浅的印记。到现在我的博客数据:

展现量92万
阅读量31万
粉丝数2万
文章数200

这样的数据是我在写第一篇博客时未曾想到的。
回顾这 5 年的博客生涯,心中满是感慨。未来,我仍将与 CSDN 相伴前行,持续输出更多有价值的内容,记录学习历程的同时为技术社区贡献自己的一份力量 。

目录

  • 打造高效 Python 日记本应用:从基础搭建到功能优化全解析
    • 数据库搭建:稳固的数据基石
    • 界面设计:美观与实用的融合
      • 1. 菜单栏:便捷操作入口
      • 2. 列表页面:日记的有序呈现
      • 3. 编辑与查看页面:专注内容创作与回顾
    • 功能实现:强大的交互体验
      • 1. 日记保存与更新
      • 2. 搜索与排序
      • 3. 高亮显示:精准定位关键信息
    • 完整代码

打造高效 Python 日记本应用:从基础搭建到功能优化全解析

在数字化时代,记录生活点滴的方式多种多样,而开发一个属于自己的日记本应用不仅充满趣味,还能极大提升记录的效率与个性化程度。今天,我们就一同深入探讨如何使用 Python 和 tkinter 库构建一个功能丰富的日记本应用,从数据库的连接与操作,到界面设计与交互逻辑的实现,再到搜索与高亮显示等高级功能的优化,全方位领略 Python 在桌面应用开发中的魅力。
在这里插入图片描述
在这里插入图片描述

数据库搭建:稳固的数据基石

应用的核心是数据存储,这里我们选择 SQLite 数据库。通过 sqlite3 模块连接数据库并创建日记表 diaries,包含 date(日期,主键)、weather(天气)和 content(日记内容)字段。这一结构设计为后续的日记管理提供了坚实基础,确保每一篇日记都能被有序存储与高效检索。

python"># 连接到 SQLite 数据库
conn = sqlite3.connect('diaries.db')
c = conn.cursor()# 创建日记表(如果不存在)
c.execute('''CREATE TABLE IF NOT EXISTS diaries(date TEXT PRIMARY KEY, weather TEXT, content TEXT)''')
conn.commit()

界面设计:美观与实用的融合

1. 菜单栏:便捷操作入口

利用 tkinterMenu 组件构建菜单栏,包含新建、搜索、返回主页、删除和编辑等功能选项。简洁明了的布局,为用户提供了直观的操作路径,轻松实现对日记的各种管理操作。

python"># 创建菜单栏
menu_bar = tk.Menu(root)
file_menu = tk.Menu(menu_bar, tearoff=0)
# 去除不支持的 -fg 和 -bg 选项
file_menu.add_command(label="新建", command=new_diary, font=FONT)
file_menu.add_command(label="搜索日记", command=search_diaries, font=FONT)
file_menu.add_command(label="返回主页", command=return_to_home, font=FONT)
file_menu.add_command(label="删除日记", command=delete_diary, font=FONT)
file_menu.add_command(label="编辑日记", command=edit_diary, font=FONT)
menu_bar.add_cascade(label="文件", menu=file_menu, font=FONT)
root.config(menu=menu_bar)

2. 列表页面:日记的有序呈现

ttk.Treeview 组件用于展示日记列表,按照日期降序排列,让用户能快速找到最新的日记。通过 update_diary_list 函数从数据库获取数据并填充列表,每一行展示日期和天气信息,点击即可查看详细内容。

python"># 列表页面
list_frame = tk.Frame(root)
# 去除 font 选项
diary_listbox = ttk.Treeview(list_frame, columns=("日期", "天气"), show="headings")
diary_listbox.heading("日期", text="日期")
diary_listbox.heading("天气", text="天气")
diary_listbox.column("日期", width=150)
diary_listbox.column("天气", width=100)
diary_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
diary_listbox.bind("<ButtonRelease-1>", open_diary)
scrollbar = ttk.Scrollbar(list_frame, command=diary_listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
diary_listbox.config(yscrollcommand=scrollbar.set)
update_diary_list()
list_frame.grid(row=1, column=0, sticky="nsew")

3. 编辑与查看页面:专注内容创作与回顾

编辑页面提供了日期、天气输入框以及用于撰写日记内容的 Text 组件,方便用户记录生活。查看页面则以只读形式展示日记详情,在搜索时还能通过 highlight_text 函数对关键词进行高亮显示,帮助用户快速定位关键信息。

python">def show_edit_page():list_frame.grid_forget()view_frame.grid_forget()edit_frame.grid(row=1, column=0, sticky="nsew")date_entry.delete(0, tk.END)date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))weather_entry.delete(0, tk.END)text.delete("1.0", tk.END)def show_view_page(date, weather, content):list_frame.grid_forget()edit_frame.grid_forget()view_frame.grid(row=1, column=0, sticky="nsew")view_date_label.config(text=f"日期: {date}")view_weather_label.config(text=f"天气: {weather}")view_text.config(state=tk.NORMAL)view_text.delete("1.0", tk.END)view_text.insert(tk.END, content)if is_searching:highlight_text(view_text, search_keyword)view_text.config(state=tk.DISABLED)

功能实现:强大的交互体验

1. 日记保存与更新

save_diary 函数负责将用户输入的日记内容保存到数据库。如果日记已存在,会提示用户是否覆盖,确保数据的准确性与完整性。

python">def save_diary():global current_editing_dateweather = weather_entry.get().strip()if not weather:messagebox.showwarning("警告", "请输入天气信息")returndate = date_entry.get().strip()try:datetime.strptime(date, "%Y-%m-%d")except ValueError:messagebox.showwarning("警告", "日期格式错误,请使用 YYYY-MM-DD 格式")returncontent = text.get("1.0", tk.END).strip()try:if current_editing_date:if current_editing_date != date:c.execute("DELETE FROM diaries WHERE date=?", (current_editing_date,))c.execute("INSERT OR REPLACE INTO diaries VALUES (?,?,?)", (date, weather, content))conn.commit()messagebox.showinfo("提示", f"日期为 {date} 的日记已更新")else:c.execute("INSERT INTO diaries VALUES (?,?,?)", (date, weather, content))conn.commit()messagebox.showinfo("提示", f"日记已保存,日期: {date}")current_editing_date = Noneupdate_diary_list()show_list_page()except sqlite3.IntegrityError:if messagebox.askyesno("提示", f"该日期 {date} 的日记已存在,是否覆盖?"):c.execute("UPDATE diaries SET weather=?, content=? WHERE date=?", (weather, content, date))conn.commit()messagebox.showinfo("提示", f"日期为 {date} 的日记已更新")update_diary_list()show_list_page()else:messagebox.showinfo("提示", f"取消保存日期为 {date} 的日记")except Exception as e:messagebox.showerror("错误", f"保存失败: {e}")

2. 搜索与排序

搜索功能通过 search_diaries 函数实现,用户输入关键词后,应用会在数据库中查询并在列表页面展示匹配结果,同样按照日期降序排列。这一功能大大提高了查找特定日记的效率。

python">def search_diaries():global is_searching, search_keywordkeyword = simpledialog.askstring("搜索", "请输入日期或日记内容关键词:")if keyword:is_searching = Truesearch_keyword = keywordupdate_diary_list()

3. 高亮显示:精准定位关键信息

highlight_text 函数利用 tkinterText 组件标签功能,在查看日记时对搜索关键词进行高亮显示,使关键信息一目了然。

python">def highlight_text(text_widget, keyword):text_widget.tag_configure("highlight", background="yellow")text = text_widget.get("1.0", tk.END)pattern = re.compile(re.escape(keyword), re.IGNORECASE)for match in pattern.finditer(text):start_index = f"1.0+{match.start()}c"end_index = f"1.0+{match.end()}c"text_widget.tag_add("highlight", start_index, end_index)

完整代码

python">import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
from tkinter import simpledialog
from datetime import datetime
import sqlite3
import re# 连接到 SQLite 数据库
conn = sqlite3.connect('diaries.db')
c = conn.cursor()# 创建日记表(如果不存在)
c.execute('''CREATE TABLE IF NOT EXISTS diaries(date TEXT PRIMARY KEY, weather TEXT, content TEXT)''')
conn.commit()current_editing_date = None
is_searching = False
search_keyword = ""
# 统一字体
FONT = ("Arial", 12)def save_diary():global current_editing_dateweather = weather_entry.get().strip()if not weather:messagebox.showwarning("警告", "请输入天气信息")returndate = date_entry.get().strip()try:datetime.strptime(date, "%Y-%m-%d")except ValueError:messagebox.showwarning("警告", "日期格式错误,请使用 YYYY-MM-DD 格式")returncontent = text.get("1.0", tk.END).strip()try:if current_editing_date:if current_editing_date != date:c.execute("DELETE FROM diaries WHERE date=?", (current_editing_date,))c.execute("INSERT OR REPLACE INTO diaries VALUES (?,?,?)", (date, weather, content))conn.commit()messagebox.showinfo("提示", f"日期为 {date} 的日记已更新")else:c.execute("INSERT INTO diaries VALUES (?,?,?)", (date, weather, content))conn.commit()messagebox.showinfo("提示", f"日记已保存,日期: {date}")current_editing_date = Noneupdate_diary_list()show_list_page()except sqlite3.IntegrityError:if messagebox.askyesno("提示", f"该日期 {date} 的日记已存在,是否覆盖?"):c.execute("UPDATE diaries SET weather=?, content=? WHERE date=?", (weather, content, date))conn.commit()messagebox.showinfo("提示", f"日期为 {date} 的日记已更新")update_diary_list()show_list_page()else:messagebox.showinfo("提示", f"取消保存日期为 {date} 的日记")except Exception as e:messagebox.showerror("错误", f"保存失败: {e}")def open_diary(event=None):selected_item = diary_listbox.selection()if not selected_item:messagebox.showwarning("警告", "请选择一篇日记查看")returndate = diary_listbox.item(selected_item, "values")[0]try:c.execute("SELECT weather, content FROM diaries WHERE date=?", (date,))result = c.fetchone()if result:weather, content = resultshow_view_page(date, weather, content)else:messagebox.showwarning("警告", f"未找到日期为 {date} 的日记")except Exception as e:messagebox.showerror("错误", f"打开失败: {e}")def update_diary_list():for item in diary_listbox.get_children():diary_listbox.delete(item)if is_searching:c.execute("SELECT date, weather, content FROM diaries WHERE date LIKE? OR content LIKE? ORDER BY date DESC",('%' + search_keyword + '%', '%' + search_keyword + '%'))else:c.execute("SELECT date, weather, content FROM diaries ORDER BY date DESC")rows = c.fetchall()for row in rows:date, weather, content = rowvalues = (date, weather)diary_listbox.insert("", "end", values=values)def search_diaries():global is_searching, search_keywordkeyword = simpledialog.askstring("搜索", "请输入日期或日记内容关键词:")if keyword:is_searching = Truesearch_keyword = keywordupdate_diary_list()def return_to_home():global current_editing_date, is_searchingcurrent_editing_date = Noneis_searching = Falsesearch_keyword = ""update_diary_list()show_list_page()def new_diary():global current_editing_datecurrent_editing_date = Noneshow_edit_page()def show_list_page():list_frame.grid(row=1, column=0, sticky="nsew")edit_frame.grid_forget()view_frame.grid_forget()def show_edit_page():list_frame.grid_forget()view_frame.grid_forget()edit_frame.grid(row=1, column=0, sticky="nsew")date_entry.delete(0, tk.END)date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))weather_entry.delete(0, tk.END)text.delete("1.0", tk.END)def show_view_page(date, weather, content):list_frame.grid_forget()edit_frame.grid_forget()view_frame.grid(row=1, column=0, sticky="nsew")view_date_label.config(text=f"日期: {date}")view_weather_label.config(text=f"天气: {weather}")view_text.config(state=tk.NORMAL)view_text.delete("1.0", tk.END)view_text.insert(tk.END, content)if is_searching:highlight_text(view_text, search_keyword)view_text.config(state=tk.DISABLED)def delete_diary():selected_item = diary_listbox.selection()if not selected_item:messagebox.showwarning("警告", "请选择一篇日记进行删除")returndate = diary_listbox.item(selected_item, "values")[0]if messagebox.askyesno("确认删除", f"确定要删除日期为 {date} 的日记吗?"):try:c.execute("DELETE FROM diaries WHERE date=?", (date,))conn.commit()messagebox.showinfo("提示", f"日期为 {date} 的日记已删除")if is_searching:update_diary_list()else:update_diary_list()show_list_page()except Exception as e:messagebox.showerror("错误", f"删除失败: {e}")def edit_diary():global current_editing_dateselected_item = diary_listbox.selection()if not selected_item:messagebox.showwarning("警告", "请选择一篇日记进行编辑")returndate = diary_listbox.item(selected_item, "values")[0]try:c.execute("SELECT weather, content FROM diaries WHERE date=?", (date,))result = c.fetchone()if result:weather, content = resultshow_edit_page()current_editing_date = datedate_entry.delete(0, tk.END)date_entry.insert(0, date)weather_entry.delete(0, tk.END)weather_entry.insert(0, weather)text.delete("1.0", tk.END)text.insert(tk.END, content)else:messagebox.showwarning("警告", f"未找到日期为 {date} 的日记")except Exception as e:messagebox.showerror("错误", f"编辑失败: {e}")def highlight_text(text_widget, keyword):text_widget.tag_configure("highlight", background="yellow")text = text_widget.get("1.0", tk.END)pattern = re.compile(re.escape(keyword), re.IGNORECASE)for match in pattern.finditer(text):start_index = f"1.0+{match.start()}c"end_index = f"1.0+{match.end()}c"text_widget.tag_add("highlight", start_index, end_index)root = tk.Tk()
root.title("日记本")
root.geometry("800x600")# 创建菜单栏
menu_bar = tk.Menu(root)
file_menu = tk.Menu(menu_bar, tearoff=0)
# 去除不支持的 -fg 和 -bg 选项
file_menu.add_command(label="新建", command=new_diary, font=FONT)
file_menu.add_command(label="搜索日记", command=search_diaries, font=FONT)
file_menu.add_command(label="返回主页", command=return_to_home, font=FONT)
file_menu.add_command(label="删除日记", command=delete_diary, font=FONT)
file_menu.add_command(label="编辑日记", command=edit_diary, font=FONT)
menu_bar.add_cascade(label="文件", menu=file_menu, font=FONT)
root.config(menu=menu_bar)# 创建样式对象
style = ttk.Style()
# 设置 Treeview 字体
style.configure("Treeview", font=FONT)
style.configure("Treeview.Heading", font=FONT)# 列表页面
list_frame = tk.Frame(root)
# 去除 font 选项
diary_listbox = ttk.Treeview(list_frame, columns=("日期", "天气"), show="headings")
diary_listbox.heading("日期", text="日期")
diary_listbox.heading("天气", text="天气")
diary_listbox.column("日期", width=150)
diary_listbox.column("天气", width=100)
diary_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
diary_listbox.bind("<ButtonRelease-1>", open_diary)
scrollbar = ttk.Scrollbar(list_frame, command=diary_listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
diary_listbox.config(yscrollcommand=scrollbar.set)
update_diary_list()
list_frame.grid(row=1, column=0, sticky="nsew")# 分隔线
separator = ttk.Separator(root, orient=tk.HORIZONTAL)
separator.grid(row=2, column=0, sticky="ew")# 编辑页面
edit_frame = tk.Frame(root)
date_label = tk.Label(edit_frame, text="日期 (YYYY-MM-DD):", font=FONT)
date_label.grid(row=0, column=0, padx=10, pady=5)
date_entry = tk.Entry(edit_frame, font=FONT)
date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))
date_entry.grid(row=0, column=1, padx=10, pady=5)
weather_label = tk.Label(edit_frame, text="输入天气:", font=FONT)
weather_label.grid(row=1, column=0, padx=10, pady=5)
weather_entry = tk.Entry(edit_frame, font=FONT)
weather_entry.grid(row=1, column=1, padx=10, pady=5)
text = tk.Text(edit_frame, wrap=tk.WORD, font=FONT)
text.grid(row=2, column=0, columnspan=2, padx=10, pady=5, sticky="nsew")
save_button = tk.Button(edit_frame, text="保存", command=save_diary, font=FONT)
save_button.grid(row=3, column=0, columnspan=2, padx=10, pady=5)# 查看页面
view_frame = tk.Frame(root)
view_date_label = tk.Label(view_frame, text="", font=FONT)
view_date_label.grid(row=0, column=0, padx=10, pady=5)
view_weather_label = tk.Label(view_frame, text="", font=FONT)
view_weather_label.grid(row=1, column=0, padx=10, pady=5)
view_text = tk.Text(view_frame, wrap=tk.WORD, state=tk.DISABLED, font=FONT)
view_text.grid(row=2, column=0, padx=10, pady=5, sticky="nsew")# 状态标签
status_label = tk.Label(root, text="准备就绪", font=FONT)
status_label.grid(row=3, column=0, sticky="ew")# 设置网格权重,使界面可伸缩
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)root.mainloop()# 关闭数据库连接
conn.close()

http://www.ppmy.cn/ops/166148.html

相关文章

Kafka精选面试题

1. 如何保证幂等性? 幂等性其实是消息的一致性, 生产和消费都只有一次, 所以分为生产者幂等性和消费者幂等性. 实际开发过程中, 一般只会保证消费幂等性, 所以面试时直接回答消费幂等就行 做法就是做唯一id, 在消费端做个判断,如果唯一id已存在则不做消费处理, 这个唯一id一般…

分支与循环(上)

1.if else语句 1.1:if else语句的三种情况 //第一种 if(判断条件) {执行代码块1; }//第二种 if(判断条件) {执行代码块1; } else { 执行代码块2; }//第三种 if(判断条件1) {执行代码块1; } else if&#xff08;判断条件2&#xff09; { 执行代码块2; } else if&#xff08;判…

CentOS 7 系统上安装 SQLite

1. 检查系统更新 在安装新软件之前&#xff0c;建议先更新系统的软件包列表&#xff0c;以确保使用的是最新的软件源和补丁。打开终端&#xff0c;执行以下命令&#xff1a; sudo yum update -y -y 选项表示在更新过程中自动回答 “yes”&#xff0c;避免手动确认。 2. 安装 …

【Go学习】04-4-Gorm框架-增删改查事务钩子

【Go学习】04-4-Gorm框架-增删改查 增删改查插入数据用指定的字段创建忽略字段批量插入map创建sql表达式使用原生sql语句 更新数据保存数据更新单个列更新多列更新选定的字段表达式子查询更新 删除数据查询数据查询函数whereselectorder分页count分组直接执行sql语句 事务和Hoo…

74.HarmonyOS NEXT ImageItemView组件深度剖析:组件基础结构与核心状态管理(一)

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT ImageItemView组件深度剖析&#xff1a;组件基础结构与核心状态管理(一) 文章目录 HarmonyOS NEXT ImageItemView组件深度剖析&…

电路原理(电容 集成电路NE555)

电容 1.特性&#xff1a;充放电&#xff0c;隔直流&#xff0c;通交流 2.电容是通过聚集正负电荷来存储电能的 3.电容充放电过程可等效为导通回路 4.多电容并联可以把容量叠加&#xff0c;但是多电容串联就不会&#xff0c;只会叠加电容的耐压值。 6.电容充放电时相当于通路&a…

二叉树的层序遍历(102)

102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* …

《Python实战进阶》No23: 使用 Selenium 自动化浏览器操作

No23: 使用 Selenium 自动化浏览器操作 摘要 Selenium 是自动化浏览器操作的“瑞士军刀”&#xff0c;可模拟人类行为操作网页&#xff0c;适用于爬虫、测试、重复任务自动化等场景。本集通过代码驱动实战&#xff0c;从安装配置到复杂交互&#xff0c;带你掌握 Selenium 的核…