当前位置: 首页 > article >正文

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

不知不觉,在 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(日记内容)字段。这一结构设计为后续的日记管理提供了坚实基础,确保每一篇日记都能被有序存储与高效检索。

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

# 创建菜单栏
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 函数从数据库获取数据并填充列表,每一行展示日期和天气信息,点击即可查看详细内容。

# 列表页面
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 函数对关键词进行高亮显示,帮助用户快速定位关键信息。

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

def save_diary():
    global current_editing_date
    weather = weather_entry.get().strip()
    if not weather:
        messagebox.showwarning("警告", "请输入天气信息")
        return
    date = date_entry.get().strip()
    try:
        datetime.strptime(date, "%Y-%m-%d")
    except ValueError:
        messagebox.showwarning("警告", "日期格式错误,请使用 YYYY-MM-DD 格式")
        return
    content = 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 = None
        update_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 函数实现,用户输入关键词后,应用会在数据库中查询并在列表页面展示匹配结果,同样按照日期降序排列。这一功能大大提高了查找特定日记的效率。

def search_diaries():
    global is_searching, search_keyword
    keyword = simpledialog.askstring("搜索", "请输入日期或日记内容关键词:")
    if keyword:
        is_searching = True
        search_keyword = keyword
        update_diary_list()

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

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

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)

完整代码

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_date
    weather = weather_entry.get().strip()
    if not weather:
        messagebox.showwarning("警告", "请输入天气信息")
        return
    date = date_entry.get().strip()
    try:
        datetime.strptime(date, "%Y-%m-%d")
    except ValueError:
        messagebox.showwarning("警告", "日期格式错误,请使用 YYYY-MM-DD 格式")
        return
    content = 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 = None
        update_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("警告", "请选择一篇日记查看")
        return
    date = 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 = result
            show_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 = row
        values = (date, weather)
        diary_listbox.insert("", "end", values=values)


def search_diaries():
    global is_searching, search_keyword
    keyword = simpledialog.askstring("搜索", "请输入日期或日记内容关键词:")
    if keyword:
        is_searching = True
        search_keyword = keyword
        update_diary_list()


def return_to_home():
    global current_editing_date, is_searching
    current_editing_date = None
    is_searching = False
    search_keyword = ""
    update_diary_list()
    show_list_page()


def new_diary():
    global current_editing_date
    current_editing_date = None
    show_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("警告", "请选择一篇日记进行删除")
        return
    date = 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_date
    selected_item = diary_listbox.selection()
    if not selected_item:
        messagebox.showwarning("警告", "请选择一篇日记进行编辑")
        return
    date = 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 = result
            show_edit_page()
            current_editing_date = date
            date_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.kler.cn/a/585820.html

相关文章:

  • Java EE Web环境安装
  • MCU详解:嵌入式系统的“智慧之心”
  • 【Prometheus】prometheus监控pod资源,ingress,service资源以及如何通过annotations实现自动化监控
  • 宝塔-服务器部署(1)-环境准备
  • 如何处理PHP中的日期和时间问题
  • HTML块级元素和内联元素(简单易懂)
  • vue/react/vite前端项目打包的时候加上时间最简单版本,防止后端扯皮
  • Centos7系统基于docker下载ollama部署Deepseek-r1(GPU版不踩坑)
  • plantuml画甘特图gantt
  • SpringBoot中使用AJ-Captcha实现行为验证码(滑动拼图、点选文字)
  • stm32u5
  • std::stack和std::queue
  • iOS OC匹配多个文字修改颜色和字号
  • Language Models are Few-Shot Learners,GPT-3详细讲解
  • 【最后203篇系列】014 AI机器人-2
  • E2PRAM
  • 二叉树的所有路径
  • Python 与 JavaScript 交互及 Web 逆向分析全解析
  • 手机遥控开关技术解析与应用指南
  • C 语言分支与循环:构建程序逻辑的基石