在 PyQt5 界面中显示 PDF

在 PyQt5 界面中显示 PDF

在 PyQt5 界面中显示 PDF(使用 PyMuPDF)

以下是将 PyMuPDF 渲染的 PDF 页面显示在 PyQt5 界面中的完整实现方案:

1. 基础实现:使用 QLabel 显示 PDF 页面

import fitz
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QScrollArea, QVBoxLayout, QWidget
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import Qt

class PdfViewer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PDF 查看器 (PyMuPDF)")
        self.setGeometry(100, 100, 800, 600)
        
        # 主部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        # 创建滚动区域
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        
        # 用于显示PDF的标签
        self.pdf_label = QLabel()
        self.pdf_label.setAlignment(Qt.AlignCenter)
        self.scroll_area.setWidget(self.pdf_label)
        
        layout.addWidget(self.scroll_area)
        
        # 加载PDF
        self.load_pdf("example.pdf")
    
    def load_pdf(self, file_path):
        # 打开PDF文档
        self.doc = fitz.open(file_path)
        self.current_page = 0
        
        # 显示第一页
        self.show_page(self.current_page)
    
    def show_page(self, page_num):
        # 加载指定页面
        page = self.doc.load_page(page_num)
        
        # 渲染为图像 (300 DPI高质量)
        zoom = 3.0  # 缩放因子 (3.0 = 300 DPI)
        mat = fitz.Matrix(zoom, zoom)
        pix = page.get_pixmap(matrix=mat, alpha=False)
        
        # 转换为Qt可用的图像格式
        img = QImage(
            pix.samples, 
            pix.width, 
            pix.height, 
            pix.stride, 
            QImage.Format_RGB888
        )
        
        # 显示在QLabel上
        self.pdf_label.setPixmap(QPixmap.fromImage(img))
        self.setWindowTitle(f"PDF 查看器 - 第 {page_num+1}/{self.doc.page_count} 页")

if __name__ == "__main__":
    app = QApplication([])
    viewer = PdfViewer()
    viewer.show()
    app.exec_()

2. 增强版:添加导航工具栏

from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QScrollArea, 
                            QVBoxLayout, QWidget, QToolBar, QAction, 
                            QSpinBox, Q***boBox)

class EnhancedPdfViewer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setup_ui()
        self.load_pdf("example.pdf")
    
    def setup_ui(self):
        self.setWindowTitle("PDF 查看器 (增强版)")
        self.setGeometry(100, 100, 1000, 800)
        
        # 主部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        # 创建工具栏
        self.setup_toolbar()
        
        # 创建滚动区域
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        
        # PDF显示标签
        self.pdf_label = QLabel()
        self.pdf_label.setAlignment(Qt.AlignCenter)
        self.scroll_area.setWidget(self.pdf_label)
        
        layout.addWidget(self.scroll_area)
    
    def setup_toolbar(self):
        toolbar = QToolBar("PDF 控制")
        self.addToolBar(toolbar)
        
        # 上一页/下一页
        prev_action = QAction("上一页", self)
        prev_action.triggered.connect(self.prev_page)
        toolbar.addAction(prev_action)
        
        next_action = QAction("下一页", self)
        next_action.triggered.connect(self.next_page)
        toolbar.addAction(next_action)
        
        # 页面跳转
        toolbar.addSeparator()
        self.page_spin = QSpinBox()
        self.page_spin.setMinimum(1)
        self.page_spin.valueChanged.connect(self.go_to_page)
        toolbar.addWidget(self.page_spin)
        
        # 缩放控制
        toolbar.addSeparator()
        self.zoom_***bo = Q***boBox()
        self.zoom_***bo.addItems(["50%", "75%", "100%", "150%", "200%", "300%"])
        self.zoom_***bo.setCurrentText("100%")
        self.zoom_***bo.currentTextChanged.connect(self.apply_zoom)
        toolbar.addWidget(self.zoom_***bo)
        
        # 旋转按钮
        toolbar.addSeparator()
        rotate_left = QAction("左转90°", self)
        rotate_left.triggered.connect(lambda: self.rotate_page(-90))
        toolbar.addAction(rotate_left)
        
        rotate_right = QAction("右转90°", self)
        rotate_right.triggered.connect(lambda: self.rotate_page(90))
        toolbar.addAction(rotate_right)
    
    def load_pdf(self, file_path):
        self.doc = fitz.open(file_path)
        self.current_page = 0
        self.zoom_factor = 1.0
        self.rotation = 0
        
        # 初始化页面选择器
        self.page_spin.setMaximum(self.doc.page_count)
        self.page_spin.setValue(1)
        
        self.show_page(self.current_page)
    
    def show_page(self, page_num):
        if not 0 <= page_num < self.doc.page_count:
            return
            
        self.current_page = page_num
        page = self.doc.load_page(page_num)
        
        # 应用缩放和旋转
        mat = fitz.Matrix(self.zoom_factor, self.zoom_factor)
        mat.prerotate(self.rotation)
        
        pix = page.get_pixmap(matrix=mat, alpha=False)
        
        # 转换为Qt图像
        img = QImage(
            pix.samples, 
            pix.width, 
            pix.height, 
            pix.stride, 
            QImage.Format_RGB888
        )
        
        self.pdf_label.setPixmap(QPixmap.fromImage(img))
        self.page_spin.setValue(page_num + 1)
        self.setWindowTitle(f"PDF 查看器 - 第 {page_num+1}/{self.doc.page_count} 页")
    
    def prev_page(self):
        if self.current_page > 0:
            self.show_page(self.current_page - 1)
    
    def next_page(self):
        if self.current_page < self.doc.page_count - 1:
            self.show_page(self.current_page + 1)
    
    def go_to_page(self, page_num):
        self.show_page(page_num - 1)
    
    def apply_zoom(self, zoom_text):
        zoom_factor = float(zoom_text.strip("%")) / 100.0
        self.zoom_factor = zoom_factor
        self.show_page(self.current_page)
    
    def rotate_page(self, angle):
        self.rotation = (self.rotation + angle) % 360
        self.show_page(self.current_page)

if __name__ == "__main__":
    app = QApplication([])
    viewer = EnhancedPdfViewer()
    viewer.show()
    app.exec_()

3. 高级功能扩展

添加文本搜索功能
# 在EnhancedPdfViewer类中添加
def setup_toolbar(self):
    # ... 其他工具栏代码 ...
    
    # 添加搜索功能
    toolbar.addSeparator()
    self.search_input = QLineEdit()
    self.search_input.setPlaceholderText("搜索文本...")
    self.search_input.returnPressed.connect(self.search_text)
    toolbar.addWidget(self.search_input)
    
    search_action = QAction("搜索", self)
    search_action.triggered.connect(self.search_text)
    toolbar.addAction(search_action)

def search_text(self):
    search_term = self.search_input.text()
    if not search_term:
        return
    
    # 从当前页开始搜索
    for page_num in range(self.current_page, self.doc.page_count):
        page = self.doc.load_page(page_num)
        text_instances = page.search_for(search_term)
        
        if text_instances:
            self.show_page(page_num)
            # 高亮显示搜索结果(需要实现高亮逻辑)
            self.highlight_search_results(text_instances)
            break

def highlight_search_results(self, rect_list):
    # 创建透明的高亮层
    pixmap = self.pdf_label.pixmap().copy()
    painter = QPainter(pixmap)
    painter.setPen(QPen(Qt.red, 2))
    
    for rect in rect_list:
        # 将PDF坐标转换为屏幕坐标
        x, y = rect.x0 * self.zoom_factor, rect.y0 * self.zoom_factor
        width = (rect.x1 - rect.x0) * self.zoom_factor
        height = (rect.y1 - rect.y0) * self.zoom_factor
        
        # 绘制高亮矩形
        painter.drawRect(int(x), int(y), int(width), int(height))
    
    painter.end()
    self.pdf_label.setPixmap(pixmap)

4. 性能优化建议

  1. 页面缓存
from functools import lru_cache

class PdfViewer:
    def __init__(self):
        self.page_cache = {}
    
    @lru_cache(maxsize=10)
    def get_page_image(self, page_num, zoom_factor, rotation):
        # 实现带缓存的页面渲染
        pass
  1. 后台渲染
from PyQt5.QtCore import QThread, pyqtSignal

class RenderThread(QThread):
    finished = pyqtSignal(QImage)
    
    def __init__(self, doc, page_num, zoom, rotation):
        super().__init__()
        self.doc = doc
        self.page_num = page_num
        self.zoom = zoom
        self.rotation = rotation
    
    def run(self):
        page = self.doc.load_page(self.page_num)
        mat = fitz.Matrix(self.zoom, self.zoom)
        mat.prerotate(self.rotation)
        pix = page.get_pixmap(matrix=mat, alpha=False)
        
        img = QImage(
            pix.samples, 
            pix.width, 
            pix.height, 
            pix.stride, 
            QImage.Format_RGB888
        )
        self.finished.emit(img)

5. 替代方案比较

方案 优点 缺点
PyMuPDF + QLabel 高性能,功能丰富,支持高级PDF操作 需要自己实现界面交互
QWebEngineView 内置支持,显示效果好 功能有限,占用内存大
python-poppler-qt5 原生Qt集成 安装复杂,维护不佳

对于大多数应用,PyMuPDF方案是最佳选择,它提供了最佳的性能和灵活性平衡。

转载请说明出处内容投诉
CSS教程网 » 在 PyQt5 界面中显示 PDF

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买