|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the help viewer base class. |
|
8 """ |
|
9 |
|
10 import functools |
|
11 |
|
12 from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QTimer, QUrl, QPoint |
|
13 from PyQt6.QtGui import QGuiApplication, QClipboard, QContextMenuEvent |
|
14 from PyQt6.QtWidgets import QMenu |
|
15 from PyQt6.QtWebEngineWidgets import QWebEngineView |
|
16 from PyQt6.QtWebEngineCore import QWebEnginePage, QWebEngineNewWindowRequest |
|
17 |
|
18 from .HelpViewerWidget import HelpViewerWidget |
|
19 from .HelpViewerImpl import HelpViewerImpl |
|
20 |
|
21 import UI.PixmapCache |
|
22 |
|
23 |
|
24 class HelpViewerImplQWE(HelpViewerImpl, QWebEngineView): |
|
25 """ |
|
26 Class implementing the QTextBrowser based help viewer class. |
|
27 """ |
|
28 ZoomLevels = [ |
|
29 30, 40, 50, 67, 80, 90, |
|
30 100, |
|
31 110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300, |
|
32 ] |
|
33 ZoomLevelDefault = 100 |
|
34 |
|
35 def __init__(self, engine, parent=None): |
|
36 """ |
|
37 Constructor |
|
38 |
|
39 @param engine reference to the help engine |
|
40 @type QHelpEngine |
|
41 @param parent reference to the parent widget |
|
42 @type QWidget |
|
43 """ |
|
44 QWebEngineView.__init__(self, parent=parent) |
|
45 HelpViewerImpl.__init__(self, engine) |
|
46 |
|
47 self.__helpViewerWidget = parent |
|
48 |
|
49 self.__rwhvqt = None |
|
50 self.installEventFilter(self) |
|
51 |
|
52 self.__page = None |
|
53 self.__createNewPage() |
|
54 |
|
55 self.__currentScale = 100 |
|
56 |
|
57 self.__menu = QMenu(self) |
|
58 |
|
59 def __createNewPage(self): |
|
60 """ |
|
61 Private method to create a new page object. |
|
62 """ |
|
63 self.__page = QWebEnginePage(self.__helpViewerWidget.webProfile()) |
|
64 self.setPage(self.__page) |
|
65 |
|
66 self.__page.titleChanged.connect(self.__titleChanged) |
|
67 self.__page.urlChanged.connect(self.__titleChanged) |
|
68 self.__page.newWindowRequested.connect(self.__newWindowRequested) |
|
69 |
|
70 def __newWindowRequested(self, request): |
|
71 """ |
|
72 Private slot handling new window requests of the web page. |
|
73 |
|
74 @param request reference to the new window request |
|
75 @type QWebEngineNewWindowRequest |
|
76 """ |
|
77 background = ( |
|
78 request.destination() == |
|
79 QWebEngineNewWindowRequest.DestinationType.InNewBackgroundTab |
|
80 ) |
|
81 newViewer = self.__helpViewerWidget.addPage(background=background) |
|
82 request.openIn(newViewer.page()) |
|
83 |
|
84 def __setRwhvqt(self): |
|
85 """ |
|
86 Private slot to set widget that receives input events. |
|
87 """ |
|
88 self.grabGesture(Qt.GestureType.PinchGesture) |
|
89 self.__rwhvqt = self.focusProxy() |
|
90 if self.__rwhvqt: |
|
91 self.__rwhvqt.grabGesture(Qt.GestureType.PinchGesture) |
|
92 self.__rwhvqt.installEventFilter(self) |
|
93 else: |
|
94 print("Focus proxy is null!") # __IGNORE_WARNING_M801__ |
|
95 |
|
96 def setLink(self, url): |
|
97 """ |
|
98 Public method to set the URL of the document to be shown. |
|
99 |
|
100 @param url URL of the document |
|
101 @type QUrl |
|
102 """ |
|
103 super().setUrl(url) |
|
104 |
|
105 def link(self): |
|
106 """ |
|
107 Public method to get the URL of the shown document. |
|
108 |
|
109 @return url URL of the document |
|
110 @rtype QUrl |
|
111 """ |
|
112 return super().url() |
|
113 |
|
114 @pyqtSlot() |
|
115 def __titleChanged(self): |
|
116 """ |
|
117 Private method to handle a change of the web page title. |
|
118 """ |
|
119 super().titleChanged.emit() |
|
120 |
|
121 def pageTitle(self): |
|
122 """ |
|
123 Public method get the page title. |
|
124 |
|
125 @return page title |
|
126 @rtype str |
|
127 """ |
|
128 titleStr = super().title() |
|
129 if not titleStr: |
|
130 if self.link().isEmpty(): |
|
131 url = self.__page.requestedUrl() |
|
132 else: |
|
133 url = self.link() |
|
134 |
|
135 titleStr = url.host() |
|
136 if not titleStr: |
|
137 titleStr = url.toString( |
|
138 QUrl.UrlFormattingOption.RemoveFragment) |
|
139 |
|
140 if not titleStr or titleStr == "about:blank": |
|
141 titleStr = self.tr("Empty Page") |
|
142 |
|
143 return titleStr |
|
144 |
|
145 ####################################################################### |
|
146 ## History related methods below |
|
147 ####################################################################### |
|
148 |
|
149 def isBackwardAvailable(self): |
|
150 """ |
|
151 Public method to check, if stepping backward through the history is |
|
152 available. |
|
153 |
|
154 @return flag indicating backward stepping is available |
|
155 @rtype bool |
|
156 """ |
|
157 return self.history().canGoBack() |
|
158 |
|
159 def isForwardAvailable(self): |
|
160 """ |
|
161 Public method to check, if stepping forward through the history is |
|
162 available. |
|
163 |
|
164 @return flag indicating forward stepping is available |
|
165 @rtype bool |
|
166 """ |
|
167 return self.history().canGoForward() |
|
168 |
|
169 def backward(self): |
|
170 """ |
|
171 Public slot to move backwards in history. |
|
172 """ |
|
173 self.triggerPageAction(QWebEnginePage.WebAction.Back) |
|
174 |
|
175 def forward(self): |
|
176 """ |
|
177 Public slot to move forward in history. |
|
178 """ |
|
179 self.triggerPageAction(QWebEnginePage.WebAction.Forward) |
|
180 |
|
181 def reload(self): |
|
182 """ |
|
183 Public slot to reload the current page. |
|
184 """ |
|
185 self.triggerPageAction(QWebEnginePage.WebAction.Reload) |
|
186 |
|
187 def backwardHistoryCount(self): |
|
188 """ |
|
189 Public method to get the number of available back history items. |
|
190 |
|
191 Note: For performance reasons this is limited to the maximum number of |
|
192 history items the help viewer is interested in. |
|
193 |
|
194 @return count of available back history items |
|
195 @rtype int |
|
196 """ |
|
197 history = self.history() |
|
198 return len(history.backItems(HelpViewerWidget.MaxHistoryItems)) |
|
199 |
|
200 def forwardHistoryCount(self): |
|
201 """ |
|
202 Public method to get the number of available forward history items. |
|
203 |
|
204 Note: For performance reasons this is limited to the maximum number of |
|
205 history items the help viewer is interested in. |
|
206 |
|
207 @return count of available forward history items |
|
208 @rtype int |
|
209 """ |
|
210 history = self.history() |
|
211 return len(history.forwardItems(HelpViewerWidget.MaxHistoryItems)) |
|
212 |
|
213 def historyTitle(self, offset): |
|
214 """ |
|
215 Public method to get the title of a history item. |
|
216 |
|
217 @param offset offset of the item with respect to the current page |
|
218 @type int |
|
219 @return title of the requeted item in history |
|
220 @rtype str |
|
221 """ |
|
222 history = self.history() |
|
223 currentIndex = history.currentItemIndex() |
|
224 itm = self.history().itemAt(currentIndex + offset) |
|
225 return itm.title() |
|
226 |
|
227 def gotoHistory(self, offset): |
|
228 """ |
|
229 Public method to go to a history item. |
|
230 |
|
231 @param offset offset of the item with respect to the current page |
|
232 @type int |
|
233 """ |
|
234 history = self.history() |
|
235 currentIndex = history.currentItemIndex() |
|
236 itm = self.history().itemAt(currentIndex + offset) |
|
237 history.goToItem(itm) |
|
238 |
|
239 def clearHistory(self): |
|
240 """ |
|
241 Public method to clear the history. |
|
242 """ |
|
243 self.history().clear() |
|
244 |
|
245 ####################################################################### |
|
246 ## Zoom related methods below |
|
247 ####################################################################### |
|
248 |
|
249 def __levelForScale(self, scale): |
|
250 """ |
|
251 Private method determining the zoom level index given a zoom factor. |
|
252 |
|
253 @param scale zoom factor |
|
254 @type int |
|
255 @return index of zoom factor |
|
256 @rtype int |
|
257 """ |
|
258 try: |
|
259 index = self.ZoomLevels.index(scale) |
|
260 except ValueError: |
|
261 for _index in range(len(self.ZoomLevels)): |
|
262 if scale <= self.ZoomLevels[scale]: |
|
263 break |
|
264 return index |
|
265 |
|
266 def scaleUp(self): |
|
267 """ |
|
268 Public method to zoom in. |
|
269 """ |
|
270 index = self.__levelForScale(self.__currentScale) |
|
271 if index < len(self.ZoomLevels) - 1: |
|
272 self.setScale(self.ZoomLevels[index + 1]) |
|
273 |
|
274 def scaleDown(self): |
|
275 """ |
|
276 Public method to zoom out. |
|
277 """ |
|
278 index = self.__levelForScale(self.__currentScale) |
|
279 if index > 0: |
|
280 self.setScale(self.ZoomLevels[index - 1]) |
|
281 |
|
282 def setScale(self, scale): |
|
283 """ |
|
284 Public method to set the zoom level. |
|
285 |
|
286 @param scale zoom level to set |
|
287 @type int |
|
288 """ |
|
289 if scale != self.__currentScale: |
|
290 self.setZoomFactor(scale / 100.0) |
|
291 self.__currentScale = scale |
|
292 self.zoomChanged.emit() |
|
293 |
|
294 def resetScale(self): |
|
295 """ |
|
296 Public method to reset the zoom level. |
|
297 """ |
|
298 index = self.__levelForScale(self.ZoomLevelDefault) |
|
299 self.setScale(self.ZoomLevels[index]) |
|
300 |
|
301 def scale(self): |
|
302 """ |
|
303 Public method to get the zoom level. |
|
304 |
|
305 @return current zoom level |
|
306 @rtype int |
|
307 """ |
|
308 return self.__currentScale |
|
309 |
|
310 def isScaleUpAvailable(self): |
|
311 """ |
|
312 Public method to check, if the max. zoom level is reached. |
|
313 |
|
314 @return flag indicating scale up is available |
|
315 @rtype bool |
|
316 """ |
|
317 index = self.__levelForScale(self.__currentScale) |
|
318 return index < len(self.ZoomLevels) - 1 |
|
319 |
|
320 def isScaleDownAvailable(self): |
|
321 """ |
|
322 Public method to check, if the min. zoom level is reached. |
|
323 |
|
324 @return flag indicating scale down is available |
|
325 @rtype bool |
|
326 """ |
|
327 index = self.__levelForScale(self.__currentScale) |
|
328 return index > 0 |
|
329 |
|
330 ####################################################################### |
|
331 ## Event handlers below |
|
332 ####################################################################### |
|
333 |
|
334 def eventFilter(self, obj, evt): |
|
335 """ |
|
336 Public method to process event for other objects. |
|
337 |
|
338 @param obj reference to object to process events for |
|
339 @type QObject |
|
340 @param evt reference to event to be processed |
|
341 @type QEvent |
|
342 @return flag indicating that the event should be filtered out |
|
343 @rtype bool |
|
344 """ |
|
345 if ( |
|
346 obj is self and |
|
347 evt.type() == QEvent.Type.ParentChange and |
|
348 self.parentWidget() is not None |
|
349 ): |
|
350 self.parentWidget().installEventFilter(self) |
|
351 |
|
352 # find the render widget receiving events for the web page |
|
353 if obj is self and evt.type() == QEvent.Type.ChildAdded: |
|
354 QTimer.singleShot(0, self.__setRwhvqt) |
|
355 |
|
356 # forward events to WebBrowserView |
|
357 if ( |
|
358 obj is self.__rwhvqt and |
|
359 evt.type() in [QEvent.Type.KeyPress, |
|
360 QEvent.Type.MouseButtonRelease, |
|
361 QEvent.Type.Wheel, |
|
362 QEvent.Type.Gesture] |
|
363 ): |
|
364 wasAccepted = evt.isAccepted() |
|
365 evt.setAccepted(False) |
|
366 if evt.type() == QEvent.Type.KeyPress: |
|
367 self._keyPressEvent(evt) |
|
368 elif evt.type() == QEvent.Type.MouseButtonRelease: |
|
369 self._mouseReleaseEvent(evt) |
|
370 elif evt.type() == QEvent.Type.Wheel: |
|
371 self._wheelEvent(evt) |
|
372 elif evt.type() == QEvent.Type.Gesture: |
|
373 self._gestureEvent(evt) |
|
374 ret = evt.isAccepted() |
|
375 evt.setAccepted(wasAccepted) |
|
376 return ret |
|
377 |
|
378 if ( |
|
379 obj is self.parentWidget() and |
|
380 evt.type() in [QEvent.Type.KeyPress, QEvent.Type.KeyRelease] |
|
381 ): |
|
382 wasAccepted = evt.isAccepted() |
|
383 evt.setAccepted(False) |
|
384 if evt.type() == QEvent.Type.KeyPress: |
|
385 self._keyPressEvent(evt) |
|
386 ret = evt.isAccepted() |
|
387 evt.setAccepted(wasAccepted) |
|
388 return ret |
|
389 |
|
390 # block already handled events |
|
391 if ( |
|
392 obj is self and |
|
393 evt.type() in [QEvent.Type.KeyPress, |
|
394 QEvent.Type.MouseButtonRelease, |
|
395 QEvent.Type.Wheel, |
|
396 QEvent.Type.Gesture] |
|
397 ): |
|
398 return True |
|
399 |
|
400 return super().eventFilter(obj, evt) |
|
401 |
|
402 def _keyPressEvent(self, evt): |
|
403 """ |
|
404 Protected method called by a key press. |
|
405 |
|
406 @param evt reference to the key event |
|
407 @type QKeyEvent |
|
408 """ |
|
409 key = evt.key() |
|
410 isControlModifier = bool( |
|
411 evt.modifiers() & Qt.KeyboardModifier.ControlModifier) |
|
412 |
|
413 if ( |
|
414 key == Qt.Key.Key_ZoomIn or |
|
415 (key == Qt.Key.Key_Plus and isControlModifier) |
|
416 ): |
|
417 self.scaleUp() |
|
418 evt.accept() |
|
419 elif ( |
|
420 key == Qt.Key.Key_ZoomOut or |
|
421 (key == Qt.Key.Key_Minus and isControlModifier) |
|
422 ): |
|
423 self.scaleDown() |
|
424 evt.accept() |
|
425 elif key == Qt.Key.Key_0 and isControlModifier: |
|
426 self.resetScale() |
|
427 evt.accept() |
|
428 elif ( |
|
429 key == Qt.Key.Key_Backspace or |
|
430 (key == Qt.Key.Key_Left and isControlModifier) |
|
431 ): |
|
432 self.backward() |
|
433 evt.accept() |
|
434 elif key == Qt.Key.Key_Right and isControlModifier: |
|
435 self.forward() |
|
436 evt.accept() |
|
437 |
|
438 def _mouseReleaseEvent(self, evt): |
|
439 """ |
|
440 Protected method called by a mouse release event. |
|
441 |
|
442 @param evt reference to the mouse event |
|
443 @type QMouseEvent |
|
444 """ |
|
445 accepted = evt.isAccepted() |
|
446 self.__page.event(evt) |
|
447 if ( |
|
448 not evt.isAccepted() and |
|
449 evt.button() == Qt.MouseButton.MiddleButton |
|
450 ): |
|
451 url = QUrl(QGuiApplication.clipboard().text( |
|
452 QClipboard.Mode.Selection)) |
|
453 if ( |
|
454 not url.isEmpty() and |
|
455 url.isValid() and |
|
456 url.scheme() != "" |
|
457 ): |
|
458 self.setLink(url) |
|
459 accepted = True |
|
460 evt.setAccepted(accepted) |
|
461 |
|
462 def _wheelEvent(self, evt): |
|
463 """ |
|
464 Protected method to handle wheel events. |
|
465 |
|
466 @param evt reference to the wheel event |
|
467 @type QWheelEvent |
|
468 """ |
|
469 delta = evt.angleDelta().y() |
|
470 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: |
|
471 if delta < 0: |
|
472 self.scaleDown() |
|
473 elif delta > 0: |
|
474 self.scaleUp() |
|
475 evt.accept() |
|
476 |
|
477 elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: |
|
478 if delta < 0: |
|
479 self.backward() |
|
480 elif delta > 0: |
|
481 self.forward() |
|
482 evt.accept() |
|
483 |
|
484 def _gestureEvent(self, evt): |
|
485 """ |
|
486 Protected method handling gesture events. |
|
487 |
|
488 @param evt reference to the gesture event |
|
489 @type QGestureEvent |
|
490 """ |
|
491 pinch = evt.gesture(Qt.GestureType.PinchGesture) |
|
492 if pinch: |
|
493 if pinch.state() == Qt.GestureState.GestureStarted: |
|
494 pinch.setTotalScaleFactor(self.__currentScale / 100.0) |
|
495 elif pinch.state() == Qt.GestureState.GestureUpdated: |
|
496 scaleFactor = pinch.totalScaleFactor() |
|
497 self.setScale(int(scaleFactor * 100)) |
|
498 evt.accept() |
|
499 |
|
500 def event(self, evt): |
|
501 """ |
|
502 Public method handling events. |
|
503 |
|
504 @param evt reference to the event (QEvent) |
|
505 @return flag indicating, if the event was handled (boolean) |
|
506 """ |
|
507 if evt.type() == QEvent.Type.Gesture: |
|
508 self._gestureEvent(evt) |
|
509 return True |
|
510 |
|
511 return super().event(evt) |
|
512 |
|
513 ####################################################################### |
|
514 ## Context menu related methods below |
|
515 ####################################################################### |
|
516 |
|
517 def contextMenuEvent(self, evt): |
|
518 """ |
|
519 Protected method called to create a context menu. |
|
520 |
|
521 This method is overridden from QWebEngineView. |
|
522 |
|
523 @param evt reference to the context menu event object |
|
524 @type QContextMenuEvent |
|
525 """ |
|
526 pos = evt.pos() |
|
527 reason = evt.reason() |
|
528 QTimer.singleShot( |
|
529 0, |
|
530 lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos))) |
|
531 # needs to be done this way because contextMenuEvent is blocking |
|
532 # the main loop |
|
533 |
|
534 def _contextMenuEvent(self, evt): |
|
535 """ |
|
536 Protected method called to create a context menu. |
|
537 |
|
538 @param evt reference to the context menu event object |
|
539 (QContextMenuEvent) |
|
540 """ |
|
541 self.__menu.clear() |
|
542 |
|
543 self.__createContextMenu(self.__menu) |
|
544 |
|
545 if not self.__menu.isEmpty(): |
|
546 pos = evt.globalPos() |
|
547 self.__menu.popup(QPoint(pos.x(), pos.y() + 1)) |
|
548 |
|
549 def __createContextMenu(self, menu): |
|
550 """ |
|
551 Private method to populate the context menu. |
|
552 |
|
553 @param menu reference to the menu to be populated |
|
554 @type QMenu |
|
555 """ |
|
556 contextMenuData = self.lastContextMenuRequest() |
|
557 |
|
558 act = menu.addAction( |
|
559 UI.PixmapCache.getIcon("back"), |
|
560 self.tr("Backward"), |
|
561 self.backward) |
|
562 act.setEnabled(self.isBackwardAvailable()) |
|
563 |
|
564 act = menu.addAction( |
|
565 UI.PixmapCache.getIcon("forward"), |
|
566 self.tr("Forward"), |
|
567 self.forward) |
|
568 act.setEnabled(self.isForwardAvailable()) |
|
569 |
|
570 act = menu.addAction( |
|
571 UI.PixmapCache.getIcon("reload"), |
|
572 self.tr("Reload"), |
|
573 self.reload) |
|
574 |
|
575 if ( |
|
576 not contextMenuData.linkUrl().isEmpty() and |
|
577 contextMenuData.linkUrl().scheme() != "javascript" |
|
578 ): |
|
579 self.__createLinkContextMenu(menu, contextMenuData) |
|
580 |
|
581 menu.addSeparator() |
|
582 |
|
583 act = menu.addAction( |
|
584 UI.PixmapCache.getIcon("editCopy"), |
|
585 self.tr("Copy Page URL to Clipboard")) |
|
586 act.setData(self.link()) |
|
587 act.triggered.connect( |
|
588 functools.partial(self.__copyLink, act)) |
|
589 |
|
590 menu.addSeparator() |
|
591 |
|
592 act = menu.addAction( |
|
593 UI.PixmapCache.getIcon("zoomIn"), |
|
594 self.tr("Zoom in"), |
|
595 self.scaleUp) |
|
596 act.setEnabled(self.isScaleUpAvailable()) |
|
597 |
|
598 act = menu.addAction( |
|
599 UI.PixmapCache.getIcon("zoomOut"), |
|
600 self.tr("Zoom out"), |
|
601 self.scaleDown) |
|
602 act.setEnabled(self.isScaleDownAvailable()) |
|
603 |
|
604 menu.addAction( |
|
605 UI.PixmapCache.getIcon("zoomReset"), |
|
606 self.tr("Zoom reset"), |
|
607 self.resetScale) |
|
608 |
|
609 menu.addSeparator() |
|
610 |
|
611 act = menu.addAction( |
|
612 UI.PixmapCache.getIcon("editCopy"), |
|
613 self.tr("Copy"), |
|
614 self.__copyText) |
|
615 act.setEnabled(bool(contextMenuData.selectedText())) |
|
616 |
|
617 menu.addAction( |
|
618 UI.PixmapCache.getIcon("editSelectAll"), |
|
619 self.tr("Select All"), |
|
620 self.__selectAll) |
|
621 |
|
622 menu.addSeparator() |
|
623 |
|
624 menu.addAction( |
|
625 UI.PixmapCache.getIcon("tabClose"), |
|
626 self.tr('Close'), |
|
627 self.__closePage) |
|
628 |
|
629 act = menu.addAction( |
|
630 UI.PixmapCache.getIcon("tabCloseOther"), |
|
631 self.tr("Close Others"), |
|
632 self.__closeOtherPages) |
|
633 act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1) |
|
634 |
|
635 def __createLinkContextMenu(self, menu, contextMenuData): |
|
636 """ |
|
637 Private method to populate the context menu for URLs. |
|
638 |
|
639 @param menu reference to the menu to be populated |
|
640 @type QMenu |
|
641 @param contextMenuData data of the last context menu request |
|
642 @type QWebEngineContextMenuRequest |
|
643 """ |
|
644 if not menu.isEmpty(): |
|
645 menu.addSeparator() |
|
646 |
|
647 act = menu.addAction( |
|
648 UI.PixmapCache.getIcon("openNewTab"), |
|
649 self.tr("Open Link in New Page")) |
|
650 act.setData(contextMenuData.linkUrl()) |
|
651 act.triggered.connect( |
|
652 functools.partial(self.__openLinkInNewPage, act)) |
|
653 |
|
654 act = menu.addAction( |
|
655 UI.PixmapCache.getIcon("newWindow"), |
|
656 self.tr("Open Link in Background Page")) |
|
657 act.setData(contextMenuData.linkUrl()) |
|
658 act.triggered.connect( |
|
659 functools.partial(self.__openLinkInBackgroundPage, act)) |
|
660 |
|
661 menu.addSeparator() |
|
662 |
|
663 act = menu.addAction( |
|
664 UI.PixmapCache.getIcon("editCopy"), |
|
665 self.tr("Copy URL to Clipboard")) |
|
666 act.setData(contextMenuData.linkUrl()) |
|
667 act.triggered.connect( |
|
668 functools.partial(self.__copyLink, act)) |
|
669 |
|
670 def __openLinkInNewPage(self, act): |
|
671 """ |
|
672 Private method called by the context menu to open a link in a new page. |
|
673 |
|
674 @param act reference to the action that triggered |
|
675 @type QAction |
|
676 """ |
|
677 url = act.data() |
|
678 if url.isEmpty(): |
|
679 return |
|
680 |
|
681 self.__helpViewerWidget.openUrlNewPage(url) |
|
682 |
|
683 def __openLinkInBackgroundPage(self, act): |
|
684 """ |
|
685 Private method called by the context menu to open a link in a |
|
686 background page. |
|
687 |
|
688 @param act reference to the action that triggered |
|
689 @type QAction |
|
690 """ |
|
691 url = act.data() |
|
692 if url.isEmpty(): |
|
693 return |
|
694 |
|
695 self.__helpViewerWidget.openUrlNewBackgroundPage(url) |
|
696 |
|
697 def __copyLink(self, act): |
|
698 """ |
|
699 Private method called by the context menu to copy a link to the |
|
700 clipboard. |
|
701 |
|
702 @param act reference to the action that triggered |
|
703 @type QAction |
|
704 """ |
|
705 data = act.data() |
|
706 if isinstance(data, QUrl) and data.isEmpty(): |
|
707 return |
|
708 |
|
709 if isinstance(data, QUrl): |
|
710 data = data.toString() |
|
711 |
|
712 # copy the URL to both clipboard areas |
|
713 QGuiApplication.clipboard().setText(data, QClipboard.Mode.Clipboard) |
|
714 QGuiApplication.clipboard().setText(data, QClipboard.Mode.Selection) |
|
715 |
|
716 def __copyText(self): |
|
717 """ |
|
718 Private method called by the context menu to copy selected text to the |
|
719 clipboard. |
|
720 """ |
|
721 self.triggerPageAction(QWebEnginePage.WebAction.Copy) |
|
722 |
|
723 def __selectAll(self): |
|
724 """ |
|
725 Private method called by the context menu to select all text. |
|
726 """ |
|
727 self.triggerPageAction(QWebEnginePage.WebAction.SelectAll) |
|
728 |
|
729 def __closePage(self): |
|
730 """ |
|
731 Private method called by the context menu to close the current page. |
|
732 """ |
|
733 self.__helpViewerWidget.closeCurrentPage() |
|
734 |
|
735 def __closeOtherPages(self): |
|
736 """ |
|
737 Private method called by the context menu to close all other pages. |
|
738 """ |
|
739 self.__helpViewerWidget.closeOtherPages() |