最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Python實(shí)現(xiàn)PDF動畫翻頁效果的閱讀器

 更新時間:2025年01月08日 08:54:28   作者:winfredzhang  
在這篇博客中,我們將深入分析一個基于 wxPython 實(shí)現(xiàn)的 PDF 閱讀器程序,該程序支持加載 PDF 文件并顯示頁面內(nèi)容,同時支持頁面切換動畫效果,文中有詳細(xì)的代碼示例,需要的朋友可以參考下

主要功能包括:

  • 加載 PDF 文件
  • 顯示當(dāng)前頁面
  • 上一頁/下一頁切換
  • 頁面切換動畫
    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')
        
        # 獲取當(dāng)前頁面并調(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),直接顯示當(dāng)前頁
            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)

整個程序由以下幾個核心部分組成:

  1. 初始化 UI 界面
  2. 加載 PDF 文件
  3. 顯示 PDF 頁面
  4. 頁面切換動畫

以下是代碼的詳細(xì)解析。

初始化 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()

解析:

  1. 創(chuàng)建主面板 wx.Panel 并使用 BoxSizer 布局管理組件。
  2. 創(chuàng)建工具欄,包括三個按鈕:打開 PDF、上一頁和下一頁。
  3. 創(chuàng)建 PDF 顯示區(qū)域,綁定 EVT_PAINT 事件用于頁面繪制。
  4. 使用 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()

解析:

  1. 使用 wx.FileDialog 打開文件對話框,選擇 PDF 文件。
  2. 調(diào)用 fitz.open 加載 PDF 文件,存儲為 self.doc。
  3. 遍歷 PDF 頁面的每一頁,使用 get_pixmap 提取頁面圖像,并轉(zhuǎn)換為 PIL 圖像對象,存儲到 self.page_images 列表中。
  4. 調(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')
    
    # 獲取當(dāng)前頁面并調(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()

解析:

  1. 檢查當(dāng)前文檔和頁面索引的有效性。
  2. 創(chuàng)建一個與顯示區(qū)域大小一致的白色背景。
  3. 將當(dāng)前頁面圖像調(diào)整為顯示區(qū)域的大小。
  4. 如果處于動畫狀態(tài),還需要繪制下一頁面,并根據(jù)動畫方向和偏移量計算粘貼位置。
  5. 將結(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()

解析:

  1. start_animation 初始化動畫參數(shù)并啟動定時器,控制動畫幀率。
  2. on_timer 事件處理器更新動畫偏移量,并檢查動畫是否完成。
  3. 動畫完成后,更新當(dāng)前頁面索引并停止定時器。

運(yùn)行效果

總結(jié)

這段代碼展示了如何結(jié)合 wxPython 和 PyMuPDF 構(gòu)建一個功能齊全的 PDF 閱讀器。它不僅實(shí)現(xiàn)了基本的 PDF 加載和顯示功能,還加入了平滑的頁面切換動畫,提升了用戶體驗(yàn)。通過合理的模塊化設(shè)計和事件綁定,代碼邏輯清晰,便于擴(kuò)展。

以上就是基于Python實(shí)現(xiàn)PDF動畫翻頁效果的閱讀器的詳細(xì)內(nèi)容,更多關(guān)于Python PDF閱讀器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Python中的enumerate函數(shù)示例詳解

    Python中的enumerate函數(shù)示例詳解

    enumerate?是一個非常有用的函數(shù),它允許你在迭代過程中方便地獲取元素及其對應(yīng)的索引,使代碼更簡潔、更Pythonic,這篇文章主要介紹了Python中的enumerate函數(shù)示例詳解,需要的朋友可以參考下
    2023-08-08
  • Python中parsel兩種獲取數(shù)據(jù)方式小結(jié)

    Python中parsel兩種獲取數(shù)據(jù)方式小結(jié)

    本文主要介紹了Python中parsel兩種獲取數(shù)據(jù)方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 從基礎(chǔ)到實(shí)戰(zhàn)詳解Python文件目錄比較的完整指南

    從基礎(chǔ)到實(shí)戰(zhàn)詳解Python文件目錄比較的完整指南

    在日常開發(fā)或系統(tǒng)維護(hù)中,比較兩個文件目錄的差異是常見需求,Python憑借豐富的標(biāo)準(zhǔn)庫和第三方工具,能輕松實(shí)現(xiàn)高效準(zhǔn)確的目錄比較,下面我們就來看看具體的實(shí)現(xiàn)方法吧
    2025-12-12
  • Python格式化輸出詳情

    Python格式化輸出詳情

    這篇文章介紹了Python格式化輸出,主要講解Python格式化輸出的三種方式:%格式化、format格式化、f-String格式化,需要的朋友可以參考下面文章的具體內(nèi)容
    2021-09-09
  • python如何制作縮略圖

    python如何制作縮略圖

    python如何制作縮略圖?這篇文章主要為大家詳細(xì)介紹了python制作縮略圖的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • python交互式圖形編程實(shí)例(二)

    python交互式圖形編程實(shí)例(二)

    這篇文章主要為大家詳細(xì)介紹了python交互式圖形編程實(shí)例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Python 在 VSCode 中使用 IPython Kernel 的方法詳解

    Python 在 VSCode 中使用 IPython Kernel 的方法詳解

    這篇文章主要介紹了Python 在 VSCode 中使用 IPython Kernel 的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • python實(shí)現(xiàn)將代碼轉(zhuǎn)成不可反編譯的pyd文件

    python實(shí)現(xiàn)將代碼轉(zhuǎn)成不可反編譯的pyd文件

    pyc文件用于提高加載速度,部分源碼可讀,而pyd文件提供更好的保密性,是編譯后的二進(jìn)制動態(tài)鏈接庫,當(dāng)有些模塊的代碼需要一定的保密性,這個時候就需要考慮pyc和pyd文件了,本文給大家介紹了python實(shí)現(xiàn)將代碼轉(zhuǎn)成不可反編譯的pyd文件,需要的朋友可以參考下
    2024-11-11
  • Python 通過xpath屬性爬取豆瓣熱映的電影信息

    Python 通過xpath屬性爬取豆瓣熱映的電影信息

    我喜歡看電影,可以說大部分熱門電影我都看過。處理愛好的目的,我看了看豆瓣熱映的電影列表。于是我寫了這個爬蟲把豆瓣熱映的電影都爬了下來。對頁面的處理主要是需要點(diǎn)擊顯示全部電影,然后爬取影片屬性,最后輸出文本。采用的還是scrapy框架。順便聊聊我的實(shí)現(xiàn)過程吧
    2021-11-11
  • Python小波變換去噪的原理解析

    Python小波變換去噪的原理解析

    這篇文章主要介紹了Python小波變換去噪,對于去噪效果好壞的評價,常用信號的信噪比(SNR)與估計信號同原始信號的均方根誤差(RMSE)來判斷,需要的朋友可以參考下
    2021-12-12

最新評論

三明市| 温泉县| 宝坻区| 井陉县| 五原县| 缙云县| 乾安县| 资源县| 道孚县| 郑州市| 上高县| 霍邱县| 庆安县| 崇信县| 龙胜| 牟定县| 甘肃省| 浮梁县| 大城县| 岐山县| 城市| 眉山市| 龙海市| 秦皇岛市| 辽阳县| 家居| 玉林市| 固阳县| 聂荣县| 黄平县| 双鸭山市| 达州市| 康保县| 霞浦县| 宜川县| 婺源县| 边坝县| 行唐县| 福泉市| 苏州市| 齐河县|