Wed, 18 Jan 2023 14:31:55 +0100
PDF Viewer
- created a specialized PDF view class to intercept and handle certain events
- added a 'Search' widget
# -*- coding: utf-8 -*- # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a specialized PDF view class. """ from PyQt6.QtCore import QSize, Qt, pyqtSlot, QEvent from PyQt6.QtGui import QGuiApplication from PyQt6.QtPdfWidgets import QPdfView from .PdfZoomSelector import PdfZoomSelector class PdfView(QPdfView): """ Class implementing a specialized PDF view. """ def __init__(self, parent): """ Constructor @param parent reference to the parent widget @type QWidget """ super().__init__(parent) self.__screenResolution = ( QGuiApplication.primaryScreen().logicalDotsPerInch() / 72.0 ) self.grabGesture(Qt.GestureType.PinchGesture) def __zoomInOut(self, zoomIn): """ Private method to zoom into or out of the view. @param zoomIn flag indicating to zoom into the view @type bool """ zoomFactor = self.__zoomFactorForMode(self.zoomMode()) factors = list(PdfZoomSelector.ZoomValues) factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView)) factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth)) if zoomIn: factors.sort() if zoomFactor >= factors[-1]: return newIndex = next(x for x, val in enumerate(factors) if val > zoomFactor) else: factors.sort(reverse=True) if zoomFactor <= factors[-1]: return newIndex = next(x for x, val in enumerate(factors) if val < zoomFactor) newFactor = factors[newIndex] if newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView): self.setZoomMode(QPdfView.ZoomMode.FitInView) self.zoomModeChanged.emit(QPdfView.ZoomMode.FitInView) elif newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth): self.setZoomMode(QPdfView.ZoomMode.FitToWidth) self.zoomModeChanged.emit(QPdfView.ZoomMode.FitToWidth) else: self.setZoomFactor(newFactor) self.zoomFactorChanged.emit(newFactor) self.setZoomMode(QPdfView.ZoomMode.Custom) self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) def __zoomFactorForMode(self, zoomMode): """ Private method to calculate the zoom factor iaw. the current zoom mode. @param zoomMode zoom mode to get the zoom factor for @type QPdfView.ZoomMode @return zoom factor @rtype float """ if zoomMode == QPdfView.ZoomMode.Custom: return self.zoomFactor() else: viewport = self.viewport() curPage = self.pageNavigator().currentPage() margins = self.documentMargins() if zoomMode == QPdfView.ZoomMode.FitToWidth: pageSize = ( self.document().pagePointSize(curPage) * self.__screenResolution ).toSize() factor = ( viewport.width() - margins.left() - margins.right() ) / pageSize.width() pageSize *= factor else: # QPdfView.ZoomMode.FitInView viewportSize = viewport.size() + QSize( -margins.left() - margins.right(), -self.pageSpacing() ) pageSize = ( self.document().pagePointSize(curPage) * self.__screenResolution ).toSize() pageSize = pageSize.scaled( viewportSize, Qt.AspectRatioMode.KeepAspectRatio ) zoomFactor = pageSize.width() / ( self.document().pagePointSize(curPage) * self.__screenResolution ).width() return zoomFactor @pyqtSlot() def zoomIn(self): """ Public slot to zoom into the view. """ self.__zoomInOut(True) @pyqtSlot() def zoomOut(self): """ Public slot to zoom out of the view. """ self.__zoomInOut(False) @pyqtSlot() def zoomReset(self): """ Public slot to reset the zoom factor of the view. """ if self.zoomMode() != QPdfView.ZoomMode.Custom or self.zoomFactor() != 1.0: self.setZoomFactor(1.0) self.zoomFactorChanged.emit(1.0) self.setZoomMode(QPdfView.ZoomMode.Custom) self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) def wheelEvent(self, evt): """ Protected method to handle wheel events. @param evt reference to the wheel event @type QWheelEvent """ delta = evt.angleDelta().y() if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: if delta < 0: self.zoomOut() elif delta > 0: self.zoomIn() evt.accept() return elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: if delta < 0: self.pageNavigator().back() elif delta > 0: self.pageNavigator().forward() evt.accept() return super().wheelEvent(evt) def event(self, evt): """ Public method handling events. @param evt reference to the event @type QEvent @return flag indicating, if the event was handled @rtype bool """ if evt.type() == QEvent.Type.Gesture: self.gestureEvent(evt) return True return super().event(evt) def gestureEvent(self, evt): """ Protected method handling gesture events. @param evt reference to the gesture event @type QGestureEvent """ pinch = evt.gesture(Qt.GestureType.PinchGesture) if pinch: if pinch.state() == Qt.GestureState.GestureStarted: pinch.setTotalScaleFactor(self.__zoomFactorForMode(self.zoomMode())) elif pinch.state() == Qt.GestureState.GestureUpdated: if self.zoomMode() != QPdfView.ZoomMode.Custom: self.setZoomMode(QPdfView.ZoomMode.Custom) self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) zoomFactor = pinch.totalScaleFactor() self.setZoomFactor(zoomFactor) self.zoomFactorChanged.emit(zoomFactor) evt.accept()