15 basestring = str |
15 basestring = str |
16 |
16 |
17 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QTimer |
17 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QTimer |
18 from PyQt5.QtGui import QCursor |
18 from PyQt5.QtGui import QCursor |
19 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, \ |
19 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, \ |
20 QComboBox, QSizePolicy, QLineEdit, QTextEdit, QToolTip |
20 QComboBox, QSizePolicy, QLineEdit, QTextBrowser, QToolTip |
21 |
21 |
22 from E5Gui.E5TextEditSearchWidget import E5TextEditSearchWidget |
22 from E5Gui.E5TextEditSearchWidget import E5TextEditSearchWidget |
23 |
23 |
24 import Preferences |
24 import Preferences |
25 |
25 |
29 prepareDocumentationViewerHtmlWarningDocument |
29 prepareDocumentationViewerHtmlWarningDocument |
30 |
30 |
31 from .data import codeDocumentationViewer_rc # __IGNORE_WARNING__ |
31 from .data import codeDocumentationViewer_rc # __IGNORE_WARNING__ |
32 |
32 |
33 |
33 |
34 class PlainTextDocumentationViewer(QWidget): |
34 class DocumentationViewerWidget(QWidget): |
35 """ |
35 """ |
36 Class implementing the plain text documentation viewer. |
36 Class implementing a rich text documentation viewer. |
37 """ |
37 """ |
38 def __init__(self, parent=None): |
38 def __init__(self, parent=None): |
39 """ |
39 """ |
40 Constructor |
40 Constructor |
41 |
41 |
42 @param parent reference to the parent widget |
42 @param parent reference to the parent widget |
43 @type QWidget |
43 @type QWidget |
44 """ |
44 """ |
45 super(PlainTextDocumentationViewer, self).__init__(parent) |
45 super(DocumentationViewerWidget, self).__init__(parent) |
46 self.setObjectName("PlainTextDocumentationViewer") |
46 self.setObjectName("DocumentationViewerWidget") |
47 |
|
48 self.__verticalLayout = QVBoxLayout(self) |
|
49 self.__verticalLayout.setObjectName("verticalLayout") |
|
50 self.__verticalLayout.setContentsMargins(0, 0, 0, 0) |
|
51 |
|
52 self.__contents = QTextEdit(self) |
|
53 self.__contents.setTabChangesFocus(True) |
|
54 self.__contents.setReadOnly(True) |
|
55 self.__contents.setLineWrapMode(QTextEdit.NoWrap) |
|
56 self.__contents.setObjectName("contents") |
|
57 self.__verticalLayout.addWidget(self.__contents) |
|
58 |
|
59 self.__searchWidget = E5TextEditSearchWidget(self, False) |
|
60 self.__searchWidget.setFocusPolicy(Qt.WheelFocus) |
|
61 self.__searchWidget.setObjectName("searchWidget") |
|
62 self.__verticalLayout.addWidget(self.__searchWidget) |
|
63 |
|
64 self.__searchWidget.attachTextEdit(self.__contents, "QTextEdit") |
|
65 |
|
66 self.preferencesChanged() |
|
67 |
|
68 def clear(self): |
|
69 """ |
|
70 Public method to clear the contents. |
|
71 """ |
|
72 self.__contents.clear() |
|
73 |
|
74 def setText(self, text): |
|
75 """ |
|
76 Public method to set the text to be shown. |
|
77 |
|
78 @param text text to be shown |
|
79 @type str |
|
80 """ |
|
81 self.__contents.setPlainText(text) |
|
82 |
|
83 def preferencesChanged(self): |
|
84 """ |
|
85 Public slot to handle a change of preferences. |
|
86 """ |
|
87 font = Preferences.getEditorOtherFonts("MonospacedFont") |
|
88 self.__contents.setFontFamily(font.family()) |
|
89 self.__contents.setFontPointSize(font.pointSize()) |
|
90 |
|
91 |
|
92 class WebViewDocumentationViewer(QWidget): |
|
93 """ |
|
94 Class implementing the rich text documentation viewer. |
|
95 """ |
|
96 def __init__(self, parent=None): |
|
97 """ |
|
98 Constructor |
|
99 |
|
100 @param parent reference to the parent widget |
|
101 @type QWidget |
|
102 """ |
|
103 super(WebViewDocumentationViewer, self).__init__(parent) |
|
104 self.setObjectName("WebViewDocumentationViewer") |
|
105 |
47 |
106 self.__verticalLayout = QVBoxLayout(self) |
48 self.__verticalLayout = QVBoxLayout(self) |
107 self.__verticalLayout.setObjectName("verticalLayout") |
49 self.__verticalLayout.setObjectName("verticalLayout") |
108 self.__verticalLayout.setContentsMargins(0, 0, 0, 0) |
50 self.__verticalLayout.setContentsMargins(0, 0, 0, 0) |
109 |
51 |
116 self.__contents.settings().setAttribute( |
58 self.__contents.settings().setAttribute( |
117 QWebEngineSettings.FocusOnNavigationEnabled, False) |
59 QWebEngineSettings.FocusOnNavigationEnabled, False) |
118 except AttributeError: |
60 except AttributeError: |
119 # pre Qt 5.8 |
61 # pre Qt 5.8 |
120 pass |
62 pass |
121 self.__usesWebKit = False |
63 self.__viewerType = "QWebEngineView" |
122 except ImportError: |
64 except ImportError: |
123 from PyQt5.QtWebKitWidgets import QWebPage, QWebView |
65 try: |
124 self.__contents = QWebView(self) |
66 from PyQt5.QtWebKitWidgets import QWebPage, QWebView |
125 self.__contents.page().setLinkDelegationPolicy( |
67 self.__contents = QWebView(self) |
126 QWebPage.DelegateAllLinks) |
68 self.__contents.page().setLinkDelegationPolicy( |
127 self.__usesWebKit = True |
69 QWebPage.DelegateAllLinks) |
|
70 self.__viewerType = "QWebView" |
|
71 except ImportError: |
|
72 self.__contents = QTextBrowser(self) |
|
73 self.__contents.setOpenExternalLinks(True) |
|
74 self.__viewerType = "QTextEdit" |
128 |
75 |
129 sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) |
76 sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) |
130 sizePolicy.setHorizontalStretch(0) |
77 sizePolicy.setHorizontalStretch(0) |
131 sizePolicy.setVerticalStretch(0) |
78 sizePolicy.setVerticalStretch(0) |
132 sizePolicy.setHeightForWidth( |
79 sizePolicy.setHeightForWidth( |
133 self.__contents.sizePolicy().hasHeightForWidth()) |
80 self.__contents.sizePolicy().hasHeightForWidth()) |
134 self.__contents.setSizePolicy(sizePolicy) |
81 self.__contents.setSizePolicy(sizePolicy) |
135 self.__contents.setContextMenuPolicy(Qt.NoContextMenu) |
82 self.__contents.setContextMenuPolicy(Qt.NoContextMenu) |
136 self.__contents.setUrl(QUrl("about:blank")) |
83 if self.__viewerType != "QTextEdit": |
|
84 self.__contents.setUrl(QUrl("about:blank")) |
137 self.__verticalLayout.addWidget(self.__contents) |
85 self.__verticalLayout.addWidget(self.__contents) |
138 |
86 |
139 self.__searchWidget = E5TextEditSearchWidget(self, False) |
87 self.__searchWidget = E5TextEditSearchWidget(self, False) |
140 self.__searchWidget.setFocusPolicy(Qt.WheelFocus) |
88 self.__searchWidget.setFocusPolicy(Qt.WheelFocus) |
141 self.__searchWidget.setObjectName("searchWidget") |
89 self.__searchWidget.setObjectName("searchWidget") |
142 self.__verticalLayout.addWidget(self.__searchWidget) |
90 self.__verticalLayout.addWidget(self.__searchWidget) |
143 |
91 |
144 self.__searchWidget.attachTextEdit( |
92 self.__searchWidget.attachTextEdit( |
145 self.__contents, |
93 self.__contents, self.__viewerType) |
146 "QWebView" if self.__usesWebKit else "QWebEngineView", |
|
147 ) |
|
148 |
94 |
149 @pyqtSlot(str) |
95 @pyqtSlot(str) |
150 def __showLink(self, urlStr): |
96 def __showLink(self, urlStr): |
151 """ |
97 """ |
152 Private slot to show the hovered link in a tooltip. |
98 Private slot to show the hovered link in a tooltip. |
254 self.objectLineEdit.setObjectName("objectLineEdit") |
203 self.objectLineEdit.setObjectName("objectLineEdit") |
255 |
204 |
256 self.verticalLayout.addLayout(self.horizontalLayout1) |
205 self.verticalLayout.addLayout(self.horizontalLayout1) |
257 self.verticalLayout.addWidget(self.objectLineEdit) |
206 self.verticalLayout.addWidget(self.objectLineEdit) |
258 |
207 |
259 try: |
208 # Rich Text (Web) Viewer |
260 # Rich Text (Web) Viewer |
209 self.__viewerWidget = DocumentationViewerWidget(self) |
261 self.__richTextViewer = WebViewDocumentationViewer(self) |
210 self.__viewerWidget.setObjectName("__viewerWidget") |
262 self.__richTextViewer.setObjectName("__richTextViewer") |
211 self.verticalLayout.addWidget(self.__viewerWidget) |
263 self.verticalLayout.addWidget(self.__richTextViewer) |
212 |
264 |
213 # backward compatibility for plug-ins before 2018-09-17 |
265 self.__plainTextViewer = None |
214 Preferences.setDocuViewer("ShowInfoAsRichText", True) |
266 |
|
267 # backward compatibility for plug-ins before 2018-09-17 |
|
268 Preferences.setDocuViewer("ShowInfoAsRichText", True) |
|
269 except ImportError: |
|
270 # neither QtWebEngineWidgets nor QtWebKitWidgets is available |
|
271 self.__richTextViewer = None |
|
272 |
|
273 # Plain Text Viewer |
|
274 self.__plainTextViewer = PlainTextDocumentationViewer(self) |
|
275 self.__plainTextViewer.setObjectName("__plainTextViewer") |
|
276 self.verticalLayout.addWidget(self.__plainTextViewer) |
|
277 |
|
278 # backward compatibility for plug-ins before 2018-09-17 |
|
279 Preferences.setDocuViewer("ShowInfoAsRichText", False) |
|
280 |
215 |
281 self.providerComboBox.currentIndexChanged[int].connect( |
216 self.providerComboBox.currentIndexChanged[int].connect( |
282 self.on_providerComboBox_currentIndexChanged) |
217 self.on_providerComboBox_currentIndexChanged) |
283 |
218 |
284 def finalizeSetup(self): |
219 def finalizeSetup(self): |
404 # try again one index before |
339 # try again one index before |
405 word = editor.getWord(line, index - 1) |
340 word = editor.getWord(line, index - 1) |
406 self.objectLineEdit.setText(word) |
341 self.objectLineEdit.setText(word) |
407 |
342 |
408 if self.__selectedProvider != self.__disabledProvider: |
343 if self.__selectedProvider != self.__disabledProvider: |
409 if self.__richTextViewer: |
344 self.__viewerWidget.clear() |
410 self.__richTextViewer.clear() |
|
411 else: |
|
412 self.__plainTextViewer.clear() |
|
413 self.__providers[self.__selectedProvider][0](editor) |
345 self.__providers[self.__selectedProvider][0](editor) |
414 |
346 |
415 def documentationReady(self, documentationInfo, isWarning=False, |
347 def documentationReady(self, documentationInfo, isWarning=False, |
416 isDocWarning=False): |
348 isDocWarning=False): |
417 """ |
349 """ |
444 self.__showDisabledMessage() |
376 self.__showDisabledMessage() |
445 else: |
377 else: |
446 self.documentationReady(self.tr("No documentation available"), |
378 self.documentationReady(self.tr("No documentation available"), |
447 isDocWarning=True) |
379 isDocWarning=True) |
448 else: |
380 else: |
449 if self.__richTextViewer: |
381 if isWarning: |
450 if isWarning: |
382 html = prepareDocumentationViewerHtmlWarningDocument( |
451 html = prepareDocumentationViewerHtmlWarningDocument( |
383 documentationInfo) |
452 documentationInfo) |
384 elif isDocWarning: |
453 elif isDocWarning: |
385 html = prepareDocumentationViewerHtmlDocWarningDocument( |
454 html = prepareDocumentationViewerHtmlDocWarningDocument( |
386 documentationInfo) |
455 documentationInfo) |
387 elif isinstance(documentationInfo, dict): |
456 elif isinstance(documentationInfo, dict): |
388 html = prepareDocumentationViewerHtmlDocument( |
457 html = prepareDocumentationViewerHtmlDocument( |
389 documentationInfo) |
458 documentationInfo) |
|
459 else: |
|
460 html = documentationInfo |
|
461 self.__setHtml(html) |
|
462 else: |
390 else: |
463 if isinstance(documentationInfo, basestring): |
391 html = documentationInfo |
464 fullText = documentationInfo |
392 self.__viewerWidget.setHtml(html) |
465 elif isinstance(documentationInfo, dict): |
|
466 name = documentationInfo["name"] |
|
467 if name: |
|
468 title = "".join([name, "\n", |
|
469 "=" * len(name), "\n\n"]) |
|
470 |
|
471 if "argspec" in documentationInfo and \ |
|
472 documentationInfo["argspec"]: |
|
473 definition = self.tr("Definition: {0}{1}\n")\ |
|
474 .format(name, documentationInfo["argspec"]) |
|
475 elif name: |
|
476 definition = self.tr("Definition: {0}\n")\ |
|
477 .format(name) |
|
478 else: |
|
479 definition = "" |
|
480 |
|
481 if "typ" in documentationInfo and \ |
|
482 documentationInfo["typ"]: |
|
483 typeInfo = self.tr("Type: {0}\n").format( |
|
484 documentationInfo["typ"]) |
|
485 else: |
|
486 typeInfo = "" |
|
487 |
|
488 if "note" in documentationInfo and \ |
|
489 documentationInfo["note"]: |
|
490 note = self.tr("Note: {0}\n").format( |
|
491 documentationInfo["note"]) |
|
492 else: |
|
493 note = "" |
|
494 |
|
495 header = "".join([title, definition, typeInfo, note]) |
|
496 else: |
|
497 header = "" |
|
498 |
|
499 if "docstring" not in documentationInfo or \ |
|
500 not documentationInfo["docstring"]: |
|
501 docString = self.tr( |
|
502 "No further documentation available") |
|
503 else: |
|
504 if header: |
|
505 docString = "\n----\n\n{0}".format( |
|
506 documentationInfo["docstring"]) |
|
507 else: |
|
508 docString = documentationInfo["docstring"] |
|
509 |
|
510 fullText = "".join([header, docString]) |
|
511 |
|
512 self.__plainTextViewer.setText(fullText) |
|
513 |
393 |
514 def __showDisabledMessage(self): |
394 def __showDisabledMessage(self): |
515 """ |
395 """ |
516 Private method to show a message giving the reason for being disabled. |
396 Private method to show a message giving the reason for being disabled. |
517 """ |
397 """ |
523 else: |
403 else: |
524 self.documentationReady( |
404 self.documentationReady( |
525 self.tr("This function has been disabled."), |
405 self.tr("This function has been disabled."), |
526 isWarning=True) |
406 isWarning=True) |
527 |
407 |
528 def __setHtml(self, html): |
|
529 """ |
|
530 Private slot to set the prepared HTML text. |
|
531 |
|
532 @param html prepared HTML text |
|
533 @type str |
|
534 """ |
|
535 if self.__richTextViewer: |
|
536 self.__richTextViewer.setHtml(html) |
|
537 |
|
538 @pyqtSlot(int) |
408 @pyqtSlot(int) |
539 def on_providerComboBox_currentIndexChanged(self, index): |
409 def on_providerComboBox_currentIndexChanged(self, index): |
540 """ |
410 """ |
541 Private slot to handle the selection of a provider. |
411 Private slot to handle the selection of a provider. |
542 |
412 |
543 @param index index of the selected provider |
413 @param index index of the selected provider |
544 @type int |
414 @type int |
545 """ |
415 """ |
546 if not self.__shuttingDown and not self.__startingUp: |
416 if not self.__shuttingDown and not self.__startingUp: |
547 if self.__richTextViewer: |
417 self.__viewerWidget.clear() |
548 self.__richTextViewer.clear() |
|
549 else: |
|
550 self.__plainTextViewer.clear() |
|
551 self.objectLineEdit.clear() |
418 self.objectLineEdit.clear() |
552 |
419 |
553 provider = self.providerComboBox.itemData(index) |
420 provider = self.providerComboBox.itemData(index) |
554 if provider == self.__disabledProvider: |
421 if provider == self.__disabledProvider: |
555 self.__showDisabledMessage() |
422 self.__showDisabledMessage() |