eric7/HelpViewer/HelpViewerWidget.py

branch
eric7
changeset 8680
85503ff2fce9
parent 8679
fd172973428e
child 8681
6285e8374d99
diff -r fd172973428e -r 85503ff2fce9 eric7/HelpViewer/HelpViewerWidget.py
--- a/eric7/HelpViewer/HelpViewerWidget.py	Thu Oct 07 20:22:02 2021 +0200
+++ b/eric7/HelpViewer/HelpViewerWidget.py	Mon Oct 11 19:59:45 2021 +0200
@@ -7,14 +7,21 @@
 Module implementing an embedded viewer for QtHelp and local HTML files.
 """
 
-from PyQt6.QtCore import pyqtSlot, QUrl
-from PyQt6.QtGui import QTextDocument
+import os
+
+from PyQt6.QtCore import pyqtSlot, Qt, QUrl
+from PyQt6.QtGui import QAction
+from PyQt6.QtHelp import QHelpEngine
 from PyQt6.QtWidgets import (
     QWidget, QHBoxLayout, QVBoxLayout, QComboBox, QSizePolicy, QStackedWidget,
-    QToolButton, QButtonGroup, QAbstractButton
+    QToolButton, QButtonGroup, QAbstractButton, QMenu
 )
 
+from EricWidgets import EricFileDialog, EricMessageBox
+
 import UI.PixmapCache
+import Utilities
+import Preferences
 
 from .OpenPagesWidget import OpenPagesWidget
 
@@ -33,6 +40,8 @@
         super().__init__(parent)
         self.setObjectName("HelpViewerWidget")
         
+        self.__ui = parent
+        
         self.__layout = QVBoxLayout()
         self.__layout.setObjectName("MainLayout")
         self.__layout.setContentsMargins(0, 3, 0, 0)
@@ -46,6 +55,8 @@
             QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
         self.__selectorLayout.addWidget(self.__helpSelector)
         self.__populateHelpSelector()
+        self.__helpSelector.currentIndexChanged.connect(
+            self.__helpTopicSelected)
         
         self.__openButton = QToolButton(self)
         self.__openButton.setIcon(UI.PixmapCache.getIcon("open"))
@@ -53,10 +64,76 @@
         self.__openButton.clicked.connect(self.__openFile)
         self.__selectorLayout.addWidget(self.__openButton)
         
+        self.__actionsButton = QToolButton(self)
+        self.__actionsButton.setIcon(
+            UI.PixmapCache.getIcon("actionsToolButton"))
+        self.__actionsButton.setToolTip(
+            self.tr("Select action from menu"))
+        self.__actionsButton.setPopupMode(
+            QToolButton.ToolButtonPopupMode.InstantPopup)
+        self.__selectorLayout.addWidget(self.__actionsButton)
+        
         self.__layout.addLayout(self.__selectorLayout)
         
         ###################################################################
         
+        self.__navButtonsLayout = QHBoxLayout()
+        
+        self.__navButtonsLayout.addStretch()
+        
+        # TODO: add backward button
+        self.__backwardButton = QToolButton(self)
+        self.__backwardButton.setIcon(UI.PixmapCache.getIcon("back"))
+        self.__backwardButton.setToolTip(self.tr("Move one page backward"))
+        self.__backwardButton.setToolButtonStyle(
+            Qt.ToolButtonStyle.ToolButtonIconOnly)
+        self.__backwardButton.setAutoRaise(True)
+        self.__backwardButton.clicked.connect(self.__backward)
+        
+        # TODO: add forward button
+        self.__forwardButton = QToolButton(self)
+        self.__forwardButton.setIcon(UI.PixmapCache.getIcon("forward"))
+        self.__forwardButton.setToolTip(self.tr("Move one page forward"))
+        self.__forwardButton.setToolButtonStyle(
+            Qt.ToolButtonStyle.ToolButtonIconOnly)
+        self.__forwardButton.setAutoRaise(True)
+        self.__forwardButton.clicked.connect(self.__forward)
+        
+        self.__backForButtonLayout = QHBoxLayout()
+        self.__backForButtonLayout.setContentsMargins(0, 0, 0, 0)
+        self.__backForButtonLayout.setSpacing(0)
+        self.__backForButtonLayout.addWidget(self.__backwardButton)
+        self.__backForButtonLayout.addWidget(self.__forwardButton)
+        self.__navButtonsLayout.addLayout(self.__backForButtonLayout)
+        
+        # TODO: add reload button
+        self.__reloadButton = QToolButton(self)
+        self.__reloadButton.setIcon(UI.PixmapCache.getIcon("reload"))
+        self.__reloadButton.setToolTip(self.tr("Reload the current page"))
+        self.__reloadButton.clicked.connect(self.__reload)
+        self.__navButtonsLayout.addWidget(self.__reloadButton)
+        
+        # TODO: add zoom in button
+        # TODO: add zoom out button
+        # TODO: add zoom reset button
+        
+        self.__navButtonsLayout.addStretch()
+        
+        self.__layout.addLayout(self.__navButtonsLayout)
+                
+        self.__backMenu = QMenu(self)
+        self.__backMenu.triggered.connect(self.__navigationMenuActionTriggered)
+        self.__backwardButton.setMenu(self.__backMenu)
+        self.__backMenu.aboutToShow.connect(self.__showBackMenu)
+        
+        self.__forwardMenu = QMenu(self)
+        self.__forwardMenu.triggered.connect(
+            self.__navigationMenuActionTriggered)
+        self.__forwardButton.setMenu(self.__forwardMenu)
+        self.__forwardMenu.aboutToShow.connect(self.__showForwardMenu)
+
+        ###################################################################
+        
         self.__helpStack = QStackedWidget(self)
         self.__helpStack.setSizePolicy(
             QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
@@ -82,12 +159,10 @@
         
         self.__buttonLayout.addStretch()
         
-        self.__openPagesButton = QToolButton(self)
-        self.__openPagesButton.setIcon(UI.PixmapCache.getIcon("fileMisc"))
-        self.__openPagesButton.setToolTip(self.tr("Show list of open pages"))
-        self.__openPagesButton.setCheckable(True)
-        self.__buttonGroup.addButton(self.__openPagesButton)
-        self.__buttonLayout.addWidget(self.__openPagesButton)
+        self.__openPagesButton = self.__addNavigationButton(
+            "fileMisc", self.tr("Show list of open pages"))
+        
+        # TODO: add buttons for the QHelp related widgets
         
         self.__buttonLayout.addStretch()
         
@@ -99,13 +174,41 @@
         
         self.__openPagesButton.setChecked(True)
         
+        self.__initHelpEngine()
+        
+        self.__ui.preferencesChanged.connect(self.__populateHelpSelector)
+        
+        self.__initActionsMenu()
+        
         self.addPage()
+        self.__checkActionButtons(0)
+    
+    def __addNavigationButton(self, iconName, toolTip):
+        """
+        Private method to create and add a navigation button.
+        
+        @param iconName name of the icon
+        @type str
+        @param toolTip tooltip to be shown
+        @type str
+        @return reference to the created button
+        @rtype QToolButton
+        """
+        button = QToolButton(self)
+        button.setIcon(UI.PixmapCache.getIcon(iconName))
+        button.setToolTip(toolTip)
+        button.setCheckable(True)
+        self.__buttonGroup.addButton(button)
+        self.__buttonLayout.addWidget(button)
+        
+        return button
     
     def __populateNavigationStack(self):
         """
         Private method to populate the stack of navigation widgets.
         """
         self.__openPagesList = OpenPagesWidget(self.__helpStack, self)
+        self.__openPagesList.currentChanged.connect(self.__checkActionButtons)
         self.__helpNavigationStack.addWidget(self.__openPagesList)
         
         # TODO: not yet implemented
@@ -127,7 +230,33 @@
         """
         Private method to populate the help selection combo box.
         """
-        # TODO: not yet implemented
+        self.__helpSelector.clear()
+        
+        self.__helpSelector.addItem("", "")
+        
+        for key, topic in [
+            ("EricDocDir", self.tr("eric API Documentation")),
+            ("PythonDocDir", self.tr("Python 3 Documentation")),
+            ("Qt5DocDir", self.tr("Qt5 Documentation")),
+            ("Qt6DocDir", self.tr("Qt6 Documentation")),
+            ("PyQt5DocDir", self.tr("PyQt5 Documentation")),
+            ("PyQt6DocDir", self.tr("PyQt6 Documentation")),
+            ("PySide2DocDir", self.tr("PySide2 Documentation")),
+            ("PySide6DocDir", self.tr("PySide6 Documentation")),
+        ]:
+            urlStr = Preferences.getHelp(key)
+            if urlStr:
+                self.__helpSelector.addItem(topic, urlStr)
+    
+    @pyqtSlot()
+    def __helpTopicSelected(self):
+        """
+        Private slot handling the selection of a new help topic.
+        """
+        urlStr = self.__helpSelector.currentData()
+        if urlStr:
+            url = QUrl(urlStr)
+            self.currentViewer().setUrl(url)
     
     def searchQtHelp(self, searchExpression):
         """
@@ -155,7 +284,14 @@
         """
         Private slot to open a local help file (*.html).
         """
-        # TODO: not yet implemented
+        htmlFile = EricFileDialog.getOpenFileName(
+            self,
+            self.tr("Open HTML File"),
+            "",
+            self.tr("HTML Files (*.htm *.html);;All Files (*)")
+        )
+        if htmlFile:
+            self.currentViewer().setUrl(QUrl.fromLocalFile(htmlFile))
     
     def addPage(self, url=QUrl("about:blank")):
         """
@@ -164,14 +300,225 @@
         @param url requested URL (defaults to QUrl("about:blank"))
         @type QUrl (optional)
         """
-        try:
-            from .HelpViewerImpl_qwe import HelpViewerImpl_qwe
-            viewer = HelpViewerImpl_qwe(self)
-        except ImportError:
-            from .HelpViewerImpl_qtb import HelpViewerImpl_qtb
-            viewer = HelpViewerImpl_qtb(self)
+        viewer = self.__newViewer()
+        viewer.setUrl(url)
         
         self.__helpStack.addWidget(viewer)
         self.__openPagesList.addPage(viewer)
+    
+    def __newViewer(self):
+        """
+        Private method to create a new help viewer.
         
-        viewer.setSource(url, QTextDocument.ResourceType.HtmlResource)
+        @return help viewer
+        @rtype HelpViewerImpl
+        """
+        try:
+            from .HelpViewerImpl_qwe import HelpViewerImpl_qwe
+            viewer = HelpViewerImpl_qwe(self.__helpEngine, self)
+        except ImportError:
+            from .HelpViewerImpl_qtb import HelpViewerImpl_qtb
+            viewer = HelpViewerImpl_qtb(self.__helpEngine, self)
+        return viewer
+    
+    def currentViewer(self):
+        """
+        Public method to get the active viewer.
+        
+        @return reference to the active help viewer
+        @rtype HelpViewerImpl
+        """
+        return self.__helpStack.currentWidget()
+    
+    #######################################################################
+    ## QtHelp related code below
+    #######################################################################
+    
+    def __initHelpEngine(self):
+        """
+        Private method to initialize the QtHelp related stuff.
+        """
+        self.__helpEngine = QHelpEngine(
+            self.__getQtHelpCollectionFileName(),
+            self)
+        self.__helpEngine.setReadOnly(False)
+        self.__helpEngine.setupData()
+        self.__helpEngine.setUsesFilterEngine(True)
+        self.__removeOldDocumentation()
+        self.__helpEngine.warning.connect(self.__warning)
+    
+    def __getQtHelpCollectionFileName(cls):
+        """
+        Private method to determine the name of the QtHelp collection file.
+        
+        @return path of the QtHelp collection file
+        @rtype str
+        """
+        qthelpDir = os.path.join(Utilities.getConfigDir(), "qthelp")
+        if not os.path.exists(qthelpDir):
+            os.makedirs(qthelpDir)
+        return os.path.join(qthelpDir, "eric7help.qhc")
+    
+    @pyqtSlot(str)
+    def __warning(self, msg):
+        """
+        Private slot handling warnings of the help engine.
+        
+        @param msg message sent by the help  engine
+        @type str
+        """
+        EricMessageBox.warning(
+            self,
+            self.tr("Help Engine"), msg)
+    
+    @pyqtSlot()
+    def __removeOldDocumentation(self):
+        """
+        Private slot to remove non-existing documentation from the help engine.
+        """
+        for namespace in self.__helpEngine.registeredDocumentations():
+            docFile = self.__helpEngine.documentationFileName(namespace)
+            if not os.path.exists(docFile):
+                self.__helpEngine.unregisterDocumentation(namespace)
+    
+    @pyqtSlot()
+    def __manageQtHelpDocuments(self):
+        """
+        Private slot to manage the QtHelp documentation database.
+        """
+        from WebBrowser.QtHelp.QtHelpDocumentationConfigurationDialog import (
+            QtHelpDocumentationConfigurationDialog
+        )
+        dlg = QtHelpDocumentationConfigurationDialog(
+            self.__helpEngine, self)
+        dlg.exec()
+    
+    @pyqtSlot()
+    def __reindexDocumentation(self):
+        """
+        Private slot 
+        """
+    
+    #######################################################################
+    ## Actions Menu related methods
+    #######################################################################
+    
+    def __initActionsMenu(self):
+        """
+        Private method to initialize the actions menu.
+        """
+        self.__actionsMenu = QMenu()
+        self.__actionsMenu.setToolTipsVisible(True)
+        
+        self.__actionsMenu.addAction(self.tr("Manage QtHelp Documents"),
+                                     self.__manageQtHelpDocuments)
+        act = self.__actionsMenu.addAction(self.tr("Reindex Documentation"),
+                                           self.__reindexDocumentation)
+##        act.triggered.connect(self.__searchEngine.reindexDocumentation)
+        
+        self.__actionsButton.setMenu(self.__actionsMenu)
+    
+    #######################################################################
+    ## Navigation related methods below
+    #######################################################################
+    
+    @pyqtSlot()
+    def __backward(self):
+        """
+        Private slot to move one page backward.
+        """
+        cv = self.currentViewer()
+        if cv:
+            cv.backward()
+    
+    @pyqtSlot()
+    def __forward(self):
+        """
+        Private slot to move one page foreward.
+        """
+        cv = self.currentViewer()
+        if cv:
+            cv.forward()
+    
+    @pyqtSlot()
+    def __reload(self):
+        """
+        Private slot to reload the current page.
+        """
+        cv = self.currentViewer()
+        if cv:
+            cv.reload()
+    
+    @pyqtSlot(int)
+    def __checkActionButtons(self, row):
+        """
+        Private slot to set the enabled state of the action buttons.
+        
+        @param row index of the current page
+        @type int
+        """
+        cv = self.currentViewer()
+        self.__backwardButton.setEnabled(cv and cv.isBackwardAvailable())
+        self.__forwardButton.setEnabled(cv and cv.isForwardAvailable())
+    
+    def __showBackMenu(self):
+        """
+        Private slot showing the backward navigation menu.
+        """
+        cv = self.currentViewer()
+        if cv:
+            self.__backMenu.clear()
+            backwardHistoryCount = max(cv.backwardHistoryCount(), 20)
+            # show max. 20 items
+            
+            for index in range(1, backwardHistoryCount + 1):
+                act = QAction(self)
+                act.setData(-index)
+                act.setText(cv.historyTitle(-index))
+                self.__backMenu.addAction(act)
+            
+            self.__backMenu.addSeparator()
+            self.__backMenu.addAction(self.tr("Clear History"),
+                                      self.__clearHistory)
+    
+    def __showForwardMenu(self):
+        """
+        Private slot showing the forward navigation menu.
+        """
+        cv = self.currentViewer()
+        if cv:
+            self.__forwardMenu.clear()
+            forwardHistoryCount = max(cv.forwardHistoryCount(), 20)
+            # show max. 20 items
+        
+            for index in range(1, forwardHistoryCount + 1):
+                act = QAction(self)
+                act.setData(index)
+                act.setText(cv.historyTitle(index))
+                self.__forwardMenu.addAction(act)
+            
+            self.__forwardMenu.addSeparator()
+            self.__forwardMenu.addAction(self.tr("Clear History"),
+                                         self.__clearHistory)
+    
+    def __navigationMenuActionTriggered(self, act):
+        """
+        Private slot to go to the selected page.
+        
+        @param act reference to the action selected in the navigation menu
+        @type QAction
+        """
+        cv = self.currentViewer()
+        if cv:
+            index = act.data()
+            cv.gotoHistory(index)
+    
+    def __clearHistory(self):
+        """
+        Private slot to clear the history of the current viewer.
+        """
+        cb = self.__mw.currentBrowser()
+        if cb is not None:
+            cb.history().clear()
+            self.__mw.setForwardAvailable(cb.isForwardAvailable())
+            self.__mw.setBackwardAvailable(cb.isBackwardAvailable())

eric ide

mercurial