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.
This commit is contained in:
Christopher T. Johnson
2025-02-20 10:09:32 -05:00
parent 50b14562fc
commit 9f1c54c1e7
16 changed files with 4040 additions and 21 deletions

View File

@@ -1,31 +1,173 @@
from PySide6.QtCore import QFile
from PySide6.QtPdf import QPdfDocument
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 QDialog, QVBoxLayout, QWidget
from PySide6.QtWidgets import QComboBox, QDialog, QMenuBar, QSpinBox, QWidget
from ui.pdfViewer import Ui_pdfViewer
ZOOM_MULTIPLIER = math.sqrt(2.0)
class PDFViewer(QDialog):
pdf_view: QPdfView
pdf_document: QPdfDocument
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)
def __init__(self, parent: QWidget) -> None:
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.pdf_view = QPdfView()
self.pdf_document = QPdfDocument()
self.zoomSelector = ZoomSelector(self)
self.pageSelector = QSpinBox(self)
self.document = QPdfDocument(self)
layout = QVBoxLayout(self)
layout.addWidget(self.pdf_view)
self.setLayout(layout)
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
def load_pdf(self, file: QFile) -> None:
if not file.isOpen():
file.open(file.OpenModeFlag.ReadOnly)
self.pdf_document.load(file)
self.pdf_view.setDocument(self.pdf_document)
self.pdf_view.setPageMode(QPdfView.PageMode.MultiPage)
size = self.pdf_document.pagePointSize(1)
self.resize(size.toSize())
print(size)
@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