PDF Viewer pdf_viewer

Wed, 18 Jan 2023 16:38:29 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 18 Jan 2023 16:38:29 +0100
branch
pdf_viewer
changeset 9706
c0ff0b4d5657
parent 9705
7629a6f23b2e
child 9707
717f95e35ca8

PDF Viewer
- added the search actions and a 'Full Screen' action to the menus

src/eric7/PdfViewer/PdfSearchWidget.py file | annotate | diff | comparison | revisions
src/eric7/PdfViewer/PdfViewerWindow.py file | annotate | diff | comparison | revisions
--- a/src/eric7/PdfViewer/PdfSearchWidget.py	Wed Jan 18 16:37:08 2023 +0100
+++ b/src/eric7/PdfViewer/PdfSearchWidget.py	Wed Jan 18 16:38:29 2023 +0100
@@ -24,9 +24,15 @@
 
     @signal rowCountChanged() emitted to indicate a change of the number
         of items
+    @signal searchNextAvailable(bool) emitted to indicate the availability of
+        search results after the current one
+    @signal searchPrevAvailable(bool) emitted to indicate the availability of
+        search results before the current one
     """
 
     rowCountChanged = pyqtSignal()
+    searchNextAvailable = pyqtSignal(bool)
+    searchPrevAvailable = pyqtSignal(bool)
 
     def __init__(self, parent=None):
         """
@@ -48,6 +54,8 @@
         self.__searchModel.modelReset.connect(self.__clear)
         self.__searchModel.rowsInserted.connect(self.__rowsInserted)
 
+        self.currentItemChanged.connect(self.__handleCurrentItemChanged)
+
     def setSearchString(self, searchString):
         """
         Public method to set the search string.
@@ -90,7 +98,10 @@
         Private slot to clear the list of search results.
         """
         self.clear()
+
         self.rowCountChanged.emit()
+        self.searchNextAvailable.emit(False)
+        self.searchPrevAvailable.emit(False)
 
     @pyqtSlot(QModelIndex, int, int)
     def __rowsInserted(self, parent, first, last):
@@ -143,6 +154,7 @@
             self.resizeColumnToContents(column)
 
         self.rowCountChanged.emit()
+        self.searchNextAvailable.emit(True)
 
     def rowCount(self):
         """
@@ -203,13 +215,34 @@
         row = self.indexOfTopLevelItem(item)
         return self.__searchModel.resultAtIndex(row)
 
+    @pyqtSlot()
+    def __handleCurrentItemChanged(self):
+        """
+        Private slot to handle a change of the current item.
+        """
+        hasSearchResults = bool(self.topLevelItemCount())
+        currentRow = self.currentRow()
+        self.searchPrevAvailable.emit(hasSearchResults and currentRow > 0)
+        self.searchNextAvailable.emit(
+            hasSearchResults and currentRow < self.topLevelItemCount() - 1
+        )
+
 
 class PdfSearchWidget(QWidget):
     """
     Class implementing a Search widget.
+
+    @signal searchResultAktivated(QPdfLink) emitted to send the activated search
+        result link
+    @signal searchNextAvailable(bool) emitted to indicate the availability of
+        search results after the current one
+    @signal searchPrevAvailable(bool) emitted to indicate the availability of
+        search results before the current one
     """
 
     searchResultActivated = pyqtSignal(QPdfLink)
+    searchNextAvailable = pyqtSignal(bool)
+    searchPrevAvailable = pyqtSignal(bool)
 
     def __init__(self, document, parent=None):
         """
@@ -271,12 +304,16 @@
         document.statusChanged.connect(self.__handleDocumentStatus)
         self.__searchEdit.returnPressed.connect(self.__search)
         self.__searchEdit.textChanged.connect(self.__searchTextChanged)
-        self.__resultsWidget.rowCountChanged.connect(self.__updateButtons)
-        self.__resultsWidget.currentItemChanged.connect(
-            self.__updateButtons
+        self.__resultsWidget.searchNextAvailable.connect(self.searchNextAvailable)
+        self.__resultsWidget.searchPrevAvailable.connect(self.searchPrevAvailable)
+        self.__resultsWidget.searchNextAvailable.connect(
+            self.__findNextButton.setEnabled
         )
-        self.__findNextButton.clicked.connect(self.__nextResult)
-        self.__findPrevButton.clicked.connect(self.__previousResult)
+        self.__resultsWidget.searchPrevAvailable.connect(
+            self.__findPrevButton.setEnabled
+        )
+        self.__findNextButton.clicked.connect(self.nextResult)
+        self.__findPrevButton.clicked.connect(self.previousResult)
 
     @pyqtSlot(QPdfDocument.Status)
     def __handleDocumentStatus(self, status):
@@ -313,18 +350,6 @@
         searchString = self.__searchEdit.text()
         self.__resultsWidget.setSearchString(searchString)
 
-    @pyqtSlot()
-    def __updateButtons(self):
-        """
-        Private slot to update the state of the navigation buttons.
-        """
-        hasSearchResults = bool(self.__resultsWidget.rowCount())
-        currentRow = self.__resultsWidget.currentRow()
-        self.__findPrevButton.setEnabled(hasSearchResults and currentRow > 0)
-        self.__findNextButton.setEnabled(
-            hasSearchResults and currentRow < self.__resultsWidget.rowCount() - 2
-        )
-
     @pyqtSlot(QTreeWidgetItem)
     def __entrySelected(self, item):
         """
@@ -337,23 +362,31 @@
         self.searchResultActivated.emit(link)
 
     @pyqtSlot()
-    def __nextResult(self):
+    def nextResult(self):
         """
-        Private slot to activate the next result.
+        Public slot to activate the next result.
         """
         row = self.__resultsWidget.currentRow()
-        if row < self.__resultsWidget.rowCount() - 2:
+        if row < self.__resultsWidget.rowCount() - 1:
             nextItem = self.__resultsWidget.topLevelItem(row + 1)
             self.__resultsWidget.setCurrentItem(nextItem)
             self.__entrySelected(nextItem)
 
     @pyqtSlot()
-    def __previousResult(self):
+    def previousResult(self):
         """
-        Private slot to activate the previous result.
+        Public slot to activate the previous result.
         """
         row = self.__resultsWidget.currentRow()
         if row > 0:
             prevItem = self.__resultsWidget.topLevelItem(row - 1)
             self.__resultsWidget.setCurrentItem(prevItem)
             self.__entrySelected(prevItem)
+
+    @pyqtSlot()
+    def activateSearch(self):
+        """
+        Public slot to 'activate' a search.
+        """
+        self.__searchEdit.setFocus(Qt.FocusReason.OtherFocusReason)
+        self.__searchEdit.selectAll()
--- a/src/eric7/PdfViewer/PdfViewerWindow.py	Wed Jan 18 16:37:08 2023 +0100
+++ b/src/eric7/PdfViewer/PdfViewerWindow.py	Wed Jan 18 16:38:29 2023 +0100
@@ -18,7 +18,8 @@
 from PyQt6.QtPdf import QPdfDocument, QPdfLink
 from PyQt6.QtPdfWidgets import QPdfView
 from PyQt6.QtWidgets import (
-    QWhatsThis, QMenu, QTabWidget, QSplitter, QToolBar, QDialog
+    QWhatsThis, QMenu, QTabWidget, QSplitter, QToolBar, QDialog, QInputDialog,
+    QLineEdit
 )
 
 from eric7 import Preferences
@@ -28,7 +29,7 @@
 from eric7.EricWidgets.EricMainWindow import EricMainWindow
 from eric7.EricWidgets.EricStretchableSpacer import EricStretchableSpacer
 from eric7.Globals import recentNamePdfFiles
-from eric7.SystemUtilities import FileSystemUtilities
+from eric7.SystemUtilities import FileSystemUtilities, OSUtilities
 
 from .PdfPageSelector import PdfPageSelector
 from .PdfZoomSelector import PdfZoomSelector
@@ -147,7 +148,10 @@
         self.__view.zoomModeChanged.connect(self.__zoomSelector.setZoomMode)
 
         self.__tocWidget.topicActivated.connect(self.__tocActivated)
+
         self.__searchWidget.searchResultActivated.connect(self.__handleSearchResult)
+        self.__searchWidget.searchNextAvailable.connect(self.searchNextAct.setEnabled)
+        self.__searchWidget.searchPrevAvailable.connect(self.searchPrevAct.setEnabled)
 
         PdfViewerWindow.windows.append(self)
 
@@ -418,7 +422,23 @@
         """
         Private method to define the view related user interface actions.
         """
-        # TODO: add Fullscreen action
+        self.fullScreenAct = EricAction(
+            self.tr("Full Screen"),
+            EricPixmapCache.getIcon("windowFullscreen"),
+            self.tr("&Full Screen"),
+            0,
+            0,
+            self,
+            "pdfviewer_view_full_screen",
+        )
+        if OSUtilities.isMacPlatform():
+            self.fullScreenAct.setShortcut(QKeySequence(self.tr("Meta+Ctrl+F")))
+        else:
+            self.fullScreenAct.setShortcut(QKeySequence(self.tr("F11")))
+        self.fullScreenAct.setCheckable(True)
+        self.fullScreenAct.triggered.connect(self.__toggleFullScreen)
+        self.__actions.append(self.fullScreenAct)
+
         self.zoomInAct = EricAction(
             self.tr("Zoom in"),
             EricPixmapCache.getIcon("zoomIn"),
@@ -508,6 +528,45 @@
         self.copyAllPageAct.triggered.connect(self.__copyAllTextOfPage)
         self.__actions.append(self.copyAllPageAct)
 
+        self.searchAct = EricAction(
+            self.tr("Search"),
+            EricPixmapCache.getIcon("find"),
+            self.tr("&Search..."),
+            QKeySequence(self.tr("Ctrl+F", "Edit|Search")),
+            0,
+            self,
+            "pdfviewer_edit_search",
+        )
+        self.searchAct.triggered.connect(self.__search)
+        self.__actions.append(self.searchAct)
+
+        self.searchNextAct = EricAction(
+            self.tr("Search Next"),
+            EricPixmapCache.getIcon("findNext"),
+            self.tr("Search &Next"),
+            QKeySequence(self.tr("F3", "Edit|Search Next")),
+            0,
+            self,
+            "pdfviewer_edit_searchnext",
+        )
+        self.searchNextAct.triggered.connect(self.__searchWidget.nextResult)
+        self.__actions.append(self.searchNextAct)
+
+        self.searchPrevAct = EricAction(
+            self.tr("Search Previous"),
+            EricPixmapCache.getIcon("findPrev"),
+            self.tr("Search &Previous"),
+            QKeySequence(self.tr("Shift+F3", "Edit|Search Previous")),
+            0,
+            self,
+            "pdfviewer_edit_searchprevious",
+        )
+        self.searchPrevAct.triggered.connect(self.__searchWidget.previousResult)
+        self.__actions.append(self.searchPrevAct)
+
+        self.searchNextAct.setEnabled(False)
+        self.searchPrevAct.setEnabled(False)
+
     def __initSettingsActions(self):
         """
         Private method to create the Settings actions.
@@ -628,6 +687,10 @@
         self.zoomWholePageAct.setEnabled(ready)
         self.__zoomSelector.setEnabled(ready)
 
+        self.copyAllAct.setEnabled(ready)
+        self.copyAllPageAct.setEnabled(ready)
+        self.searchAct.setEnabled(ready)
+
     def __initMenus(self):
         """
         Private method to create the menus.
@@ -658,6 +721,8 @@
 
         menu = mb.addMenu(self.tr("&View"))
         menu.setTearOffEnabled(True)
+        menu.addAction(self.fullScreenAct)
+        menu.addSeparator()
         menu.addAction(self.zoomInAct)
         menu.addAction(self.zoomOutAct)
         menu.addAction(self.zoomResetAct)
@@ -679,6 +744,10 @@
         menu.addAction(self.copyAllAct)
         menu.addSeparator()
         menu.addAction(self.copyAllPageAct)
+        menu.addSeparator()
+        menu.addAction(self.searchAct)
+        menu.addAction(self.searchNextAct)
+        menu.addAction(self.searchPrevAct)
 
         menu = mb.addMenu(self.tr("&Go To"))
         menu.setTearOffEnabled(True)
@@ -774,6 +843,8 @@
 
         self.__saveRecent()
 
+        self.__documentInfoWidget.setDocument(None)
+
         evt.accept()
         self.viewerClosed.emit()
 
@@ -855,9 +926,21 @@
         @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)
+        canceled = False
+        err = QPdfDocument.Error.IncorrectPassword
+        while not canceled and err == QPdfDocument.Error.IncorrectPassword:
+            err = self.__pdfDocument.load(fileName)
+            if err == QPdfDocument.Error.IncorrectPassword:
+                password, ok = QInputDialog.getText(
+                    self,
+                    self.tr("Load PDF File"),
+                    self.tr("Enter password to read the document:"),
+                    QLineEdit.EchoMode.Password,
+                )
+                if ok:
+                    self.__pdfDocument.setPassword(password)
+                else:
+                    canceled = True
         if err != QPdfDocument.Error.None_:
             EricMessageBox.critical(
                 self,
@@ -996,6 +1079,14 @@
         """
         self.__view.pageNavigator().jump(link)
 
+    @pyqtSlot()
+    def __search(self):
+        """
+        Private slot to initiate a search.
+        """
+        self.__info.setCurrentWidget(self.__searchWidget)
+        self.__searchWidget.activateSearch()
+
     def __setCurrentFile(self, fileName):
         """
         Private method to register the file name of the current file.
@@ -1204,6 +1295,19 @@
         """
         self.__view.pageNavigator().forward()
 
+    @pyqtSlot(bool)
+    def __toggleFullScreen(self, on):
+        """
+        Private slot to toggle the full screen mode.
+
+        @param on flag indicating to activate full screen mode
+        @type bool
+        """
+        if on:
+            self.showFullScreen()
+        else:
+            self.showNormal()
+
     @pyqtSlot()
     def __zoomIn(self):
         """

eric ide

mercurial