使用Tkinter和PyMuPDF开发简易PDF查看编辑器

在Python中,Tkinter是内置的GUI库,而PyMuPDF(fitz)是功能强大的PDF处理库。结合两者可以快速开发轻量级PDF工具。

环境准备

安装PyMuPDF库:

pip install pymupdf

Tkinter通常随Python标准库安装,无需额外操作。

基础框架搭建

创建主窗口和基础控件结构:

import tkinter as tk
from tkinter import filedialog
import fitz

class PDFViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF Viewer/Editor")
        self.current_page = 0
        self.doc = None
        
        # 创建菜单栏
        menubar = tk.Menu(root)
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Open", command=self.open_file)
        menubar.add_cascade(label="File", menu=filemenu)
        root.config(menu=menubar)
        
        # 创建画布用于显示PDF
        self.canvas = tk.Canvas(root, width=600, height=800)
        self.canvas.pack()
        
        # 添加导航按钮
        nav_frame = tk.Frame(root)
        nav_frame.pack(fill=tk.X)
        
        self.prev_btn = tk.Button(nav_frame, text="Previous", command=self.prev_page)
        self.prev_btn.pack(side=tk.LEFT)
        
        self.next_btn = tk.Button(nav_frame, text="Next", command=self.next_page)
        self.next_btn.pack(side=tk.LEFT)
        
        self.page_label = tk.Label(nav_frame, text="Page: 0/0")
        self.page_label.pack(side=tk.LEFT)

PDF渲染功能

添加PDF文件打开和页面渲染逻辑:

    def open_file(self):
        filepath = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")])
        if filepath:
            self.doc = fitz.open(filepath)
            self.current_page = 0
            self.display_page()
    
    def display_page(self):
        if not self.doc:
            return
            
        page = self.doc.load_page(self.current_page)
        zoom = 1.5  # 缩放因子
        mat = fitz.Matrix(zoom, zoom)
        pix = page.get_pixmap(matrix=mat)
        
        # 清除画布并显示新页面
        self.canvas.delete("all")
        img_data = pix.tobytes("ppm")
        img = tk.PhotoImage(data=img_data)
        self.canvas.image = img  # 保持引用
        self.canvas.create_image(0, 0, anchor=tk.NW, image=img)
        
        # 更新页面标签
        self.page_label.config(text=f"Page: {self.current_page+1}/{len(self.doc)}")

页面导航功能

实现前后翻页功能:

    def next_page(self):
        if self.doc and self.current_page < len(self.doc)-1:
            self.current_page += 1
            self.display_page()
    
    def prev_page(self):
        if self.doc and self.current_page > 0:
            self.current_page -= 1
            self.display_page()

简易文本编辑功能

添加文本标记和提取功能:

    def __init__(self, root):
        # ... 初始化代码 ...
        
        # 添加文本提取按钮
        self.extract_btn = tk.Button(nav_frame, text="Extract Text", command=self.extract_text)
        self.extract_btn.pack(side=tk.LEFT)
        
        # 添加文本框显示提取的文本
        self.text_frame = tk.Frame(root)
        self.text_frame.pack(fill=tk.BOTH, expand=True)
        
        self.text_box = tk.Text(self.text_frame, wrap=tk.WORD)
        self.text_box.pack(fill=tk.BOTH, expand=True)
    
    def extract_text(self):
        if not self.doc:
            return
            
        page = self.doc.load_page(self.current_page)
        text = page.get_text("text")
        self.text_box.delete(1.0, tk.END)
        self.text_box.insert(tk.END, text)

添加注释功能

实现简单的高亮和注释功能:

    def __init__(self, root):
        # ... 初始化代码 ...
        
        # 添加注释按钮
        self.annotate_btn = tk.Button(nav_frame, text="Add Highlight", command=self.add_highlight)
        self.annotate_btn.pack(side=tk.LEFT)
        
        # 绑定鼠标事件
        self.canvas.bind("<Button-1>", self.start_selection)
        self.canvas.bind("<B1-Motion>", self.drag_selection)
        self.canvas.bind("<ButtonRelease-1>", self.end_selection)
        self.selection_start = None
    
    def start_selection(self, event):
        self.selection_start = (event.x, event.y)
    
    def drag_selection(self, event):
        if self.selection_start:
            self.canvas.delete("selection")
            x1, y1 = self.selection_start
            x2, y2 = event.x, event.y
            self.canvas.create_rectangle(x1, y1, x2, y2, outline="red", tags="selection")
    
    def end_selection(self, event):
        if self.selection_start and self.doc:
            x1, y1 = self.selection_start
            x2, y2 = event.x, event.y
            page = self.doc.load_page(self.current_page)
            
            # 将画布坐标转换为PDF坐标
            zoom = 1.5
            rect = fitz.Rect(x1/zoom, y1/zoom, x2/zoom, y2/zoom)
            
            # 添加高亮注释
            highlight = page.add_highlight_annot(rect)
            self.doc.save("annotated.pdf", incremental=True)
            
            self.canvas.delete("selection")
            self.selection_start = None
            self.display_page()  # 刷新显示

运行应用

最后添加启动代码:

if __name__ == "__main__":
    root = tk.Tk()
    app = PDFViewer(root)
    root.mainloop()

功能扩展建议

  • 添加页面旋转功能
  • 实现PDF合并和拆分功能
  • 添加书签支持
  • 实现表单填写功能
  • 添加搜索功能

这个基础实现展示了如何结合Tkinter和PyMuPDF创建功能性PDF工具。PyMuPDF提供了丰富的PDF操作能力,而Tkinter则提供了直观的用户界面。开发者可以根据需要进一步扩展功能。

更多推荐