当前位置: 首页 > news >正文

使用 PyMuPDF 和 PySide6/PyQt6 编写的 PDF 查看器 (显示树状书签和缩略图列表,没有文字选择功能)

成品展示: 

 

完整代码:

import sys
import fitz  # PyMuPDF
from PySide6.QtWidgets import (QApplication,QMainWindow, QLabel, QScrollArea,QToolBar, QStatusBar, QFileDialog, QComboBox, QDockWidget,QListWidget, QListWidgetItem, QVBoxLayout, QWidget, QProgressBar, QTreeWidget, QTreeWidgetItem, QTreeView,QLineEdit, QPushButton, QCheckBox, QHBoxLayout, QMessageBox, QDialog
)
from PySide6.QtGui import (QPixmap, QImage, QKeySequence, QIcon, QAction, QPageSize, QPen, QBrush, QColor, QPainter
)
from PySide6.QtCore import (Qt, QSize, QThread, Signal, QRectF, QTimer
)
from PySide6.QtPrintSupport import QPrinter, QPrintDialog
pyqtSignal = Signalclass ThumbnailLoader(QThread):"""后台线程用于加载缩略图"""thumbnail_loaded = pyqtSignal(int, QPixmap)progress_updated = pyqtSignal(int)  # 新增进度信号def __init__(self, doc, zoom_factor=0.2):super().__init__()self.doc = docself.zoom_factor = zoom_factorself.running = Truedef run(self):if not self.doc:returntotal_pages = len(self.doc)for page_num in range(len(self.doc)):if not self.running:breakpage = self.doc.load_page(page_num)zoom_matrix = fitz.Matrix(self.zoom_factor, self.zoom_factor)pix = page.get_pixmap(matrix=zoom_matrix)image_format = QImage.Format.Format_RGB888if pix.alpha:image_format = QImage.Format.Format_RGBA8888qimage = QImage(pix.samples,pix.width,pix.height,pix.stride,image_format)pixmap = QPixmap.fromImage(qimage)self.thumbnail_loaded.emit(page_num, pixmap)progress = int((page_num + 1) / total_pages * 100)self.progress_updated.emit(progress)def stop(self):self.running = Falseself.wait()class AboutDialog(QDialog):def __init__(self, parent):super().__init__(parent)self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowCloseButtonHint)self.setWindowTitle("关于 PDF 查看器")self.titleButton = QLabel("基于 Python, PyMuPDF 和 PySide6 的 PDF 查看器 Demo,\n""         由 DeepSeek 和 AOUS220623 共同开发。",self)self.titleButton.move(120, 100)class PDFViewer(QMainWindow):def __init__(self):super().__init__()self.search_pixmap = Noneself.windowTitleBase = "PDF 查看器"self.setWindowTitle(self.windowTitleBase)self.setWindowIcon(QIcon("resources/icon.ico"))self.setGeometry(100, 100, 1000, 700)# 初始化状态self.file_opened = False# 关于对话框self.about_dialog = AboutDialog(self)self.about_dialog.setFixedSize(600, 400)# 菜单self.file_menu = self.menuBar().addMenu("文件")self.file_menu_open_action = QAction("打开文件", self)self.file_menu_open_action.triggered.connect(self.open_file)self.file_menu_open_action.setShortcut("Ctrl+Shift+O")self.file_menu.addAction(self.file_menu_open_action)self.file_menu_close_action = QAction("关闭文件", self)self.file_menu_close_action.triggered.connect(self.close_file)self.file_menu_close_action.setShortcut("Ctrl+Shift+C")self.file_menu.addAction(self.file_menu_close_action)self.view_menu = self.menuBar().addMenu("视图")self.help_menu = self.menuBar().addMenu("帮助")self.about_action = QAction("关于", self)self.about_action.triggered.connect(self.about_dialog.exec)self.help_menu.addAction(self.about_action)# PDF 文档相关变量self.doc = Noneself.current_page = 0self.zoom_factor = 1.0self.total_pages = 0self.thumbnail_loader = None# 创建界面self.init_ui()self.setAcceptDrops(True)# 创建进度条self.progress_bar = QProgressBar()self.progress_bar.setMaximum(100)self.progress_bar.setVisible(False)self.status_bar.addPermanentWidget(self.progress_bar)# 书签变量self.bookmark_dock = Noneself.bookmark_tree = Noneself.create_bookmark_panel()# 搜索相关变量self.search_results = []self.current_search_index = -1self.search_dock = Noneself.create_search_panel()self.timer = QTimer(self)self.timer.timeout.connect(self.update_fileopen_state)self.timer.start(0.2)# 更新工具栏状态self.update_fileopen_state()def close_file(self):self.thumbnail_list.clear()self.bookmark_tree.clear()width, height = self.search_dock.width(), self.search_dock.height()if self.file_opened:self.progress_bar.setVisible(False)self.file_opened = Falseself.scroll_area.close()self.scroll_area.deleteLater()self.scroll_area = QScrollArea()self.scroll_area.setWidgetResizable(True)self.setCentralWidget(self.scroll_area)self.pdf_label = QLabel()self.pdf_label.setAlignment(Qt.AlignmentFlag.AlignCenter)self.scroll_area.setWidget(self.pdf_label)self.setWindowTitle(self.windowTitleBase)self.update_fileopen_state()self.search_dock.resize(width, height)if self.thumbnail_loader:self.thumbnail_loader.stop()try:if self.doc:self.doc.close()except Exception as e:...def create_search_panel(self):"""创建搜索面板"""self.search_dock = QDockWidget("搜索", self)self.search_dock.setAllowedAreas(Qt.DockWidgetArea.BottomDockWidgetArea | Qt.DockWidgetArea.TopDockWidgetArea)# 搜索输入框和按钮self.search_input = QLineEdit()self.search_input.setPlaceholderText("输入搜索内容")self.search_input.returnPressed.connect(self.do_search)self.search_button = QPushButton("搜索")self.search_button.clicked.connect(self.do_search)self.search_results_list = QListWidget()self.search_results_list.itemClicked.connect(self.go_to_search_result)# 搜索选项self.case_sensitive_check = QCheckBox("区分大小写")self.whole_word_check = QCheckBox("全词匹配")# 布局search_options = QHBoxLayout()search_options.addWidget(self.case_sensitive_check)search_options.addWidget(self.whole_word_check)search_bar = QHBoxLayout()search_bar.addWidget(self.search_input)search_bar.addWidget(self.search_button)container = QWidget()layout = QVBoxLayout()layout.addLayout(search_bar)layout.addLayout(search_options)layout.addWidget(self.search_results_list)container.setLayout(layout)self.search_dock.setWidget(container)self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.search_dock)self.search_dock.hide()self.search_dock.closeEvent = self.search_box_close_eventself.search_dock.showEvent = self.search_box_show_event# 添加显示/隐藏搜索面板的动作toggle_search_action = self.search_dock.toggleViewAction()toggle_search_action.setText("搜索面板")toggle_search_action.setShortcut("Ctrl+F")self.view_menu.addAction(toggle_search_action)def do_search(self):"""执行搜索"""if not self.doc:returnsearch_text = self.search_input.text().strip()if not search_text:returnself.search_results = []self.current_search_index = -1self.search_results_list.clear()# 设置搜索选项flags = 0if self.case_sensitive_check.isChecked():flags |= fitz.TEXT_PRESERVE_LIGATURES | fitz.TEXT_PRESERVE_WHITESPACEif self.whole_word_check.isChecked():flags |= fitz.TEXT_PRESERVE_LIGATURES | fitz.TEXT_PRESERVE_WHITESPACE# 在所有页面中搜索for page_num in range(len(self.doc)):page = self.doc.load_page(page_num)text_instances = page.search_for(search_text, flags=flags)for rect in text_instances:self.search_results.append((page_num, rect))self.search_results_list.addItem(f"第 {page_num + 1} 页 - 位置 ({int(rect.x0)}, {int(rect.y0)})")if self.search_results:self.search_results_list.setCurrentRow(0)self.go_to_search_result(self.search_results_list.currentItem())else:QMessageBox.information(self, "搜索", "未找到匹配结果")def go_to_search_result(self, item):"""跳转到搜索结果"""if not item or not self.search_results:returnindex = self.search_results_list.row(item)if 0 <= index < len(self.search_results):self.current_search_index = indexpage_num, rect = self.search_results[index]# 跳转到页面if page_num != self.current_page:self.current_page = page_numself.display_page()# 高亮显示搜索结果self.highlight_search_result(rect)def highlight_search_result(self, rect):"""高亮显示搜索结果"""if not self.doc:return# 获取当前页面的缩放矩阵zoom_matrix = fitz.Matrix(self.zoom_factor, self.zoom_factor)# 渲染页面和搜索结果高亮page = self.doc.load_page(self.current_page)pix = page.get_pixmap(matrix=zoom_matrix)# 创建QImageimage_format = QImage.Format.Format_RGB888if pix.alpha:image_format = QImage.Format.Format_RGBA8888qimage = QImage(pix.samples,pix.width,pix.height,pix.stride,image_format)# 转换为QPixmapself.search_pixmap = QPixmap.fromImage(qimage)# 绘制高亮矩形painter = QPainter(self.search_pixmap)painter.setPen(QPen(Qt.GlobalColor.red, 2))painter.setBrush(QBrush(QColor(255, 255, 0, 100)))# 转换坐标到显示尺寸display_rect = QRectF(rect.x0 * self.zoom_factor,rect.y0 * self.zoom_factor,(rect.x1 - rect.x0) * self.zoom_factor,(rect.y1 - rect.y0) * self.zoom_factor)painter.drawRect(display_rect)painter.end()self.pdf_label.setPixmap(self.search_pixmap)self.update_status_bar()def search_box_close_event(self, e):self.cancel_search()def search_box_show_event(self, e):self.toolbar_.addAction(self.prev_search_action)self.toolbar_.addAction(self.next_search_action)def cancel_search(self):"""取消当前搜索并清除高亮"""self.search_results = []self.current_search_index = -1self.search_results_list.clear()self.search_input.clear()self.toolbar_.removeAction(self.next_search_action)self.toolbar_.removeAction(self.prev_search_action)# 重新渲染当前页面以清除高亮if self.file_opened:if self.doc and 0 <= self.current_page < self.total_pages:self.display_page()def create_bookmark_panel(self):"""创建书签面板"""self.bookmark_dock = QDockWidget("书签", self)self.bookmark_dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea)# 创建树状列表self.bookmark_tree = QTreeWidget()self.bookmark_tree.setHeaderHidden(True)self.bookmark_tree.itemClicked.connect(self.bookmark_item_clicked)# 设置布局container = QWidget()layout = QVBoxLayout()layout.addWidget(self.bookmark_tree)layout.setContentsMargins(0, 0, 0, 0)container.setLayout(layout)self.bookmark_dock.setWidget(container)self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.bookmark_dock)# 添加显示/隐藏书签面板的动作toggle_bookmark_action = self.bookmark_dock.toggleViewAction()toggle_bookmark_action.setText("书签面板")toggle_bookmark_action.setShortcut("Ctrl+B")self.view_menu.addAction(toggle_bookmark_action)def bookmark_item_clicked(self, item):"""书签项点击事件"""page_num = item.data(0, Qt.ItemDataRole.UserRole)if page_num != self.current_page:self.current_page = page_numself.display_page()self.update_status_bar()def update_bookmarks(self):"""更新书签树"""if not self.doc:returnself.bookmark_tree.clear()# 获取PDF书签toc = self.doc.get_toc()if not toc:return# 创建父节点字典用于处理层级关系parent_nodes = {0: self.bookmark_tree}for item in toc:level, title, page = itempage_num = max(0, page - 1)  # PDF页码从1开始,我们使用0-based# 创建树节点node = QTreeWidgetItem([title])node.setData(0, Qt.ItemDataRole.UserRole, page_num)# 添加到父节点parent_level = level - 1if parent_level in parent_nodes:if parent_level == 0:parent_nodes[parent_level].addTopLevelItem(node)else:parent_nodes[parent_level].addChild(node)parent_nodes[level] = node# 展开第一级书签for i in range(self.bookmark_tree.topLevelItemCount()):self.bookmark_tree.topLevelItem(i).setExpanded(True)def init_ui(self):# 创建中央部件和滚动区域self.scroll_area = QScrollArea()self.scroll_area.setWidgetResizable(True)self.setCentralWidget(self.scroll_area)# 用于显示 PDF 的标签self.pdf_label = QLabel()self.pdf_label.setAlignment(Qt.AlignmentFlag.AlignCenter)self.scroll_area.setWidget(self.pdf_label)# 创建缩略图面板self.create_thumbnail_panel()# 创建工具栏self.create_toolbar()# 创建状态栏self.status_bar = QStatusBar()self.setStatusBar(self.status_bar)self.update_status_bar()def create_thumbnail_panel(self):"""创建缩略图面板"""self.thumbnail_dock = QDockWidget("缩略图", self)self.thumbnail_dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea)# 创建缩略图列表self.thumbnail_list = QListWidget()self.thumbnail_list.setViewMode(QListWidget.ViewMode.IconMode)self.thumbnail_list.setIconSize(QSize(100, 100))self.thumbnail_list.setResizeMode(QListWidget.ResizeMode.Adjust)self.thumbnail_list.setSpacing(5)self.thumbnail_list.itemClicked.connect(self.thumbnail_clicked)# 设置布局container = QWidget()layout = QVBoxLayout()layout.addWidget(self.thumbnail_list)layout.setContentsMargins(0, 0, 0, 0)container.setLayout(layout)self.thumbnail_dock.setWidget(container)self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.thumbnail_dock)# 添加显示/隐藏缩略图面板的动作toggle_thumbnail_action = self.thumbnail_dock.toggleViewAction()toggle_thumbnail_action.setText("缩略图面板")toggle_thumbnail_action.setShortcut("Ctrl+T")self.view_menu.addAction(toggle_thumbnail_action)def create_toolbar(self):# 文件操作self.toolbar_ = QToolBar("Main Toolbar")self.toolbar_.setIconSize(QSize(16, 16))self.addToolBar(self.toolbar_)# 打开文件操作self.toolbar_open_action = QAction(QIcon.fromTheme("document-open"), "打开 PDF 文件", self)self.toolbar_open_action.triggered.connect(self.open_file)self.toolbar_open_action.setShortcut(QKeySequence.StandardKey.Open)self.toolbar_.addAction(self.toolbar_open_action)# 导航操作self.toolbar_.addSeparator()self.toolbar_prev_action = QAction(QIcon.fromTheme("go-previous"), "上一页", self)self.toolbar_prev_action.triggered.connect(self.prev_page)self.toolbar_prev_action.setShortcut(Qt.Key.Key_Left)self.toolbar_.addAction(self.toolbar_prev_action)self.toolbar_next_action = QAction(QIcon.fromTheme("go-next"), "下一页", self)self.toolbar_next_action.triggered.connect(self.next_page)self.toolbar_next_action.setShortcut(Qt.Key.Key_Right)self.toolbar_.addAction(self.toolbar_next_action)# 缩放操作self.toolbar_.addSeparator()self.toolbar_zoom_out_action = QAction(QIcon.fromTheme("zoom-out"), "缩小", self)self.toolbar_zoom_out_action.triggered.connect(lambda: self.adjust_zoom(0.8))self.toolbar_zoom_out_action.setShortcut(QKeySequence.StandardKey.ZoomOut)self.toolbar_.addAction(self.toolbar_zoom_out_action)self.zoom_combo = QComboBox()self.zoom_combo.addItems(["50%", "75%", "100%", "125%", "150%", "200%"])self.zoom_combo.setCurrentText("100%")self.zoom_combo.currentIndexChanged.connect(self.zoom_changed)self.toolbar_.addWidget(self.zoom_combo)self.toolbar_zoom_in_action = QAction(QIcon.fromTheme("zoom-in"), "放大", self)self.toolbar_zoom_in_action.triggered.connect(lambda: self.adjust_zoom(1.25))self.toolbar_zoom_in_action.setShortcut(QKeySequence.StandardKey.ZoomIn)self.toolbar_.addAction(self.toolbar_zoom_in_action)# 重置缩放self.toolbar_.addSeparator()self.toolbar_reset_zoom_action = QAction("重置缩放比例", self)self.toolbar_reset_zoom_action.triggered.connect(self.reset_zoom)self.toolbar_.addAction(self.toolbar_reset_zoom_action)self.toolbar_.addSeparator()self.toolbar_print_action = QAction(QIcon.fromTheme("document-print"), "打印", self)self.toolbar_print_action.triggered.connect(self.print_document)self.toolbar_print_action.setShortcut(QKeySequence.StandardKey.Print)self.toolbar_.addAction(self.toolbar_print_action)self.toolbar_.addSeparator()self.prev_search_action = QAction("上一个结果", self)self.prev_search_action.triggered.connect(self.prev_search_result)self.prev_search_action.setShortcut("Shift+F3")self.next_search_action = QAction("下一个结果", self)self.next_search_action.triggered.connect(self.next_search_result)self.next_search_action.setShortcut("F3")def prev_search_result(self):"""跳转到上一个搜索结果"""if not self.search_results:returnnew_index = (self.current_search_index - 1) % len(self.search_results)self.search_results_list.setCurrentRow(new_index)self.go_to_search_result(self.search_results_list.currentItem())def next_search_result(self):"""跳转到下一个搜索结果"""if not self.search_results:returnnew_index = (self.current_search_index + 1) % len(self.search_results)self.search_results_list.setCurrentRow(new_index)self.go_to_search_result(self.search_results_list.currentItem())def load_thumbnails(self):"""启动后台线程加载缩略图"""if not self.doc:returnif self.thumbnail_loader:self.thumbnail_loader.stop()self.thumbnail_loader = ThumbnailLoader(self.doc)self.thumbnail_loader.thumbnail_loaded.connect(self.add_thumbnail)self.thumbnail_loader.progress_updated.connect(self.update_progress)  # 连接进度信号self.progress_bar.setVisible(True)  # 显示进度条self.thumbnail_loader.start()def update_progress(self, value):"""更新进度条"""self.progress_bar.setValue(value)if value >= 100:self.progress_bar.setVisible(False)  # 完成后隐藏def add_thumbnail(self, page_num, pixmap):"""添加单个缩略图到列表"""item = QListWidgetItem()item.setIcon(QIcon(pixmap))item.setText(f"第 {page_num + 1} 页")item.setData(Qt.ItemDataRole.UserRole, page_num)self.thumbnail_list.addItem(item)# 如果是当前页,高亮显示if page_num == self.current_page:self.thumbnail_list.setCurrentItem(item)def thumbnail_clicked(self, item):"""缩略图点击事件"""page_num = item.data(Qt.ItemDataRole.UserRole)if page_num != self.current_page:self.current_page = page_numself.display_page()self.update_status_bar()def display_page(self):if self.doc is None or self.total_pages == 0:return# 获取当前页面page = self.doc.load_page(self.current_page)# 计算缩放后的尺寸zoom_matrix = fitz.Matrix(self.zoom_factor, self.zoom_factor)# 渲染页面为像素图pix = page.get_pixmap(matrix=zoom_matrix)# 转换为 QImageimage_format = QImage.Format.Format_RGB888if pix.alpha:image_format = QImage.Format.Format_RGBA8888qimage = QImage(pix.samples,pix.width,pix.height,pix.stride,image_format)# 转换为 QPixmap 并显示pixmap = QPixmap.fromImage(qimage)self.pdf_label.setPixmap(pixmap)# 更新缩略图选择self.update_thumbnail_selection()self.highlight_current_bookmark()if 0 <= self.current_search_index < len(self.search_results):page_num, rect = self.search_results[self.current_search_index]if page_num == self.current_page:self.highlight_search_result(rect)def highlight_current_bookmark(self):"""高亮当前页面对应的书签"""if not self.bookmark_tree:return# 清除之前的选择self.bookmark_tree.clearSelection()# 递归查找匹配的书签项def find_bookmark_item(item):if item.data(0, Qt.ItemDataRole.UserRole) == self.current_page:return itemfor i in range(item.childCount()):found = find_bookmark_item(item.child(i))if found:return foundreturn None# 在所有顶级项中查找for i in range(self.bookmark_tree.topLevelItemCount()):item = find_bookmark_item(self.bookmark_tree.topLevelItem(i))if item:item.setSelected(True)self.bookmark_tree.scrollToItem(item)breakdef update_thumbnail_selection(self):"""更新缩略图列表中的当前页选择"""for i in range(self.thumbnail_list.count()):item = self.thumbnail_list.item(i)if item.data(Qt.ItemDataRole.UserRole) == self.current_page:self.thumbnail_list.setCurrentItem(item)self.thumbnail_list.scrollToItem(item)breakdef update_status_bar(self):if self.doc:self.status_bar.showMessage(f"第 {self.current_page + 1} 页,共 {self.total_pages} 页 | "f"缩放: {int(self.zoom_factor * 100)}%")else:self.status_bar.showMessage("就绪")def prev_page(self):if self.doc and self.current_page > 0:self.current_page -= 1self.display_page()self.update_status_bar()def next_page(self):if self.doc and self.current_page < self.total_pages - 1:self.current_page += 1self.display_page()self.update_status_bar()def adjust_zoom(self, factor):self.zoom_factor *= factorself.zoom_combo.setCurrentText(f"{int(self.zoom_factor * 100)}%")self.display_page()self.update_status_bar()def reset_zoom(self):self.zoom_factor = 1.0self.zoom_combo.setCurrentText("100%")self.display_page()self.update_status_bar()def zoom_changed(self):text = self.zoom_combo.currentText()try:# 移除百分号并转换为浮点数self.zoom_factor = float(text.replace("%", "")) / 100.0self.display_page()self.update_status_bar()except ValueError:passdef closeEvent(self, event):"""窗口关闭时清理资源"""self.progress_bar.setVisible(False)if self.thumbnail_loader:self.thumbnail_loader.stop()try:if self.doc:self.doc.close()except Exception as e:...event.accept()# 添加以下三个方法来实现拖放功能def dragEnterEvent(self, event):if event.mimeData().hasUrls():urls = event.mimeData().urls()if urls and urls[0].toString().lower().endswith('.pdf'):event.acceptProposedAction()def dragMoveEvent(self, event):if event.mimeData().hasUrls():urls = event.mimeData().urls()if urls and urls[0].toString().lower().endswith('.pdf'):event.acceptProposedAction()def dropEvent(self, event):urls = event.mimeData().urls()if urls and urls[0].toString().lower().endswith('.pdf'):file_path = urls[0].toLocalFile()self.open_dropped_file(file_path)event.acceptProposedAction()def keyPressEvent(self, event):"""处理键盘事件"""if event.key() == Qt.Key.Key_Escape:self.search_dock.hide()self.cancel_search()else:super().keyPressEvent(event)def open_file(self):file_path, _ = QFileDialog.getOpenFileName(self, "打开 PDF 文件", "", "PDF 文件 (*.pdf)")if file_path:try:# 关闭当前文档(如果存在)try:if self.doc:self.doc.close()if self.thumbnail_loader:self.thumbnail_loader.stop()except Exception as e:...# 打开新文档self.doc = fitz.open(file_path)self.total_pages = len(self.doc)self.current_page = 0self.zoom_factor = 1.0self.zoom_combo.setCurrentText("100%")# 更新书签self.update_bookmarks()# 清空缩略图列表self.thumbnail_list.clear()# 加载缩略图self.progress_bar.setVisible(True)self.load_thumbnails()# 显示第一页self.display_page()self.update_status_bar()self.setWindowTitle(f"{self.windowTitleBase} - {file_path}")# 设置文件打开状态self.file_opened = Trueself.update_fileopen_state()except Exception as e:print(f"Error opening PDF: {e}")def open_dropped_file(self, file_path):"""处理拖放打开的文件"""try:# 关闭当前文档(如果存在)try:if self.doc:self.doc.close()if self.thumbnail_loader:self.thumbnail_loader.stop()except Exception as e:...# 打开新文档self.doc = fitz.open(file_path)self.total_pages = len(self.doc)self.current_page = 0self.zoom_factor = 1.0self.zoom_combo.setCurrentText("100%")# 更新书签self.update_bookmarks()# 清空缩略图列表self.thumbnail_list.clear()# 加载缩略图self.progress_bar.setVisible(True)self.load_thumbnails()# 显示第一页self.display_page()self.update_status_bar()# 更新窗口标题显示当前文件名self.setWindowTitle(f"{self.windowTitleBase} - {file_path}")# 设置文件打开状态self.file_opened = Trueself.update_fileopen_state()except Exception as e:print(f"Error opening dropped PDF: {e}")def print_document(self):if not self.doc:return# 创建打印机对象printer = QPrinter(QPrinter.PrinterMode.HighResolution)printer.setPageSize(QPageSize.PageSizeId.A4)printer.setColorMode(QPrinter.ColorMode.Color)# 显示打印对话框print_dialog = QPrintDialog(printer, self)if print_dialog.exec() != QPrintDialog.DialogCode.Accepted:return# 创建绘图对象painter = QPainter()if not painter.begin(printer):print("Failed to initialize printer")return# 获取当前显示的页面page = self.doc.load_page(self.current_page)# 获取页面尺寸(以像素为单位)rect = page.rectwidth = rect.widthheight = rect.height# 计算适合打印机的缩放比例scale = min(printer.pageRect().width() / width,printer.pageRect().height() / height)# 渲染页面到打印机zoom_matrix = fitz.Matrix(scale, scale)pix = page.get_pixmap(matrix=zoom_matrix)# 转换为 QImageimage_format = QImage.Format.Format_RGB888if pix.alpha:image_format = QImage.Format.Format_RGBA8888qimage = QImage(pix.samples,pix.width,pix.height,pix.stride,image_format)# 绘制到打印机painter.drawImage(0, 0, qimage)painter.end()def update_fileopen_state(self):if self.file_opened:self.toolbar_prev_action.setEnabled(True)self.toolbar_next_action.setEnabled(True)self.toolbar_zoom_out_action.setEnabled(True)self.zoom_combo.setEnabled(True)self.toolbar_zoom_in_action.setEnabled(True)self.toolbar_reset_zoom_action.setEnabled(True)self.toolbar_print_action.setEnabled(True)self.prev_search_action.setEnabled(True)self.next_search_action.setEnabled(True)self.file_menu_close_action.setEnabled(True)self.search_input.setEnabled(True)self.search_button.setEnabled(True)else:self.toolbar_prev_action.setEnabled(False)self.toolbar_next_action.setEnabled(False)self.toolbar_zoom_out_action.setEnabled(False)self.zoom_combo.setEnabled(False)self.toolbar_zoom_in_action.setEnabled(False)self.toolbar_reset_zoom_action.setEnabled(False)self.toolbar_print_action.setEnabled(False)self.prev_search_action.setEnabled(False)self.next_search_action.setEnabled(False)self.file_menu_close_action.setEnabled(False)self.search_input.clear()self.search_input.setEnabled(False)self.search_button.setEnabled(False)self.thumbnail_list.clear()self.bookmark_tree.clear()self.search_results_list.clear()if __name__ == "__main__":app = QApplication(sys.argv)app.setStyle("fusion")viewer = PDFViewer()viewer.show()sys.exit(app.exec())

相关文章:

  • 异步爬虫---
  • C++11 Generalized(non-trivial) Unions:从入门到精通
  • 音乐调性关系与音准训练指南
  • 深刻理解深度学习的注意力机制Attention
  • vLLM用2*(8 H800)部署DeepSeek-R1-0528-685B
  • ubuntu 拒绝ssh连接,连不上ssh,无法远程登录: Connection failed.
  • 第18篇:数据库中间件架构中的服务治理与限流熔断机制设计
  • [Java恶补day24] 74. 搜索二维矩阵
  • 【MacOS】系统数据占用超大存储空间,原因、定位、清理方式记录
  • vue常用框架,及更新内容
  • ServiceNow培训第1期
  • 50种3D效果演示(OpenGL)
  • openeuler 虚拟机:Nginx 日志分析脚本
  • 从开发到上线:iOS App混淆保护的完整生命周期管理(含Ipa Guard)
  • 直角坐标系-zernike多项式波面拟合
  • 初学时间复杂度
  • MRI中的“髓鞘探测器”:T1w/T2w比值揭秘
  • LeetCode 744.寻找比目标字母大的最小字母
  • 【C++特殊工具与技术】优化内存分配(六):运行时类型识别
  • 用 PlatformIO + ESP-IDF 框架开发 ESP32
  • 重庆网站设计哪家好/整站优化 快速排名
  • 国内最新新闻摘抄30字/东莞seo代理
  • 用百度网盘做视频网站/怎么申请建立网站
  • wordpress 如何从新安装/云优客seo排名公司
  • 网站建设 济南/网站权重怎么看
  • 网站建设公司广东/优化seo可以从以下几个方面进行