基于Python实现PDF动画翻页效果的阅读器

2025-01-08 15:50

本文主要是介绍基于Python实现PDF动画翻页效果的阅读器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载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)
        
        # 创建工具栏
        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):
        # 创建定时器用于动画
        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 = []
        
        # 预加载所有页面
        for page in self.doc:
            pix = page.get_pixmap()
            img = www.chinasem.cnImage.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()
        
        # 创建背景
        background = Image.new('RGB', (panel_size.width, panel_size.height), 'WHITE')
        
        # 获取当前页面并调整大小
        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))
        
        # 转换为wx.Bitmap
        self.current_bitmap = wx.Bitmap.FromBuffer(
            panelhttp://www.chinasem.cn_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  # 调整这个值可以改变动画速度
        
        self.animation_offset += step
        
        # 检查动画是否完成
        if self.animation_offset >= panel_width:
            self.animation_offset = 0
            self.is_animating = False
            self.current_page = selfwww.chinasem.cn.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="基于Python实现PDF动画翻页效果的阅读器")
    frame.Show()
    app.MainLoop()

if __name__ == '__main__':
    main()

代码结构

整个程序由以下几个核心部分组成:

  1. 初始化 UI 界面
  2. 加载 PDF 文件
  3. 显示 PDF 页面
  4. 页面切换动画

以下是代码的详细解析。

初始化 UI 界面

代码段:

self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)

# 创建工具栏
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. 创建主面板 wx.Panel 并使用 BoxSizer 布局管理组件。
  2. 创建工具栏,包括三个按钮:打开 PDF、上一页和下一页。
  3. 创建 PDF 显示区域,绑定 EVT_PAINT 事件用于页面绘制。
  4. 使用 Add 方法将工具栏和显示区域添加到垂直布局中。

加载 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 = []
    
    # 预加载所有页面
    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. 调用 fitz.open 加载 PDF 文件,存储为 self.doc
  3. 遍历 PDF 页面的每一页,使用 get_pixmap 提取页面图像,并转换为 PIL 图像对象,存储到 self.page_images 列表中。
  4. 调用 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()
    
    # 创建背景
    backgr编程ound = Image.new('RGB', (panel_size.width, panel_size.height), 'WHITE')
    
    # 获取当前页面并调整大小
    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. 检查当前文档和页面索引的有效性。
  2. 创建一个与显示区域大小一致的白色背景。
  3. 将当前页面图像调整为显示区域的大小。
  4. 如果处于动画状态,还需要绘制下一页面,并根据动画方向和偏移量计算粘贴位置。
  5. 将结果图像转换为 wx.Bitmap,刷新显示区域。

页面切换动画

代码段:

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 = selphpf.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 初始化动画参数并启动定时器,控制动画帧率。
  2. on_timer 事件处理器更新动画偏移量,并检查动画是否完成。
  3. 动画完成后,更新当前页面索引并停止定时器。

运行效果

基于Python实现PDF动画翻页效果的阅读器

总结

这段代码展示了如何结合 wxPython 和 PyMuPDF 构建一个功能齐全的 PDF 阅读器。它不仅实现了基本的 PDF 加载和显示功能,还加入了平滑的页面切换动画,提升了用户体验。通过合理的模块化设计和事件绑定,代码逻辑清晰,便于扩展。

以上就是基于Python实现PDF动画翻页效果的阅读器的详细内容,更多关于Python PDF阅读器的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于基于Python实现PDF动画翻页效果的阅读器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1152985

相关文章

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.

python修改字符串值的三种方法

《python修改字符串值的三种方法》本文主要介绍了python修改字符串值的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录第一种方法:第二种方法:第三种方法:在python中,字符串对象是不可变类型,所以我们没办法直接

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

spring6+JDK17实现SSM起步配置文件

《spring6+JDK17实现SSM起步配置文件》本文介绍了使用Spring6和JDK17配置SSM(Spring+SpringMVC+MyBatis)框架,文中通过示例代码介绍的非常详细,对大家的... 目录1.配置POM文件2.在resource目录下新建beans.XML文件,用于配置spirng3

python写个唤醒睡眠电脑的脚本

《python写个唤醒睡眠电脑的脚本》这篇文章主要为大家详细介绍了如何使用python写个唤醒睡眠电脑的脚本,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 环境:win10python3.12问题描述:怎么用python写个唤醒睡眠电脑的脚本?解决方案:1.唤醒处于睡眠状

Python项目打包部署到服务器的实现

《Python项目打包部署到服务器的实现》本文主要介绍了PyCharm和Ubuntu服务器部署Python项目,包括打包、上传、安装和设置自启动服务的步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录一、准备工作二、项目打包三、部署到服务器四、设置服务自启动一、准备工作开发环境:本文以PyChar

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是