src/eric7/PdfViewer/PdfViewerWindow.py

branch
pdf_viewer
changeset 9699
92dcd34d54e4
parent 9698
69e183e4db6f
child 9700
b74a13a382a8
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)

eric ide

mercurial