--- a/src/eric7/HelpViewer/HelpViewerImplQWE.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/HelpViewer/HelpViewerImplQWE.py Wed Jul 13 14:55:47 2022 +0200 @@ -26,17 +26,34 @@ """ Class implementing the QTextBrowser based help viewer class. """ + ZoomLevels = [ - 30, 40, 50, 67, 80, 90, + 30, + 40, + 50, + 67, + 80, + 90, 100, - 110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300, + 110, + 120, + 133, + 150, + 170, + 200, + 220, + 233, + 250, + 270, + 285, + 300, ] ZoomLevelDefault = 100 - + def __init__(self, engine, parent=None): """ Constructor - + @param engine reference to the help engine @type QHelpEngine @param parent reference to the parent widget @@ -44,44 +61,44 @@ """ QWebEngineView.__init__(self, parent=parent) HelpViewerImpl.__init__(self, engine) - + self.__helpViewerWidget = parent - + self.__rwhvqt = None self.installEventFilter(self) - + self.__page = None self.__createNewPage() - + self.__currentScale = 100 - + self.__menu = QMenu(self) - + def __createNewPage(self): """ Private method to create a new page object. """ self.__page = QWebEnginePage(self.__helpViewerWidget.webProfile()) self.setPage(self.__page) - + self.__page.titleChanged.connect(self.__titleChanged) self.__page.urlChanged.connect(self.__titleChanged) self.__page.newWindowRequested.connect(self.__newWindowRequested) - + def __newWindowRequested(self, request): """ Private slot handling new window requests of the web page. - + @param request reference to the new window request @type QWebEngineNewWindowRequest """ background = ( - request.destination() == - QWebEngineNewWindowRequest.DestinationType.InNewBackgroundTab + request.destination() + == QWebEngineNewWindowRequest.DestinationType.InNewBackgroundTab ) newViewer = self.__helpViewerWidget.addPage(background=background) request.openIn(newViewer.page()) - + def __setRwhvqt(self): """ Private slot to set widget that receives input events. @@ -92,12 +109,12 @@ self.__rwhvqt.grabGesture(Qt.GestureType.PinchGesture) self.__rwhvqt.installEventFilter(self) else: - print("Focus proxy is null!") # __IGNORE_WARNING_M801__ - + print("Focus proxy is null!") # __IGNORE_WARNING_M801__ + def setLink(self, url): """ Public method to set the URL of the document to be shown. - + @param url URL of the document @type QUrl """ @@ -105,27 +122,27 @@ self.setHtml(self.__helpViewerWidget.emptyDocument()) else: super().setUrl(url) - + def link(self): """ Public method to get the URL of the shown document. - + @return url URL of the document @rtype QUrl """ return super().url() - + @pyqtSlot() def __titleChanged(self): """ Private method to handle a change of the web page title. """ super().titleChanged.emit() - + def pageTitle(self): """ Public method get the page title. - + @return page title @rtype str """ @@ -135,98 +152,97 @@ url = self.__page.requestedUrl() else: url = self.link() - + titleStr = url.host() if not titleStr: - titleStr = url.toString( - QUrl.UrlFormattingOption.RemoveFragment) - + titleStr = url.toString(QUrl.UrlFormattingOption.RemoveFragment) + if not titleStr or titleStr == "about:blank": titleStr = self.tr("Empty Page") - + return titleStr - + def isEmptyPage(self): """ Public method to check, if the current page is the empty page. - + @return flag indicating an empty page is loaded @rtype bool """ return self.pageTitle() == self.tr("Empty Page") - + ####################################################################### ## History related methods below ####################################################################### - + def isBackwardAvailable(self): """ Public method to check, if stepping backward through the history is available. - + @return flag indicating backward stepping is available @rtype bool """ return self.history().canGoBack() - + def isForwardAvailable(self): """ Public method to check, if stepping forward through the history is available. - + @return flag indicating forward stepping is available @rtype bool """ return self.history().canGoForward() - + def backward(self): """ Public slot to move backwards in history. """ self.triggerPageAction(QWebEnginePage.WebAction.Back) - + def forward(self): """ Public slot to move forward in history. """ self.triggerPageAction(QWebEnginePage.WebAction.Forward) - + def reload(self): """ Public slot to reload the current page. """ self.triggerPageAction(QWebEnginePage.WebAction.Reload) - + def backwardHistoryCount(self): """ Public method to get the number of available back history items. - + Note: For performance reasons this is limited to the maximum number of history items the help viewer is interested in. - + @return count of available back history items @rtype int """ history = self.history() return len(history.backItems(HelpViewerWidget.MaxHistoryItems)) - + def forwardHistoryCount(self): """ Public method to get the number of available forward history items. - + Note: For performance reasons this is limited to the maximum number of history items the help viewer is interested in. - + @return count of available forward history items @rtype int """ history = self.history() return len(history.forwardItems(HelpViewerWidget.MaxHistoryItems)) - + def historyTitle(self, offset): """ Public method to get the title of a history item. - + @param offset offset of the item with respect to the current page @type int @return title of the requeted item in history @@ -236,11 +252,11 @@ currentIndex = history.currentItemIndex() itm = self.history().itemAt(currentIndex + offset) return itm.title() - + def gotoHistory(self, offset): """ Public method to go to a history item. - + @param offset offset of the item with respect to the current page @type int """ @@ -248,21 +264,21 @@ currentIndex = history.currentItemIndex() itm = self.history().itemAt(currentIndex + offset) history.goToItem(itm) - + def clearHistory(self): """ Public method to clear the history. """ self.history().clear() - + ####################################################################### ## Zoom related methods below ####################################################################### - + def __levelForScale(self, scale): """ Private method determining the zoom level index given a zoom factor. - + @param scale zoom factor @type int @return index of zoom factor @@ -275,7 +291,7 @@ if scale <= self.ZoomLevels[scale]: break return index - + def scaleUp(self): """ Public method to zoom in. @@ -283,7 +299,7 @@ index = self.__levelForScale(self.__currentScale) if index < len(self.ZoomLevels) - 1: self.setScale(self.ZoomLevels[index + 1]) - + def scaleDown(self): """ Public method to zoom out. @@ -291,11 +307,11 @@ index = self.__levelForScale(self.__currentScale) if index > 0: self.setScale(self.ZoomLevels[index - 1]) - + def setScale(self, scale): """ Public method to set the zoom level. - + @param scale zoom level to set @type int """ @@ -303,51 +319,51 @@ self.setZoomFactor(scale / 100.0) self.__currentScale = scale self.zoomChanged.emit() - + def resetScale(self): """ Public method to reset the zoom level. """ index = self.__levelForScale(self.ZoomLevelDefault) self.setScale(self.ZoomLevels[index]) - + def scale(self): """ Public method to get the zoom level. - + @return current zoom level @rtype int """ return self.__currentScale - + def isScaleUpAvailable(self): """ Public method to check, if the max. zoom level is reached. - + @return flag indicating scale up is available @rtype bool """ index = self.__levelForScale(self.__currentScale) return index < len(self.ZoomLevels) - 1 - + def isScaleDownAvailable(self): """ Public method to check, if the min. zoom level is reached. - + @return flag indicating scale down is available @rtype bool """ index = self.__levelForScale(self.__currentScale) return index > 0 - + ####################################################################### ## Event handlers below ####################################################################### - + def eventFilter(self, obj, evt): """ Public method to process event for other objects. - + @param obj reference to object to process events for @type QObject @param evt reference to event to be processed @@ -356,24 +372,23 @@ @rtype bool """ if ( - obj is self and - evt.type() == QEvent.Type.ParentChange and - self.parentWidget() is not None + obj is self + and evt.type() == QEvent.Type.ParentChange + and self.parentWidget() is not None ): self.parentWidget().installEventFilter(self) - + # find the render widget receiving events for the web page if obj is self and evt.type() == QEvent.Type.ChildAdded: QTimer.singleShot(0, self.__setRwhvqt) - + # forward events to WebBrowserView - if ( - obj is self.__rwhvqt and - evt.type() in [QEvent.Type.KeyPress, - QEvent.Type.MouseButtonRelease, - QEvent.Type.Wheel, - QEvent.Type.Gesture] - ): + if obj is self.__rwhvqt and evt.type() in [ + QEvent.Type.KeyPress, + QEvent.Type.MouseButtonRelease, + QEvent.Type.Wheel, + QEvent.Type.Gesture, + ]: wasAccepted = evt.isAccepted() evt.setAccepted(False) if evt.type() == QEvent.Type.KeyPress: @@ -387,11 +402,11 @@ ret = evt.isAccepted() evt.setAccepted(wasAccepted) return ret - - if ( - obj is self.parentWidget() and - evt.type() in [QEvent.Type.KeyPress, QEvent.Type.KeyRelease] - ): + + if obj is self.parentWidget() and evt.type() in [ + QEvent.Type.KeyPress, + QEvent.Type.KeyRelease, + ]: wasAccepted = evt.isAccepted() evt.setAccepted(False) if evt.type() == QEvent.Type.KeyPress: @@ -399,49 +414,41 @@ ret = evt.isAccepted() evt.setAccepted(wasAccepted) return ret - + # block already handled events - if ( - obj is self and - evt.type() in [QEvent.Type.KeyPress, - QEvent.Type.MouseButtonRelease, - QEvent.Type.Wheel, - QEvent.Type.Gesture] - ): + if obj is self and evt.type() in [ + QEvent.Type.KeyPress, + QEvent.Type.MouseButtonRelease, + QEvent.Type.Wheel, + QEvent.Type.Gesture, + ]: return True - + return super().eventFilter(obj, evt) - + def _keyPressEvent(self, evt): """ Protected method called by a key press. - + @param evt reference to the key event @type QKeyEvent """ key = evt.key() - isControlModifier = ( - evt.modifiers() == Qt.KeyboardModifier.ControlModifier - ) - - if ( - key == Qt.Key.Key_ZoomIn or - (key == Qt.Key.Key_Plus and isControlModifier) - ): + isControlModifier = evt.modifiers() == Qt.KeyboardModifier.ControlModifier + + if key == Qt.Key.Key_ZoomIn or (key == Qt.Key.Key_Plus and isControlModifier): self.scaleUp() evt.accept() - elif ( - key == Qt.Key.Key_ZoomOut or - (key == Qt.Key.Key_Minus and isControlModifier) + elif key == Qt.Key.Key_ZoomOut or ( + key == Qt.Key.Key_Minus and isControlModifier ): self.scaleDown() evt.accept() elif key == Qt.Key.Key_0 and isControlModifier: self.resetScale() evt.accept() - elif ( - key == Qt.Key.Key_Backspace or - (key == Qt.Key.Key_Left and isControlModifier) + elif key == Qt.Key.Key_Backspace or ( + key == Qt.Key.Key_Left and isControlModifier ): self.backward() evt.accept() @@ -451,47 +458,36 @@ elif key == Qt.Key.Key_F and isControlModifier: self.__helpViewerWidget.showHideSearch(True) evt.accept() - elif ( - key == Qt.Key.Key_F3 and - evt.modifiers() == Qt.KeyboardModifier.NoModifier - ): + elif key == Qt.Key.Key_F3 and evt.modifiers() == Qt.KeyboardModifier.NoModifier: self.__helpViewerWidget.searchNext() evt.accept() elif ( - key == Qt.Key.Key_F3 and - evt.modifiers() == Qt.KeyboardModifier.ShiftModifier + key == Qt.Key.Key_F3 + and evt.modifiers() == Qt.KeyboardModifier.ShiftModifier ): self.__helpViewerWidget.searchPrev() evt.accept() - + def _mouseReleaseEvent(self, evt): """ Protected method called by a mouse release event. - + @param evt reference to the mouse event @type QMouseEvent """ accepted = evt.isAccepted() self.__page.event(evt) - if ( - not evt.isAccepted() and - evt.button() == Qt.MouseButton.MiddleButton - ): - url = QUrl(QGuiApplication.clipboard().text( - QClipboard.Mode.Selection)) - if ( - not url.isEmpty() and - url.isValid() and - url.scheme() != "" - ): + if not evt.isAccepted() and evt.button() == Qt.MouseButton.MiddleButton: + url = QUrl(QGuiApplication.clipboard().text(QClipboard.Mode.Selection)) + if not url.isEmpty() and url.isValid() and url.scheme() != "": self.setLink(url) accepted = True evt.setAccepted(accepted) - + def _wheelEvent(self, evt): """ Protected method to handle wheel events. - + @param evt reference to the wheel event @type QWheelEvent """ @@ -502,18 +498,18 @@ elif delta > 0: self.scaleUp() evt.accept() - + elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: if delta < 0: self.backward() elif delta > 0: self.forward() evt.accept() - + def _gestureEvent(self, evt): """ Protected method handling gesture events. - + @param evt reference to the gesture event @type QGestureEvent """ @@ -525,156 +521,145 @@ scaleFactor = pinch.totalScaleFactor() self.setScale(int(scaleFactor * 100)) evt.accept() - + def event(self, evt): """ Public method handling events. - + @param evt reference to the event (QEvent) @return flag indicating, if the event was handled (boolean) """ if evt.type() == QEvent.Type.Gesture: self._gestureEvent(evt) return True - + return super().event(evt) - + ####################################################################### ## Context menu related methods below ####################################################################### - + def contextMenuEvent(self, evt): """ Protected method called to create a context menu. - + This method is overridden from QWebEngineView. - + @param evt reference to the context menu event object @type QContextMenuEvent """ pos = evt.pos() reason = evt.reason() QTimer.singleShot( - 0, - lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos))) + 0, lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos)) + ) # needs to be done this way because contextMenuEvent is blocking # the main loop - + def _contextMenuEvent(self, evt): """ Protected method called to create a context menu. - + @param evt reference to the context menu event object (QContextMenuEvent) """ self.__menu.clear() - + self.__createContextMenu(self.__menu) - + if not self.__menu.isEmpty(): pos = evt.globalPos() self.__menu.popup(QPoint(pos.x(), pos.y() + 1)) - + def __createContextMenu(self, menu): """ Private method to populate the context menu. - + @param menu reference to the menu to be populated @type QMenu """ contextMenuData = self.lastContextMenuRequest() - + act = menu.addAction( - UI.PixmapCache.getIcon("back"), - self.tr("Backward"), - self.backward) + UI.PixmapCache.getIcon("back"), self.tr("Backward"), self.backward + ) act.setEnabled(self.isBackwardAvailable()) - + act = menu.addAction( - UI.PixmapCache.getIcon("forward"), - self.tr("Forward"), - self.forward) + UI.PixmapCache.getIcon("forward"), self.tr("Forward"), self.forward + ) act.setEnabled(self.isForwardAvailable()) - + act = menu.addAction( - UI.PixmapCache.getIcon("reload"), - self.tr("Reload"), - self.reload) - + UI.PixmapCache.getIcon("reload"), self.tr("Reload"), self.reload + ) + if ( - not contextMenuData.linkUrl().isEmpty() and - contextMenuData.linkUrl().scheme() != "javascript" + not contextMenuData.linkUrl().isEmpty() + and contextMenuData.linkUrl().scheme() != "javascript" ): self.__createLinkContextMenu(menu, contextMenuData) - + menu.addSeparator() - - act = menu.addAction( - UI.PixmapCache.getIcon("editCopy"), - self.tr("Copy Page URL to Clipboard")) - act.setData(self.link()) - act.triggered.connect( - functools.partial(self.__copyLink, act)) - + act = menu.addAction( - UI.PixmapCache.getIcon("bookmark22"), - self.tr("Bookmark Page")) - act.setData({ - "title": self.pageTitle(), - "url": self.link() - }) - act.triggered.connect( - functools.partial(self.__bookmarkPage, act)) - + UI.PixmapCache.getIcon("editCopy"), self.tr("Copy Page URL to Clipboard") + ) + act.setData(self.link()) + act.triggered.connect(functools.partial(self.__copyLink, act)) + + act = menu.addAction( + UI.PixmapCache.getIcon("bookmark22"), self.tr("Bookmark Page") + ) + act.setData({"title": self.pageTitle(), "url": self.link()}) + act.triggered.connect(functools.partial(self.__bookmarkPage, act)) + menu.addSeparator() - + act = menu.addAction( - UI.PixmapCache.getIcon("zoomIn"), - self.tr("Zoom in"), - self.scaleUp) + UI.PixmapCache.getIcon("zoomIn"), self.tr("Zoom in"), self.scaleUp + ) act.setEnabled(self.isScaleUpAvailable()) - + act = menu.addAction( - UI.PixmapCache.getIcon("zoomOut"), - self.tr("Zoom out"), - self.scaleDown) + UI.PixmapCache.getIcon("zoomOut"), self.tr("Zoom out"), self.scaleDown + ) act.setEnabled(self.isScaleDownAvailable()) - + menu.addAction( - UI.PixmapCache.getIcon("zoomReset"), - self.tr("Zoom reset"), - self.resetScale) - + UI.PixmapCache.getIcon("zoomReset"), self.tr("Zoom reset"), self.resetScale + ) + menu.addSeparator() - + act = menu.addAction( - UI.PixmapCache.getIcon("editCopy"), - self.tr("Copy"), - self.__copyText) + UI.PixmapCache.getIcon("editCopy"), self.tr("Copy"), self.__copyText + ) act.setEnabled(bool(contextMenuData.selectedText())) - + menu.addAction( UI.PixmapCache.getIcon("editSelectAll"), self.tr("Select All"), - self.__selectAll) - + self.__selectAll, + ) + menu.addSeparator() - + menu.addAction( - UI.PixmapCache.getIcon("tabClose"), - self.tr('Close'), - self.__closePage) - + UI.PixmapCache.getIcon("tabClose"), self.tr("Close"), self.__closePage + ) + act = menu.addAction( UI.PixmapCache.getIcon("tabCloseOther"), self.tr("Close Others"), - self.__closeOtherPages) + self.__closeOtherPages, + ) act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1) - + def __createLinkContextMenu(self, menu, contextMenuData): """ Private method to populate the context menu for URLs. - + @param menu reference to the menu to be populated @type QMenu @param contextMenuData data of the last context menu request @@ -682,61 +667,58 @@ """ if not menu.isEmpty(): menu.addSeparator() - + act = menu.addAction( - UI.PixmapCache.getIcon("openNewTab"), - self.tr("Open Link in New Page")) + UI.PixmapCache.getIcon("openNewTab"), self.tr("Open Link in New Page") + ) act.setData(contextMenuData.linkUrl()) - act.triggered.connect( - functools.partial(self.__openLinkInNewPage, act)) - + act.triggered.connect(functools.partial(self.__openLinkInNewPage, act)) + act = menu.addAction( - UI.PixmapCache.getIcon("newWindow"), - self.tr("Open Link in Background Page")) + UI.PixmapCache.getIcon("newWindow"), self.tr("Open Link in Background Page") + ) act.setData(contextMenuData.linkUrl()) - act.triggered.connect( - functools.partial(self.__openLinkInBackgroundPage, act)) - + act.triggered.connect(functools.partial(self.__openLinkInBackgroundPage, act)) + menu.addSeparator() - + act = menu.addAction( - UI.PixmapCache.getIcon("editCopy"), - self.tr("Copy URL to Clipboard")) + UI.PixmapCache.getIcon("editCopy"), self.tr("Copy URL to Clipboard") + ) act.setData(contextMenuData.linkUrl()) - act.triggered.connect( - functools.partial(self.__copyLink, act)) - + act.triggered.connect(functools.partial(self.__copyLink, act)) + def __openLinkInNewPage(self, act): """ Private method called by the context menu to open a link in a new page. - + @param act reference to the action that triggered @type QAction """ url = act.data() if url.isEmpty(): return - + self.__helpViewerWidget.openUrlNewPage(url) - + def __openLinkInBackgroundPage(self, act): """ Private method called by the context menu to open a link in a background page. - + @param act reference to the action that triggered @type QAction """ url = act.data() if url.isEmpty(): return - + self.__helpViewerWidget.openUrlNewBackgroundPage(url) - + def __bookmarkPage(self, act): """ Private method called by the context menu to bookmark the page. - + @param act reference to the action that triggered @type QAction """ @@ -745,47 +727,47 @@ with contextlib.suppress(KeyError): url = data["url"] title = data["title"] - + self.__helpViewerWidget.bookmarkPage(title, url) - + def __copyLink(self, act): """ Private method called by the context menu to copy a link to the clipboard. - + @param act reference to the action that triggered @type QAction """ data = act.data() if isinstance(data, QUrl) and data.isEmpty(): return - + if isinstance(data, QUrl): data = data.toString() - + # copy the URL to both clipboard areas QGuiApplication.clipboard().setText(data, QClipboard.Mode.Clipboard) QGuiApplication.clipboard().setText(data, QClipboard.Mode.Selection) - + def __copyText(self): """ Private method called by the context menu to copy selected text to the clipboard. """ self.triggerPageAction(QWebEnginePage.WebAction.Copy) - + def __selectAll(self): """ Private method called by the context menu to select all text. """ self.triggerPageAction(QWebEnginePage.WebAction.SelectAll) - + def __closePage(self): """ Private method called by the context menu to close the current page. """ self.__helpViewerWidget.closeCurrentPage() - + def __closeOtherPages(self): """ Private method called by the context menu to close all other pages.