Files
scotus-watch/pdfView.py
Christopher T. Johnson 9f1c54c1e7 Implement a PDF Viewer Dialog
This is based on the example in pyside6 changed to use a QDialog and
not a QMainWindow.  The MenuBar is gone, the Status Bar is gone.
2025-02-20 10:09:32 -05:00

174 lines
6.1 KiB
Python

import math
from PySide6.QtCore import QFile, QModelIndex, QPoint, Signal, Slot
from PySide6.QtPdf import QPdfBookmarkModel, QPdfDocument
from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtWidgets import QComboBox, QDialog, QMenuBar, QSpinBox, QWidget
from ui.pdfViewer import Ui_pdfViewer
ZOOM_MULTIPLIER = math.sqrt(2.0)
class ZoomSelector(QComboBox):
zoom_mode_changed = Signal(QPdfView.ZoomMode)
zoom_factor_changed = Signal(float)
def __init__(self, parent: QWidget|None = None) -> None:
super(ZoomSelector, self).__init__(parent)
self.setEditable(True)
for text in [ 'Fit Width', 'Fit Page', '12%', '25%', '33%', '50%', '66%', '75%', '100%', '125%', '150%', '200%', '400%']:
self.addItem(text)
self.currentTextChanged.connect(self.on_current_text_changed)
lineEdit = self.lineEdit()
assert lineEdit is not None
lineEdit.editingFinished.connect(self._editing_finished)
return
@Slot(float) # type: ignore
def set_zoom_factor(self, zoomFactor: float) -> None:
percent = int(zoomFactor * 100)
self.setCurrentText(f"{percent}%")
return
@Slot()
def reset(self) -> None:
self.setCurrentIndex(8) # 100%
return
@Slot(str) # type: ignore
def on_current_text_changed(self, text: str) -> None:
if text == "Fit Width":
self.zoom_mode_changed.emit(QPdfView.ZoomMode.FitToWidth)
elif text == "Fit Page":
self.zoom_mode_changed.emit(QPdfView.ZoomMode.FitInView)
elif text.endswith("%"):
zoom_level = int(text[:-1])
factor = zoom_level / 100.0
self.zoom_mode_changed.emit(QPdfView.ZoomMode.Custom)
self.zoom_factor_changed.emit(factor)
return
@Slot()
def _editing_finished(self) -> None:
lineEdit = self.lineEdit()
assert lineEdit is not None
self.on_current_text_changed(lineEdit.text())
return
class PDFViewer(QDialog, Ui_pdfViewer):
menubar: QMenuBar
zoomSelector: ZoomSelector
pageSelector: QSpinBox
document: QPdfDocument
def __init__(self, parent: QWidget | None) -> None:
super(PDFViewer, self).__init__(parent)
self.setupUi(self)
self.zoomSelector = ZoomSelector(self)
self.pageSelector = QSpinBox(self)
self.document = QPdfDocument(self)
self.zoomSelector.setMaximumWidth(150)
self.mainToolBar.insertWidget(self.actionZoom_In, self.zoomSelector)
self.mainToolBar.insertWidget(self.actionForward, self.pageSelector)
self.pageSelector.valueChanged.connect(self.page_selected)
nav = self.pdfView.pageNavigator()
nav.currentPageChanged.connect(self.pageSelector.setValue)
nav.backAvailableChanged.connect(self.actionBack.setEnabled)
nav.forwardAvailableChanged.connect(self.actionForward.setEnabled)
self.zoomSelector.zoom_mode_changed.connect(self.pdfView.setZoomMode)
self.zoomSelector.zoom_factor_changed.connect(self.pdfView.setZoomFactor)
self.zoomSelector.reset()
bookmark_model = QPdfBookmarkModel(self)
bookmark_model.setDocument(self.document)
self.bookmarkView.setModel(bookmark_model)
self.bookmarkView.activated.connect(self.bookmark_selected)
self.tabWidget.setTabEnabled(1, False) # disable pages tabwidget
self.pdfView.setDocument(self.document)
self.pdfView.zoomFactorChanged.connect(self.zoomSelector.set_zoom_factor)
return
@Slot(QFile) # type: ignore
def open(self, file: QFile) -> None:
#assert file.exists()
#if not file.isOpen():
#file.open(QFile.OpenModeFlag.ReadOnly)
#self.document.load(file)
self.document.load(file.fileName())
document_title = self.document.metaData(QPdfDocument.MetaDataField.Title)
self.setWindowTitle(document_title if document_title else "PDF Viewer")
self.page_selected(0)
self.pageSelector.setMaximum(self.document.pageCount() -1 )
return
@Slot(QModelIndex) # type: ignore
def bookmark_selected(self, index: QModelIndex) -> None:
if not index.isValid():
return
page = index.data(int(QPdfBookmarkModel.Role.Page))
zoom_level = index.data(int(QPdfBookmarkModel.Role.Level))
nav = self.pdfView.pageNavigator()
assert nav is not None
nav.jump(page, QPoint(), zoom_level)
return
@Slot(int) # type: ignore
def page_selected(self, page: int) -> None:
nav = self.pdfView.pageNavigator()
nav.jump(page, QPoint(), nav.currentZoom())
return
@Slot()
def on_actionOpen_triggered(self) -> None:
print("What are you doing in actionOpen_triggered?")
return
@Slot()
def on_actionQuit_triggered(self) -> None:
self.close()
return
@Slot()
def on_actionZoom_In_triggered(self) -> None:
factor = self.pdfView.zoomFactor() * ZOOM_MULTIPLIER
self.pdfView.setZoomFactor(factor)
return
@Slot()
def on_actionZoom_Out_triggered(self) -> None:
factor = self.pdfView.zoomFactor() / ZOOM_MULTIPLIER
self.pdfView.setZoomFactor(factor)
return
@Slot()
def on_actionPrevious_Page_triggered(self) -> None:
nav = self.pdfView.pageNavigator()
nav.jump(nav.currentPage() - 1, QPoint(), nav.currentZoom())
return
@Slot()
def on_actionNext_Page_triggered(self) -> None:
nav = self.pdfView.pageNavigator()
nav.jump(nav.currentPage() + 1, QPoint(), nav.currentZoom())
return
@Slot()
def on_actionContinuous_triggered(self) -> None:
cont_checked = self.actionContinuous.isChecked()
mode = QPdfView.PageMode.MultiPage if cont_checked else QPdfView.PageMode.SinglePage
self.pdfView.setPageMode(mode)
return
@Slot()
def on_actionBack_triggered(self) -> None:
self.pdfView.pageNavigator().back()
return
@Slot()
def on_actionForward_triggered(self) -> None:
self.pdfView.pageNavigator().forward()
return