用python给书单页面做一个图形化界面编辑工具

本文最后更新于 2025年2月5日 下午

之前写过的一篇文章:为hexo博客Fluid主题添加书单是创建了一个书单页面来管理书籍,但是每次想插入新书的时候有点麻烦,要通过编辑index.md,并复制<li>标签中的内容来新加一本书。所以我想着能不能用Python搞个图形化界面来直接填写书籍内容,然后更新书籍。

所以以下Python文件除了tk,其他都不需要安装,直接终端运行就可以实现编辑了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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):

1
2
3
4
5
6
7
8
[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/ 目录下:

1
2
3
mkdir -p ~/.local/share/applications/
cp book-manager.desktop ~/.local/share/applications/
chmod +x ~/.local/share/applications/book-manager.desktop

以后再更新书籍的时候就可以免去编辑index.md的麻烦了~


用python给书单页面做一个图形化界面编辑工具
https://inkcodes.com/2025/02/05/用python给书单页面做一个图形化界面编辑工具/
作者
Specialhua
发布于
2025年2月5日
更新于
2025年2月5日
许可协议