基于Python實現(xiàn)PDF動畫翻頁效果的閱讀器
主要功能包括:
- 加載 PDF 文件
- 顯示當前頁面
- 上一頁/下一頁切換
- 頁面切換動畫
C:\pythoncode\new\pdfreader.py
全部代碼
import wx import fitz # PyMuPDF from PIL import Image import time class PDFReader(wx.Frame): def __init__(self, parent, title): super(PDFReader, self).__init__(parent, title=title, size=(800, 600)) self.current_page = 0 self.doc = None self.page_images = [] self.animation_offset = 0 self.is_animating = False self.animation_direction = 0 self.next_page_idx = 0 self.init_ui() self.init_timer() def init_ui(self): self.panel = wx.Panel(self) vbox = wx.BoxSizer(wx.VERTICAL) # 創(chuàng)建工具欄 toolbar = wx.BoxSizer(wx.HORIZONTAL) open_btn = wx.Button(self.panel, label='打開PDF') prev_btn = wx.Button(self.panel, label='上一頁') next_btn = wx.Button(self.panel, label='下一頁') open_btn.Bind(wx.EVT_BUTTON, self.on_open) prev_btn.Bind(wx.EVT_BUTTON, self.on_prev_page) next_btn.Bind(wx.EVT_BUTTON, self.on_next_page) toolbar.Add(open_btn, 0, wx.ALL, 5) toolbar.Add(prev_btn, 0, wx.ALL, 5) toolbar.Add(next_btn, 0, wx.ALL, 5) self.pdf_panel = wx.Panel(self.panel) self.pdf_panel.SetBackgroundColour(wx.WHITE) self.pdf_panel.Bind(wx.EVT_PAINT, self.on_paint) vbox.Add(toolbar, 0, wx.EXPAND) vbox.Add(self.pdf_panel, 1, wx.EXPAND | wx.ALL, 5) self.panel.SetSizer(vbox) self.Centre() def init_timer(self): # 創(chuàng)建定時器用于動畫 self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_timer) def on_open(self, event): with wx.FileDialog(self, "選擇PDF文件", wildcard="PDF files (*.pdf)|*.pdf", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return pdf_path = fileDialog.GetPath() self.load_pdf(pdf_path) def load_pdf(self, path): self.doc = fitz.open(path) self.current_page = 0 self.page_images = [] # 預(yù)加載所有頁面 for page in self.doc: pix = page.get_pixmap() img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) self.page_images.append(img) self.render_current_page() def render_current_page(self): if not self.doc or self.current_page >= len(self.page_images): return panel_size = self.pdf_panel.GetSize() # 創(chuàng)建背景 background = Image.new('RGB', (panel_size.width, panel_size.height), 'WHITE') # 獲取當前頁面并調(diào)整大小 current_img = self.page_images[self.current_page].resize( (panel_size.width, panel_size.height), Image.LANCZOS) # 如果在動畫中,需要繪制兩個頁面 if self.is_animating: next_img = self.page_images[self.next_page_idx].resize( (panel_size.width, panel_size.height), Image.LANCZOS) # 計算位置并粘貼圖像 if self.animation_direction > 0: # 向右翻頁 background.paste(current_img, (-self.animation_offset, 0)) background.paste(next_img, (panel_size.width - self.animation_offset, 0)) else: # 向左翻頁 background.paste(current_img, (self.animation_offset, 0)) background.paste(next_img, (-panel_size.width + self.animation_offset, 0)) else: # 非動畫狀態(tài),直接顯示當前頁 background.paste(current_img, (0, 0)) # 轉(zhuǎn)換為wx.Bitmap self.current_bitmap = wx.Bitmap.FromBuffer( panel_size.width, panel_size.height, background.tobytes()) # 刷新顯示 self.pdf_panel.Refresh() def start_animation(self, direction): """開始頁面切換動畫""" if self.is_animating: return next_page = self.current_page + direction if next_page < 0 or next_page >= len(self.page_images): return self.is_animating = True self.animation_direction = direction self.next_page_idx = next_page self.animation_offset = 0 # 啟動定時器,控制動畫 self.timer.Start(16) # 約60fps def on_timer(self, event): """定時器事件處理,更新動畫""" if not self.is_animating: return # 更新動畫偏移 panel_width = self.pdf_panel.GetSize().width step = panel_width // 15 # 調(diào)整這個值可以改變動畫速度 self.animation_offset += step # 檢查動畫是否完成 if self.animation_offset >= panel_width: self.animation_offset = 0 self.is_animating = False self.current_page = self.next_page_idx self.timer.Stop() self.render_current_page() def on_prev_page(self, event): if self.is_animating or not self.doc: return if self.current_page > 0: self.start_animation(-1) def on_next_page(self, event): if self.is_animating or not self.doc: return if self.current_page < len(self.page_images) - 1: self.start_animation(1) def on_paint(self, event): if not hasattr(self, 'current_bitmap'): return dc = wx.PaintDC(self.pdf_panel) dc.DrawBitmap(self.current_bitmap, 0, 0, True) def main(): app = wx.App() frame = PDFReader(None, title='PDF閱讀器') frame.Show() app.MainLoop() if __name__ == '__main__': main()
代碼結(jié)構(gòu)
整個程序由以下幾個核心部分組成:
- 初始化 UI 界面
- 加載 PDF 文件
- 顯示 PDF 頁面
- 頁面切換動畫
以下是代碼的詳細解析。
初始化 UI 界面
代碼段:
self.panel = wx.Panel(self) vbox = wx.BoxSizer(wx.VERTICAL) # 創(chuàng)建工具欄 toolbar = wx.BoxSizer(wx.HORIZONTAL) open_btn = wx.Button(self.panel, label='打開PDF') prev_btn = wx.Button(self.panel, label='上一頁') next_btn = wx.Button(self.panel, label='下一頁') open_btn.Bind(wx.EVT_BUTTON, self.on_open) prev_btn.Bind(wx.EVT_BUTTON, self.on_prev_page) next_btn.Bind(wx.EVT_BUTTON, self.on_next_page) toolbar.Add(open_btn, 0, wx.ALL, 5) toolbar.Add(prev_btn, 0, wx.ALL, 5) toolbar.Add(next_btn, 0, wx.ALL, 5) self.pdf_panel = wx.Panel(self.panel) self.pdf_panel.SetBackgroundColour(wx.WHITE) self.pdf_panel.Bind(wx.EVT_PAINT, self.on_paint) vbox.Add(toolbar, 0, wx.EXPAND) vbox.Add(self.pdf_panel, 1, wx.EXPAND | wx.ALL, 5) self.panel.SetSizer(vbox) self.Centre()
解析:
- 創(chuàng)建主面板
wx.Panel
并使用BoxSizer
布局管理組件。 - 創(chuàng)建工具欄,包括三個按鈕:打開 PDF、上一頁和下一頁。
- 創(chuàng)建 PDF 顯示區(qū)域,綁定
EVT_PAINT
事件用于頁面繪制。 - 使用
Add
方法將工具欄和顯示區(qū)域添加到垂直布局中。
加載 PDF 文件
代碼段:
def on_open(self, event): with wx.FileDialog(self, "選擇PDF文件", wildcard="PDF files (*.pdf)|*.pdf", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return pdf_path = fileDialog.GetPath() self.load_pdf(pdf_path) def load_pdf(self, path): self.doc = fitz.open(path) self.current_page = 0 self.page_images = [] # 預(yù)加載所有頁面 for page in self.doc: pix = page.get_pixmap() img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) self.page_images.append(img) self.render_current_page()
解析:
- 使用
wx.FileDialog
打開文件對話框,選擇 PDF 文件。 - 調(diào)用
fitz.open
加載 PDF 文件,存儲為self.doc
。 - 遍歷 PDF 頁面的每一頁,使用
get_pixmap
提取頁面圖像,并轉(zhuǎn)換為 PIL 圖像對象,存儲到self.page_images
列表中。 - 調(diào)用
render_current_page
渲染第一頁。
顯示 PDF 頁面
代碼段:
def render_current_page(self): if not self.doc or self.current_page >= len(self.page_images): return panel_size = self.pdf_panel.GetSize() # 創(chuàng)建背景 background = Image.new('RGB', (panel_size.width, panel_size.height), 'WHITE') # 獲取當前頁面并調(diào)整大小 current_img = self.page_images[self.current_page].resize( (panel_size.width, panel_size.height), Image.LANCZOS) if self.is_animating: next_img = self.page_images[self.next_page_idx].resize( (panel_size.width, panel_size.height), Image.LANCZOS) if self.animation_direction > 0: # 向右翻頁 background.paste(current_img, (-self.animation_offset, 0)) background.paste(next_img, (panel_size.width - self.animation_offset, 0)) else: # 向左翻頁 background.paste(current_img, (self.animation_offset, 0)) background.paste(next_img, (-panel_size.width + self.animation_offset, 0)) else: background.paste(current_img, (0, 0)) self.current_bitmap = wx.Bitmap.FromBuffer( panel_size.width, panel_size.height, background.tobytes()) self.pdf_panel.Refresh()
解析:
- 檢查當前文檔和頁面索引的有效性。
- 創(chuàng)建一個與顯示區(qū)域大小一致的白色背景。
- 將當前頁面圖像調(diào)整為顯示區(qū)域的大小。
- 如果處于動畫狀態(tài),還需要繪制下一頁面,并根據(jù)動畫方向和偏移量計算粘貼位置。
- 將結(jié)果圖像轉(zhuǎn)換為
wx.Bitmap
,刷新顯示區(qū)域。
頁面切換動畫
代碼段:
def start_animation(self, direction): if self.is_animating: return next_page = self.current_page + direction if next_page < 0 or next_page >= len(self.page_images): return self.is_animating = True self.animation_direction = direction self.next_page_idx = next_page self.animation_offset = 0 self.timer.Start(16) # 約60fps def on_timer(self, event): if not self.is_animating: return panel_width = self.pdf_panel.GetSize().width step = panel_width // 15 self.animation_offset += step if self.animation_offset >= panel_width: self.animation_offset = 0 self.is_animating = False self.current_page = self.next_page_idx self.timer.Stop() self.render_current_page()
解析:
start_animation
初始化動畫參數(shù)并啟動定時器,控制動畫幀率。on_timer
事件處理器更新動畫偏移量,并檢查動畫是否完成。- 動畫完成后,更新當前頁面索引并停止定時器。
運行效果
總結(jié)
這段代碼展示了如何結(jié)合 wxPython 和 PyMuPDF 構(gòu)建一個功能齊全的 PDF 閱讀器。它不僅實現(xiàn)了基本的 PDF 加載和顯示功能,還加入了平滑的頁面切換動畫,提升了用戶體驗。通過合理的模塊化設(shè)計和事件綁定,代碼邏輯清晰,便于擴展。
以上就是基于Python實現(xiàn)PDF動畫翻頁效果的閱讀器的詳細內(nèi)容,更多關(guān)于Python PDF閱讀器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用Python進行中文繁簡轉(zhuǎn)換的實現(xiàn)代碼
這篇文章主要介紹了使用Python進行中文繁簡轉(zhuǎn)換的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10python pip安裝包出現(xiàn):Failed building wheel for xxx錯誤的解決
今天小編就為大家分享一篇python pip安裝包出現(xiàn):Failed building wheel for xxx錯誤的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12Python?Collections庫的高級功能使用示例詳解
Python的collections庫提供了一系列有用的數(shù)據(jù)類型,擴展了內(nèi)建的數(shù)據(jù)類型,為開發(fā)者提供了更多高級功能,本文將深入探討collections庫的一些高級功能,通過詳細的示例代碼演示,幫助大家更好地理解和應(yīng)用這些功能2023-12-12class類在python中獲取金融數(shù)據(jù)的實例方法
在本篇文章里小編給大家整理了關(guān)于class類怎樣在python中獲取金融數(shù)據(jù)的相關(guān)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。2020-12-12利用Python實現(xiàn)Excel的文件間的數(shù)據(jù)匹配功能
這篇文章主要介紹了利用Python實現(xiàn)Excel的文件間的數(shù)據(jù)匹配,本文通過一個函數(shù)實現(xiàn)此功能,通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06使用Pyhton集合set()實現(xiàn)成果查漏的例子
今天小編就為大家分享一篇使用Pyhton集合set()實現(xiàn)成果查漏的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11Python linecache.getline()讀取文件中特定一行的腳本
Python中使用標準庫中的linecache中的getline方法可以從某個文件中讀取出特定的一行。2008-09-09