用Python的tkinter库制作一个美观的Markdown阅读器,核心功能包括导入md文件、设置字体大小/颜色/背景色,以及上一页、下一页的翻页功能。



实现一个功能完整、界面美观的Markdown阅读器,核心思路是:
tkinter搭建主界面,包含菜单栏、工具栏和阅读区域markdown2库将md文件转换为HTML,再用tkinter的Text组件显示首先确保安装依赖:
pip install markdown2import tkinter as tkfrom tkinter import filedialog, colorchooser, fontimport markdown2import osclassMarkdownReader:def__init__(self, root): self.root = root self.root.title("优雅的Markdown阅读器") self.root.geometry("1000x700") # 设置初始窗口大小# 初始化变量 self.current_file = None# 当前打开的文件路径 self.content_html = ""# 转换后的HTML内容 self.font_size = 12# 默认字体大小 self.font_color = "#000000"# 默认字体颜色(黑色) self.bg_color = "#ffffff"# 默认背景色(白色) self.current_page = 0# 当前页码 self.pages = [] # 分页后的内容列表# 设置默认字体 self.default_font = font.Font(family="微软雅黑", size=self.font_size)# 创建菜单栏 self.create_menu()# 创建工具栏 self.create_toolbar()# 创建阅读区域 self.create_reading_area()# 绑定窗口大小变化事件 self.root.bind("<Configure>", self.on_window_resize)defcreate_menu(self):"""创建菜单栏""" menubar = tk.Menu(self.root) self.root.config(menu=menubar)# 文件菜单 file_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="文件", menu=file_menu) file_menu.add_command(label="打开Markdown文件", command=self.open_md_file) file_menu.add_separator() file_menu.add_command(label="退出", command=self.root.quit)# 设置菜单 setting_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="设置", menu=setting_menu) setting_menu.add_command(label="调整字体大小", command=self.set_font_size) setting_menu.add_command(label="设置字体颜色", command=self.set_font_color) setting_menu.add_command(label="设置背景颜色", command=self.set_bg_color)defcreate_toolbar(self):"""创建工具栏(翻页按钮)""" toolbar = tk.Frame(self.root, bg="#f0f0f0", height=40) toolbar.pack(fill=tk.X, side=tk.TOP)# 上一页按钮 self.prev_btn = tk.Button( toolbar, text="上一页", command=self.prev_page, font=("微软雅黑", 10), width=10, state=tk.DISABLED ) self.prev_btn.pack(side=tk.LEFT, padx=20, pady=5)# 页码显示 self.page_label = tk.Label( toolbar, text="第 0 / 0 页", bg="#f0f0f0", font=("微软雅黑", 10) ) self.page_label.pack(side=tk.LEFT, padx=20)# 下一页按钮 self.next_btn = tk.Button( toolbar, text="下一页", command=self.next_page, font=("微软雅黑", 10), width=10, state=tk.DISABLED ) self.next_btn.pack(side=tk.LEFT, padx=20, pady=5)defcreate_reading_area(self):"""创建阅读区域"""# 主阅读框(带滚动条) read_frame = tk.Frame(self.root) read_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)# 滚动条 scrollbar = tk.Scrollbar(read_frame) scrollbar.pack(side=tk.RIGHT, fill=tk.Y)# 文本显示区域 self.text_area = tk.Text( read_frame, font=self.default_font, wrap=tk.WORD, fg=self.font_color, bg=self.bg_color, yscrollcommand=scrollbar.set, state=tk.DISABLED ) self.text_area.pack(fill=tk.BOTH, expand=True) scrollbar.config(command=self.text_area.yview)defopen_md_file(self):"""打开并加载Markdown文件""" file_path = filedialog.askopenfilename( title="选择Markdown文件", filetypes=[("Markdown文件", "*.md"), ("所有文件", "*.*")] )if file_path: self.current_file = file_path self.root.title(f"优雅的Markdown阅读器 - {os.path.basename(file_path)}")# 读取并转换Markdown为HTMLwith open(file_path, "r", encoding="utf-8") as f: md_content = f.read()# 转换Markdown(启用代码高亮、表格等扩展) self.content_html = markdown2.markdown( md_content, extras=["fenced-code-blocks", "tables", "strike", "superscript"] )# 重置页码并分页 self.current_page = 0 self.split_content_to_pages()# 显示第一页 self.show_current_page()# 启用翻页按钮if len(self.pages) > 1: self.next_btn.config(state=tk.NORMAL)defsplit_content_to_pages(self):"""将内容按当前窗口高度分页""" self.pages = []ifnot self.content_html:return# 临时启用文本框以计算内容高度 self.text_area.config(state=tk.NORMAL) self.text_area.delete(1.0, tk.END) self.text_area.insert(tk.END, self.content_html)# 获取文本框可显示的行数 line_height = self.text_area.dlineinfo(1.0)[3] # 行高 visible_height = self.text_area.winfo_height() # 可见高度 lines_per_page = int(visible_height / line_height) - 2# 预留2行余量# 分页 total_lines = int(self.text_area.index(tk.END).split(".")[0])for i in range(0, total_lines, lines_per_page): start = f"{i+1}.0" end = f"{i+lines_per_page+1}.0"if (i+lines_per_page) < total_lines else tk.END page_content = self.text_area.get(start, end) self.pages.append(page_content)# 恢复文本框为只读 self.text_area.delete(1.0, tk.END) self.text_area.config(state=tk.DISABLED)defshow_current_page(self):"""显示当前页内容"""ifnot self.pages:return# 更新页码显示 self.page_label.config(text=f"第 {self.current_page+1} / {len(self.pages)} 页")# 更新按钮状态 self.prev_btn.config(state=tk.NORMAL if self.current_page > 0else tk.DISABLED) self.next_btn.config(state=tk.NORMAL if self.current_page < len(self.pages)-1else tk.DISABLED)# 显示内容 self.text_area.config(state=tk.NORMAL) self.text_area.delete(1.0, tk.END) self.text_area.insert(tk.END, self.pages[self.current_page]) self.text_area.config(state=tk.DISABLED)defprev_page(self):"""上一页"""if self.current_page > 0: self.current_page -= 1 self.show_current_page()defnext_page(self):"""下一页"""if self.current_page < len(self.pages)-1: self.current_page += 1 self.show_current_page()defset_font_size(self):"""调整字体大小"""defapply_size():try: new_size = int(size_entry.get())if8 <= new_size <= 72: # 限制字体大小范围 self.font_size = new_size self.default_font.config(size=self.font_size) self.text_area.config(font=self.default_font)# 重新分页并显示当前页 self.split_content_to_pages() self.show_current_page() top.destroy()except ValueError:pass# 创建弹窗 top = tk.Toplevel(self.root) top.title("设置字体大小") top.geometry("300x150") top.resizable(False, False) tk.Label(top, text="请输入字体大小(8-72):", font=("微软雅黑", 10)).pack(pady=20) size_entry = tk.Entry(top, font=("微软雅黑", 10), width=10) size_entry.insert(0, str(self.font_size)) size_entry.pack(pady=5) tk.Button(top, text="确认", command=apply_size, font=("微软雅黑", 10)).pack(pady=10)defset_font_color(self):"""设置字体颜色""" color = colorchooser.askcolor(title="选择字体颜色")[1]if color: self.font_color = color self.text_area.config(fg=self.font_color)defset_bg_color(self):"""设置背景颜色""" color = colorchooser.askcolor(title="选择背景颜色")[1]if color: self.bg_color = color self.text_area.config(bg=self.bg_color)defon_window_resize(self, event):"""窗口大小变化时重新分页"""if self.current_file and event.widget == self.root:# 避免频繁触发,延迟执行 self.root.after(200, self.split_content_to_pages) self.root.after(200, self.show_current_page)if __name__ == "__main__": root = tk.Tk() app = MarkdownReader(root) root.mainloop()界面布局:
核心功能:
markdown2库将md文件转换为可读文本,支持代码块、表格等常用扩展交互优化:
你可以直接运行代码,点击「文件」->「打开Markdown文件」选择md文件即可阅读,也可以通过「设置」菜单调整字体大小、颜色和背景色,使用工具栏的按钮进行翻页。