diff -r 69e183e4db6f -r 92dcd34d54e4 src/eric7/PdfViewer/PdfViewerWindow.py --- a/src/eric7/PdfViewer/PdfViewerWindow.py Fri Jan 13 18:20:54 2023 +0100 +++ b/src/eric7/PdfViewer/PdfViewerWindow.py Sat Jan 14 18:25:26 2023 +0100 @@ -8,6 +8,7 @@ """ import contextlib +import math import os import pathlib @@ -16,7 +17,7 @@ from PyQt6.QtPdf import QPdfDocument from PyQt6.QtPdfWidgets import QPdfView from PyQt6.QtWidgets import ( - QWhatsThis, QMenu, QTabWidget, QSplitter, QSpinBox, QAbstractSpinBox + QWhatsThis, QMenu, QTabWidget, QSplitter, QToolBar, QDialog ) from eric7 import Preferences @@ -24,10 +25,15 @@ from eric7.EricGui.EricAction import EricAction from eric7.EricWidgets import EricFileDialog, EricMessageBox from eric7.EricWidgets.EricMainWindow import EricMainWindow +from eric7.EricWidgets.EricStretchableSpacer import EricStretchableSpacer from eric7.Globals import recentNamePdfFiles from eric7.SystemUtilities import FileSystemUtilities from .PdfPageSelector import PdfPageSelector +from .PdfZoomSelector import PdfZoomSelector + + +# TODO: make this an eric widget class PdfViewerWindow(EricMainWindow): @@ -43,6 +49,8 @@ maxMenuFilePathLen = 75 + ZoomMultiplier = math.sqrt(2.0) + def __init__(self, fileName="", parent=None, fromEric=False, project=None): """ Constructor @@ -78,7 +86,7 @@ self.setCentralWidget(self.__cw) # create a few widgets needed in the toolbars - self.__pageSelector = PdfPageSelector() + self.__pageSelector = PdfPageSelector(self) self.__pageSelector.setDocument(self.__pdfDocument) self.__view.pageNavigator().currentPageChanged.connect( self.__pageSelector.setValue @@ -86,6 +94,13 @@ self.__pageSelector.valueChanged.connect(self.__pageSelected) self.__pageSelector.gotoPage.connect(self.__gotoPage) + self.__zoomSelector = PdfZoomSelector(self) + self.__zoomSelector.zoomModeChanged.connect(self.__view.setZoomMode) + ##self.__zoomSelector.zoomModeChanged.connect(self.__setFocusToView) + self.__zoomSelector.zoomFactorChanged.connect(self.__view.setZoomFactor) + ##self.__zoomSelector.zoomFactorChanged.connect(self.__setFocusToView) + self.__zoomSelector.reset() + g = Preferences.getGeometry("PdfViewerGeometry") if g.isEmpty(): s = QSize(1000, 1000) @@ -105,13 +120,11 @@ self.__view.pageNavigator().forwardAvailableChanged.connect( self.forwardAct.setEnabled ) + self.__view.zoomFactorChanged.connect(self.__zoomSelector.setZoomFactor) PdfViewerWindow.windows.append(self) - state = Preferences.getPdfViewer("PdfViewerState") - self.restoreState(state) - splitterState = Preferences.getPdfViewer("PdfViewerSplitterState") - self.__cw.restoreState(splitterState) + self.__restoreViewerState() self.__checkActions() @@ -272,7 +285,6 @@ """ Private method to define the navigation related user interface actions. """ - # TODO: Goto page (goto dialog) (Ctrl+G) self.previousPageAct = EricAction( self.tr("Previous Page"), EricPixmapCache.getIcon("1leftarrow"), @@ -359,15 +371,62 @@ self.forwardAct.triggered.connect(self.__forwardInHistory) self.__actions.append(self.forwardAct) + self.gotoAct = EricAction( + self.tr("Go to Page"), + EricPixmapCache.getIcon("gotoJump"), + self.tr("&Go to Page..."), + QKeySequence(self.tr("Ctrl+G", "Goto|Go to Page")), + 0, + self, + "pdfviewer_goto_gotopage", + ) + self.gotoAct.setStatusTip( + self.tr("Jump to a page selected via a dialog") + ) + self.gotoAct.triggered.connect(self.__gotoPage) + self.__actions.append(self.gotoAct) + def __initViewActions(self): """ Private method to define the view related user interface actions. """ - # Zoom in (Ctrl++) - # Zoom out (Ctrl+-) - # Zoom reset (Ctrl+0) # Page Width (checkable, exclusive) # Whole Page (checkable, exclusive) + self.zoomInAct = EricAction( + self.tr("Zoom in"), + EricPixmapCache.getIcon("zoomIn"), + self.tr("Zoom &in"), + QKeySequence(self.tr("Ctrl++", "View|Zoom in")), + 0, + self, + "pdfviewer_view_zoomin", + ) + self.zoomInAct.triggered.connect(self.__zoomIn) + self.__actions.append(self.zoomInAct) + + self.zoomOutAct = EricAction( + self.tr("Zoom out"), + EricPixmapCache.getIcon("zoomOut"), + self.tr("Zoom &out"), + QKeySequence(self.tr("Ctrl+-", "View|Zoom out")), + 0, + self, + "pdfviewer_view_zoomout", + ) + self.zoomOutAct.triggered.connect(self.__zoomOut) + self.__actions.append(self.zoomOutAct) + + self.zoomResetAct = EricAction( + self.tr("Zoom to 100%"), + EricPixmapCache.getIcon("zoomReset"), + self.tr("Zoom to &100%"), + QKeySequence(self.tr("Ctrl+0", "View|Zoom reset")), + 0, + self, + "pdfviewer_view_zoomreset", + ) + self.zoomResetAct.triggered.connect(self.__zoomReset) + self.__actions.append(self.zoomResetAct) def __initHelpActions(self): """ @@ -434,19 +493,30 @@ """ Private slot to check some actions for their enable/disable status. """ - self.reloadAct.setEnabled( - self.__pdfDocument.status() == QPdfDocument.Status.Ready - ) + ready = self.__pdfDocument.status() == QPdfDocument.Status.Ready + + self.reloadAct.setEnabled(ready) + self.propertiesAct.setEnabled(ready) curPage = self.__view.pageNavigator().currentPage() - self.previousPageAct.setEnabled(curPage > 0) - self.nextPageAct.setEnabled(curPage < self.__pdfDocument.pageCount() - 1) - self.startDocumentAct.setEnabled(curPage != 0) - self.endDocumentAct.setEnabled(curPage != self.__pdfDocument.pageCount() - 1) + self.previousPageAct.setEnabled(curPage > 0 and ready) + self.nextPageAct.setEnabled( + curPage < self.__pdfDocument.pageCount() - 1 and ready + ) + self.startDocumentAct.setEnabled(curPage != 0 and ready) + self.endDocumentAct.setEnabled( + curPage != self.__pdfDocument.pageCount() - 1 and ready + ) + self.gotoAct.setEnabled(ready) self.backwardAct.setEnabled(self.__view.pageNavigator().backAvailable()) self.forwardAct.setEnabled(self.__view.pageNavigator().forwardAvailable()) + self.zoomInAct.setEnabled(ready) + self.zoomOutAct.setEnabled(ready) + self.zoomResetAct.setEnabled(ready) + self.__zoomSelector.setEnabled(ready) + # TODO: not yet implemented ##def setRecentPath(self, openPath): @@ -487,6 +557,13 @@ self.__recentMenu.aboutToShow.connect(self.__showRecentMenu) self.__recentMenu.triggered.connect(self.__openRecentPdfFile) + menu = mb.addMenu(self.tr("&View")) + menu.setTearOffEnabled(True) + menu.addAction(self.zoomInAct) + menu.addAction(self.zoomOutAct) + menu.addAction(self.zoomResetAct) + # TODO: not yet implemented + menu = mb.addMenu(self.tr("&Go To")) menu.setTearOffEnabled(True) menu.addAction(self.previousPageAct) @@ -498,7 +575,7 @@ menu.addAction(self.backwardAct) menu.addAction(self.forwardAct) menu.addSeparator() - # TODO: not yet implemented + menu.addAction(self.gotoAct) mb.addSeparator() @@ -512,28 +589,37 @@ """ Private method to create the toolbars. """ - filetb = self.addToolBar(self.tr("File")) - filetb.setObjectName("FileToolBar") - filetb.addAction(self.newWindowAct) - filetb.addAction(self.openAct) - filetb.addSeparator() - filetb.addAction(self.closeAct) + mainToolBar = QToolBar() + mainToolBar.setObjectName("main_toolbar") + mainToolBar.setMovable(False) + mainToolBar.setFloatable(False) + + # 1. File actions + mainToolBar.addAction(self.newWindowAct) + mainToolBar.addAction(self.openAct) + mainToolBar.addSeparator() + mainToolBar.addAction(self.closeAct) if not self.__fromEric: - filetb.addAction(self.exitAct) + mainToolBar.addAction(self.exitAct) + mainToolBar.addSeparator() - gototb = self.addToolBar(self.tr("Goto")) - gototb.setObjectName("GotoToolBar") - gototb.addAction(self.startDocumentAct) - gototb.addWidget(self.__pageSelector) - gototb.addAction(self.endDocumentAct) + # 2. Go to actions + mainToolBar.addWidget(EricStretchableSpacer()) + mainToolBar.addAction(self.startDocumentAct) + mainToolBar.addWidget(self.__pageSelector) + mainToolBar.addAction(self.endDocumentAct) + mainToolBar.addWidget(EricStretchableSpacer()) + mainToolBar.addSeparator() - viewtb = self.addToolBar(self.tr("View")) - viewtb.setObjectName("ViewToolBar") + # 3. View actions # TODO: not yet implemented + mainToolBar.addAction(self.zoomOutAct) + mainToolBar.addWidget(self.__zoomSelector) + mainToolBar.addAction(self.zoomInAct) + mainToolBar.addAction(self.zoomResetAct) - helptb = self.addToolBar(self.tr("Help")) - helptb.setObjectName("HelpToolBar") - helptb.addAction(self.whatsThisAct) + self.addToolBar(mainToolBar) + self.addToolBarBreak() def __createStatusBar(self): """ @@ -551,24 +637,41 @@ @param evt reference to the close event @type QCloseEvent """ + Preferences.setGeometry("PdfViewerGeometry", self.saveGeometry()) + + self.__saveViewerState() + + with contextlib.suppress(ValueError): + if self.__fromEric or len(PdfViewerWindow.windows) > 1: + PdfViewerWindow.windows.remove(self) + + self.__saveRecent() + + evt.accept() + self.viewerClosed.emit() + + def __saveViewerState(self): + """ + Private method to save the PDF Viewer state data. + """ + # TODO: save current zoom factor and mode + page mode state = self.saveState() Preferences.setPdfViewer("PdfViewerState", state) splitterState = self.__cw.saveState() Preferences.setPdfViewer("PdfViewerSplitterState", splitterState) - Preferences.setGeometry("PdfViewerGeometry", self.saveGeometry()) - - with contextlib.suppress(ValueError): - if self.__fromEric or len(PdfViewerWindow.windows) > 1: - PdfViewerWindow.windows.remove(self) - if not self.__fromEric: Preferences.syncPreferences() - self.__saveRecent() - - evt.accept() - self.viewerClosed.emit() + def __restoreViewerState(self): + """ + Private method to restore the PDF Viewer state data. + """ + # TODO: restore zoom factor and mode + page mode + state = Preferences.getPdfViewer("PdfViewerState") + self.restoreState(state) + splitterState = Preferences.getPdfViewer("PdfViewerSplitterState") + self.__cw.restoreState(splitterState) def __setViewerTitle(self, title): """ @@ -615,6 +718,8 @@ @param fileName path of the PDF file to load @type str """ + # TODO: if error is QPdfDocument.Error.IncorrectPassword ask for PW and try + # again until cancelled err = self.__pdfDocument.load(fileName) if err != QPdfDocument.Error.None_: EricMessageBox.critical( @@ -812,6 +917,7 @@ @param act reference to the action that triggered @type QAction """ + # TODO: add config option to open recent files in new viewer or the same one fileName = act.data() self.__loadPdfFile(fileName) @@ -870,6 +976,16 @@ Private slot to show a dialog to select a page to jump to. """ # TODO: not yet implemented + from .PdfGoToDialog import PdfGoToDialog + + dlg = PdfGoToDialog( + self.__view.pageNavigator().currentPage(), + self.__pdfDocument.pageCount(), + self, + ) + if dlg.exec() == QDialog.DialogCode.Accepted: + page = dlg.getPage() + self.__pageSelected(page) @pyqtSlot() def __previousPage(self): @@ -916,3 +1032,41 @@ Private slot to go forward in the view history. """ self.__view.pageNavigator().forward() + + def __calculateZoomFactor(self): + if self.__view.ZoomMode == QPdfView.ZoomMode.FitToWidth: + pass + else: + return self.__view.zoomFactor() + + @pyqtSlot() + def __zoomIn(self): + """ + Private slot to zoom into the view. + """ + self.__view.setZoomFactor( + self.__view.zoomFactor() * PdfViewerWindow.ZoomMultiplier + ) + + @pyqtSlot() + def __zoomOut(self): + """ + Private slot to zoom out of the view. + """ + self.__view.setZoomFactor( + self.__view.zoomFactor() / PdfViewerWindow.ZoomMultiplier + ) + + @pyqtSlot() + def __zoomReset(self): + """ + Private slot to reset the zoom factor of the view. + """ + self.__view.setZoomFactor(1.0) + + @pyqtSlot() + def __setFocusToView(self): + """ + Private slot to set the focus to the PDF document view. + """ + self.__view.setFocus(Qt.FocusReason.OtherFocusReason)