之前写过的一篇文章:为hexo博客Fluid主题添加书单是创建了一个书单页面来管理书籍,但是每次想插入新书的时候有点麻烦,要通过编辑index.md,并复制<li>标签中的内容来新加一本书。所以我想着能不能用Python搞个图形化界面来直接填写书籍内容,然后更新书籍。
所以以下Python文件除了tk,其他都不需要安装,直接终端运行就可以实现编辑了
Python
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import shutil
from datetime import datetime
import re
class BookListManager:
def __init__(self, root):
self.root = root
self.root.title("书单管理器")
self.root.geometry("800x650") # 增加窗口高度以容纳新的备份路径选择框
# 设置默认路径
self.default_path = "/home/specialhua/specialhua/source/books"
if not os.path.exists(self.default_path):
self.default_path = "/home"
# 设置默认备份路径
self.default_backup_path = "/home/specialhua/Documents/books/books_md_backup"
if not os.path.exists(self.default_backup_path):
os.makedirs(self.default_backup_path)
# 文件路径框架
self.paths_frame = ttk.LabelFrame(root, text="文件路径设置", padding="5")
self.paths_frame.pack(fill="x", padx=5, pady=5)
# 源文件路径
self.source_frame = ttk.Frame(self.paths_frame)
self.source_frame.pack(fill="x", padx=5, pady=2)
ttk.Label(self.source_frame, text="源文件:").pack(side="left")
self.file_path = tk.StringVar()
ttk.Entry(self.source_frame, textvariable=self.file_path, width=50).pack(side="left", padx=5)
ttk.Button(self.source_frame, text="选择文件", command=self.choose_file).pack(side="left", padx=5)
# 备份路径
self.backup_frame = ttk.Frame(self.paths_frame)
self.backup_frame.pack(fill="x", padx=5, pady=2)
ttk.Label(self.backup_frame, text="备份至:").pack(side="left")
self.backup_path = tk.StringVar(value=self.default_backup_path)
ttk.Entry(self.backup_frame, textvariable=self.backup_path, width=50).pack(side="left", padx=5)
ttk.Button(self.backup_frame, text="选择路径", command=self.choose_backup_path).pack(side="left", padx=5)
# 书籍信息输入
self.book_frame = ttk.LabelFrame(root, text="书籍信息", padding="5")
self.book_frame.pack(fill="both", expand=True, padx=5, pady=5)
# 创建输入字段
self.fields = {
"书名": tk.StringVar(),
"作者": tk.StringVar(),
"出版时间": tk.StringVar(),
"豆瓣链接": tk.StringVar(),
"封面图片链接": tk.StringVar(),
"网盘链接": tk.StringVar(),
"提取码": tk.StringVar(),
"简介": tk.StringVar()
}
# 添加说明标签
ttk.Label(self.book_frame, text="提示:出版时间格式为 YYYY-MM-DD",
font=("", 9, "italic")).grid(row=0, column=1, columnspan=2, padx=5, sticky="w")
row = 1 # 从第1行开始,因为第0行用于显示提示信息
for field, var in self.fields.items():
ttk.Label(self.book_frame, text=field).grid(row=row, column=0, padx=5, pady=5, sticky="e")
if field == "简介":
text_widget = tk.Text(self.book_frame, height=3, width=50)
text_widget.grid(row=row, column=1, columnspan=2, padx=5, pady=5, sticky="w")
self.fields[field] = text_widget
else:
entry = ttk.Entry(self.book_frame, textvariable=var, width=50)
entry.grid(row=row, column=1, columnspan=2, padx=5, pady=5, sticky="w")
entry.bind('<FocusIn>', self.switch_to_chinese)
row += 1
# 按钮
self.button_frame = ttk.Frame(root)
self.button_frame.pack(fill="x", padx=5, pady=5)
ttk.Button(self.button_frame, text="生成更新", command=self.generate_new_file).pack(side="right", padx=5)
ttk.Button(self.button_frame, text="清空内容", command=self.clear_fields).pack(side="right", padx=5)
def switch_to_chinese(self, event):
"""切换到中文输入法"""
try:
os.system("fcitx5-remote -s pinyin")
except:
pass
def choose_file(self):
filename = filedialog.askopenfilename(
title="选择index.md文件",
initialdir=self.default_path,
filetypes=[("Markdown files", "*.md"), ("All files", "*.*")]
)
if filename:
self.file_path.set(filename)
def choose_backup_path(self):
"""选择备份文件保存路径"""
backup_dir = filedialog.askdirectory(
title="选择备份文件保存路径",
initialdir=self.backup_path.get()
)
if backup_dir:
self.backup_path.set(backup_dir)
def clear_fields(self):
for var in self.fields.values():
if isinstance(var, tk.Text):
var.delete(1.0, tk.END)
else:
var.set("")
def backup_file(self, original_path):
"""备份文件到指定路径"""
# 确保备份目录存在
backup_dir = self.backup_path.get()
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
filename = os.path.basename(original_path)
backup_name = f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}"
backup_path = os.path.join(backup_dir, backup_name)
shutil.copy2(original_path, backup_path)
return backup_path
def validate_date_format(self, date_str):
"""验证日期格式是否正确"""
pattern = r'^\d{4}-\d{2}-\d{2}$'
if not re.match(pattern, date_str):
return False
try:
datetime.strptime(date_str, '%Y-%m-%d')
return True
except ValueError:
return False
def generate_new_file(self):
if not self.file_path.get():
messagebox.showerror("错误", "请先选择index.md文件!")
return
# 验证日期格式
date_str = self.fields["出版时间"].get()
if not self.validate_date_format(date_str):
messagebox.showerror("错误", "出版时间格式不正确,请使用 YYYY-MM-DD 格式(如:2024-02-05)")
return
try:
# 备份原文件
backup_path = self.backup_file(self.file_path.get())
# 读取原文件内容
with open(self.file_path.get(), 'r', encoding='utf-8') as file:
content = file.read()
# 构建新的书籍HTML
new_book_html = f''' <!-- 书籍 -->
<li>
<div class="info">
<a href="{self.fields['豆瓣链接'].get()}" target="_blank" rel="noopener noreferrer" class="book-container">
<div class="book" title="{self.fields['书名'].get()}">
<img src="{self.fields['封面图片链接'].get()}" alt="{self.fields['书名'].get()}">
</div>
</a>
<div class="info-card">
<div class="hidden-content">
<p class="text">{self.fields['简介'].get("1.0", tk.END).strip()}</p>
</div>
<h3>《{self.fields['书名'].get()}》</h3>
<p>作者:{self.fields['作者'].get()}</p>
<p>出版时间:{self.fields['出版时间'].get()}</p>
<p>
<a href="{self.fields['网盘链接'].get()}" target="_blank" rel="noopener noreferrer">📥 下载</a>
</p>
<p class="pwd-text">
提取码:{self.fields['提取码'].get()}
</p>
</div>
</div>
</li>
'''
# 在最后一个</ul>标签前插入新书籍
last_ul_index = content.rindex('</ul>')
updated_content = content[:last_ul_index] + new_book_html + content[last_ul_index:]
# 写入新文件
with open(self.file_path.get(), 'w', encoding='utf-8') as file:
file.write(updated_content)
messagebox.showinfo("成功", f"文件已更新!\n原文件已备份为:{backup_path}")
self.clear_fields()
except Exception as e:
messagebox.showerror("错误", f"更新文件时出错:{str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = BookListManager(root)
root.mainloop()
图形化的界面如下:

在archlinux+hyprland下使用良好,于是接下来就可以创建desktop文件了:
a. 创建一个桌面入口文件 (book-manager.desktop):
BASH
[Desktop Entry]
Name=Book Manager
Comment=Manage book list for hexo blog
Exec=/usr/bin/python3 /path/to/your/book_manager.py
Icon=text-editor
Terminal=false
Type=Application
Categories=Utility;
b.将此文件保存到 ~/.local/share/applications/ 目录下:
BASH
mkdir -p ~/.local/share/applications/
cp book-manager.desktop ~/.local/share/applications/
chmod +x ~/.local/share/applications/book-manager.desktop
以后再更新书籍的时候就可以免去编辑index.md的麻烦了~