8 plug-ins. |
8 plug-ins. |
9 """ |
9 """ |
10 |
10 |
11 from __future__ import unicode_literals |
11 from __future__ import unicode_literals |
12 |
12 |
13 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QThread |
13 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QThread, QUrl |
|
14 from PyQt5.QtGui import QCursor |
14 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, \ |
15 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, \ |
15 QComboBox, QSizePolicy, QLineEdit, QTextEdit |
16 QComboBox, QSizePolicy, QLineEdit, QTextEdit, QToolTip, QToolButton, \ |
|
17 QActionGroup, QMenu |
16 |
18 |
17 from E5Gui.E5TextEditSearchWidget import E5TextEditSearchWidget |
19 from E5Gui.E5TextEditSearchWidget import E5TextEditSearchWidget |
|
20 from E5Gui.E5ToolButton import E5ToolButton |
18 |
21 |
19 import Preferences |
22 import Preferences |
|
23 import UI.PixmapCache |
20 |
24 |
21 |
25 |
22 class PlainTextDocumentationViewer(QWidget): |
26 class PlainTextDocumentationViewer(QWidget): |
23 """ |
27 """ |
24 Class implementing the plain text documentation viewer. |
28 Class implementing the plain text documentation viewer. |
63 |
68 |
64 @param text text to be shown |
69 @param text text to be shown |
65 @type str |
70 @type str |
66 """ |
71 """ |
67 self.__contents.setPlainText(text) |
72 self.__contents.setPlainText(text) |
68 |
|
69 def setHtml(self, html): |
|
70 self.__contents.setHtml(html) |
|
71 |
73 |
72 def preferencesChanged(self): |
74 def preferencesChanged(self): |
73 """ |
75 """ |
74 Public slot to handle a change of preferences. |
76 Public slot to handle a change of preferences. |
75 """ |
77 """ |
76 font = Preferences.getEditorOtherFonts("MonospacedFont") |
78 font = Preferences.getEditorOtherFonts("MonospacedFont") |
77 self.__contents.setFontFamily(font.family()) |
79 self.__contents.setFontFamily(font.family()) |
78 self.__contents.setFontPointSize(font.pointSize()) |
80 self.__contents.setFontPointSize(font.pointSize()) |
79 |
81 |
|
82 |
|
83 class WebViewDocumentationViewer(QWidget): |
|
84 """ |
|
85 Class implementing the rich text documentation viewer. |
|
86 """ |
|
87 def __init__(self, parent=None): |
|
88 """ |
|
89 Constructor |
|
90 |
|
91 @param parent reference to the parent widget |
|
92 @type QWidget |
|
93 """ |
|
94 super(WebViewDocumentationViewer, self).__init__(parent) |
|
95 self.setObjectName("WebViewDocumentationViewer") |
|
96 |
|
97 self.__verticalLayout = QVBoxLayout(self) |
|
98 self.__verticalLayout.setObjectName("verticalLayout") |
|
99 self.__verticalLayout.setContentsMargins(0, 0, 0, 0) |
|
100 |
|
101 try: |
|
102 from PyQt5.QtWebEngineWidgets import QWebEngineView |
|
103 self.__contents = QWebEngineView(self) |
|
104 self.__contents.page().linkHovered.connect(self.__showLink) |
|
105 self.__usesWebKit = False |
|
106 except ImportError: |
|
107 from PyQt5.QtWebKitWidgets import QWebPage, QWebView |
|
108 self.__contents = QWebView(self) |
|
109 self.__contents.page().setLinkDelegationPolicy( |
|
110 QWebPage.DelegateAllLinks) |
|
111 self.__usesWebKit = True |
|
112 |
|
113 sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) |
|
114 sizePolicy.setHorizontalStretch(0) |
|
115 sizePolicy.setVerticalStretch(0) |
|
116 sizePolicy.setHeightForWidth( |
|
117 self.__contents.sizePolicy().hasHeightForWidth()) |
|
118 self.__contents.setSizePolicy(sizePolicy) |
|
119 self.__contents.setContextMenuPolicy(Qt.NoContextMenu) |
|
120 self.__contents.setUrl(QUrl("about:blank")) |
|
121 self.__verticalLayout.addWidget(self.__contents) |
|
122 |
|
123 self.__searchWidget = E5TextEditSearchWidget(self) |
|
124 self.__searchWidget.setFocusPolicy(Qt.WheelFocus) |
|
125 self.__searchWidget.setObjectName("searchWidget") |
|
126 self.__verticalLayout.addWidget(self.__searchWidget) |
|
127 |
|
128 self.__searchWidget.attachTextEdit( |
|
129 self.__contents, |
|
130 "QWebView" if self.__usesWebKit else "QWebEngineView", |
|
131 ) |
|
132 |
|
133 @pyqtSlot(str) |
|
134 def __showLink(self, urlStr): |
|
135 """ |
|
136 Private slot to show the hovered link in a tooltip. |
|
137 |
|
138 @param urlStr hovered URL |
|
139 @type str |
|
140 """ |
|
141 QToolTip.showText(QCursor.pos(), urlStr, self.__contents) |
|
142 |
|
143 def setHtml(self, html): |
|
144 """ |
|
145 Public method to set the HTML text of the widget. |
|
146 |
|
147 @param html HTML text to be shown |
|
148 @type str |
|
149 """ |
|
150 self.__contents.setHtml(html) |
|
151 |
|
152 def clear(self): |
|
153 """ |
|
154 Public method to clear the shown contents. |
|
155 """ |
|
156 self.__contents.setHtml("") |
|
157 |
80 |
158 |
81 class CodeDocumentationViewer(QWidget): |
159 class CodeDocumentationViewer(QWidget): |
82 """ |
160 """ |
83 Class implementing a widget to show some source code information provided |
161 Class implementing a widget to show some source code information provided |
84 by plug-ins. |
162 by plug-ins. |
|
163 |
|
164 @signal providerAdded() emitted to indicate the availability of a new |
|
165 provider |
|
166 @signal providerRemoved() emitted to indicate the removal of a provider |
85 """ |
167 """ |
86 providerAdded = pyqtSignal() |
168 providerAdded = pyqtSignal() |
|
169 providerRemoved = pyqtSignal() |
87 |
170 |
88 def __init__(self, parent=None): |
171 def __init__(self, parent=None): |
89 """ |
172 """ |
90 Constructor |
173 Constructor |
91 |
174 |
151 self.objectLineEdit = QLineEdit(self) |
232 self.objectLineEdit = QLineEdit(self) |
152 self.objectLineEdit.setReadOnly(True) |
233 self.objectLineEdit.setReadOnly(True) |
153 self.objectLineEdit.setObjectName("objectLineEdit") |
234 self.objectLineEdit.setObjectName("objectLineEdit") |
154 self.horizontalLayout.addWidget(self.objectLineEdit) |
235 self.horizontalLayout.addWidget(self.objectLineEdit) |
155 |
236 |
|
237 self.__toolButton = E5ToolButton(self) |
|
238 self.__toolButton.setObjectName( |
|
239 "navigation_supermenu_button") |
|
240 self.__toolButton.setIcon(UI.PixmapCache.getIcon("superMenu.png")) |
|
241 self.__toolButton.setToolTip(self.tr("Main Menu")) |
|
242 self.__toolButton.setPopupMode(QToolButton.InstantPopup) |
|
243 self.__toolButton.setToolButtonStyle(Qt.ToolButtonIconOnly) |
|
244 self.__toolButton.setFocusPolicy(Qt.NoFocus) |
|
245 self.__toolButton.setAutoRaise(True) |
|
246 self.__toolButton.setShowMenuInside(True) |
|
247 |
|
248 self.__optionsMenu = QMenu(self) |
|
249 self.__richTextAct = self.__optionsMenu.addAction( |
|
250 self.tr("Rich Text"), |
|
251 lambda: self.__showTextViewer(True)) |
|
252 self.__richTextAct.setCheckable(True) |
|
253 self.__plainTextAct = self.__optionsMenu.addAction( |
|
254 self.tr("Plain Text"), |
|
255 lambda: self.__showTextViewer(False)) |
|
256 self.__plainTextAct.setCheckable(True) |
|
257 self.__optionsActionGroup = QActionGroup(self) |
|
258 self.__optionsActionGroup.setExclusive(True) |
|
259 self.__optionsActionGroup.addAction(self.__richTextAct) |
|
260 self.__optionsActionGroup.addAction(self.__plainTextAct) |
|
261 |
|
262 self.__toolButton.setMenu(self.__optionsMenu) |
|
263 self.horizontalLayout.addWidget(self.__toolButton) |
|
264 |
156 self.verticalLayout.addLayout(self.horizontalLayout) |
265 self.verticalLayout.addLayout(self.horizontalLayout) |
157 |
266 |
158 self.contents = PlainTextDocumentationViewer(self) |
267 # Plain Text Viewer |
159 self.contents.setObjectName("contents") |
268 self.__plainTextViewer = PlainTextDocumentationViewer(self) |
160 self.verticalLayout.addWidget(self.contents) |
269 self.__plainTextViewer.setObjectName("__plainTextViewer") |
|
270 self.verticalLayout.addWidget(self.__plainTextViewer) |
|
271 |
|
272 # Rich Text (Web) Viewer |
|
273 self.__richTextViewer = WebViewDocumentationViewer(self) |
|
274 self.__richTextViewer.setObjectName("__richTextViewer") |
|
275 self.verticalLayout.addWidget(self.__richTextViewer) |
161 |
276 |
162 self.providerComboBox.currentIndexChanged[int].connect( |
277 self.providerComboBox.currentIndexChanged[int].connect( |
163 self.on_providerComboBox_currentIndexChanged) |
278 self.on_providerComboBox_currentIndexChanged) |
164 |
279 |
165 def finalizeSetup(self): |
280 def finalizeSetup(self): |
166 """ |
281 """ |
167 Public method to finalize the setup of the documentation viewer. |
282 Public method to finalize the setup of the documentation viewer. |
168 """ |
283 """ |
|
284 self.__showTextViewer(Preferences.getDocuViewer("ShowInfoAsMarkdown")) |
|
285 |
169 self.__startingUp = False |
286 self.__startingUp = False |
170 provider = Preferences.getDocuViewer("Provider") |
287 provider = Preferences.getDocuViewer("Provider") |
171 if provider in self.__providers: |
288 if provider in self.__providers: |
172 index = self.providerComboBox.findData(provider) |
289 index = self.providerComboBox.findData(provider) |
173 else: |
290 else: |
258 """ |
379 """ |
259 line, index = editor.getCursorPosition() |
380 line, index = editor.getCursorPosition() |
260 word = editor.getWord(line, index) |
381 word = editor.getWord(line, index) |
261 if not word: |
382 if not word: |
262 # try again one index before |
383 # try again one index before |
263 word = editor.getWord(line, index - 1) |
384 word = editor.getWord(line, index - 1) |
264 self.objectLineEdit.setText(word) |
385 self.objectLineEdit.setText(word) |
265 |
386 |
266 if self.__selectedProvider != self.__disabledProvider: |
387 if self.__selectedProvider != self.__disabledProvider: |
267 self.contents.clear() |
388 self.__plainTextViewer.clear() |
|
389 self.__richTextViewer.clear() |
268 self.__providers[self.__selectedProvider][0](editor) |
390 self.__providers[self.__selectedProvider][0](editor) |
269 |
391 |
270 # TODO: document this hook in the plug-in document |
392 # TODO: document this hook in the plug-in document |
271 def documentationReady(self, documentationInfo): |
393 def documentationReady(self, documentationInfo): |
272 """ |
394 """ |
286 """ |
408 """ |
287 self.__ui.activateCodeDocumentationViewer(switchFocus=False) |
409 self.__ui.activateCodeDocumentationViewer(switchFocus=False) |
288 |
410 |
289 self.__lastDocumentation = documentationInfo |
411 self.__lastDocumentation = documentationInfo |
290 |
412 |
291 if not documentationInfo: |
413 if documentationInfo is not None: |
292 fullText = self.__noDocumentationString |
414 if not documentationInfo: |
293 elif isinstance(documentationInfo, str): |
415 if self.__selectedProvider == self.__disabledProvider: |
294 fullText = documentationInfo |
416 fullText = self.__disabledString |
295 elif isinstance(documentationInfo, dict): |
417 else: |
296 # format the text with markdown syntax |
418 fullText = self.__noDocumentationString |
297 name = documentationInfo["name"] |
419 elif isinstance(documentationInfo, str): |
298 if name: |
420 fullText = documentationInfo |
299 title = "".join([name, "\n", |
421 elif isinstance(documentationInfo, dict): |
300 "=" * len(name), "\n\n"]) |
422 # format the text with markdown syntax |
|
423 name = documentationInfo["name"] |
|
424 if name: |
|
425 title = "".join([name, "\n", |
|
426 "=" * len(name), "\n\n"]) |
|
427 else: |
|
428 title = "" |
|
429 |
|
430 if documentationInfo["argspec"]: |
|
431 if self.__showMarkdown: |
|
432 definition = self.tr( |
|
433 "**Definition**: {0}{1}\n", |
|
434 "string with markdown syntax").format( |
|
435 name, documentationInfo["argspec"]) |
|
436 else: |
|
437 definition = self.tr( |
|
438 "Definition: {0}{1}\n", |
|
439 "string as plain text").format( |
|
440 name, documentationInfo["argspec"]) |
|
441 else: |
|
442 definition = '' |
|
443 |
|
444 if documentationInfo["note"]: |
|
445 if self.__showMarkdown: |
|
446 note = self.tr( |
|
447 "**Info**: {0}\n\n----\n\n", |
|
448 "string with markdown syntax").format( |
|
449 documentationInfo["note"]) |
|
450 else: |
|
451 note = self.tr( |
|
452 "Info: {0}\n\n----\n\n", |
|
453 "string as plain text").format( |
|
454 documentationInfo["note"]) |
|
455 else: |
|
456 note = "" |
|
457 |
|
458 if documentationInfo["docstring"] is None: |
|
459 docString = "" |
|
460 else: |
|
461 docString = documentationInfo["docstring"] |
|
462 |
|
463 fullText = "".join([title, definition, note, docString]) |
|
464 |
|
465 if self.__showMarkdown: |
|
466 self.__processingThread.process("markdown", fullText) |
301 else: |
467 else: |
302 title = "" |
468 self.__plainTextViewer.setText(fullText) |
303 |
|
304 if documentationInfo["argspec"]: |
|
305 if self.__showMarkdown: |
|
306 definition = self.tr("**Definition**: {0}{1}\n", |
|
307 "string with markdown syntax").format( |
|
308 name, documentationInfo["argspec"]) |
|
309 else: |
|
310 definition = self.tr("Definition: {0}{1}\n", |
|
311 "string as plain text").format( |
|
312 name, documentationInfo["argspec"]) |
|
313 else: |
|
314 definition = '' |
|
315 |
|
316 if documentationInfo["note"]: |
|
317 if self.__showMarkdown: |
|
318 note = self.tr("**Info**: {0}\n\n----\n\n", |
|
319 "string with markdown syntax").format( |
|
320 documentationInfo["note"]) |
|
321 else: |
|
322 note = self.tr("Info: {0}\n\n----\n\n", |
|
323 "string as plain text").format( |
|
324 documentationInfo["note"]) |
|
325 else: |
|
326 note = "" |
|
327 |
|
328 fullText = "".join([title, definition, note, |
|
329 documentationInfo['docstring']]) |
|
330 |
|
331 if self.__showMarkdown: |
|
332 self.__processingThread.process("markdown", fullText) |
|
333 else: |
|
334 self.contents.setText(fullText) |
|
335 |
469 |
336 def __setHtml(self, html): |
470 def __setHtml(self, html): |
337 """ |
471 """ |
338 Private slot to set the prepared HTML text. |
472 Private slot to set the prepared HTML text. |
339 |
473 |
340 @param html prepared HTML text |
474 @param html prepared HTML text |
341 @type str |
475 @type str |
342 """ |
476 """ |
343 self.contents.setHtml(html) |
477 self.__richTextViewer.setHtml(html) |
344 |
478 |
345 @pyqtSlot(int) |
479 @pyqtSlot(int) |
346 def on_providerComboBox_currentIndexChanged(self, index): |
480 def on_providerComboBox_currentIndexChanged(self, index): |
347 """ |
481 """ |
348 Private slot to handle the selection of a provider. |
482 Private slot to handle the selection of a provider. |
349 |
483 |
350 @param index index of the selected provider |
484 @param index index of the selected provider |
351 @type int |
485 @type int |
352 """ |
486 """ |
353 if not self.__shuttingDown and not self.__startingUp: |
487 if not self.__shuttingDown and not self.__startingUp: |
354 self.contents.clear() |
488 self.__plainTextViewer.clear() |
|
489 self.__richTextViewer.clear() |
355 self.objectLineEdit.clear() |
490 self.objectLineEdit.clear() |
356 |
491 |
357 provider = self.providerComboBox.itemData(index) |
492 provider = self.providerComboBox.itemData(index) |
358 if provider == self.__disabledProvider: |
493 if provider == self.__disabledProvider: |
359 self.documentationReady(self.__disabledString) |
494 self.documentationReady(self.__disabledString) |
|
495 else: |
|
496 self.__lastDocumentation = None |
|
497 |
360 Preferences.setDocuViewer("Provider", provider) |
498 Preferences.setDocuViewer("Provider", provider) |
361 self.__selectedProvider = provider |
499 self.__selectedProvider = provider |
362 |
500 |
363 def shutdown(self): |
501 def shutdown(self): |
364 """ |
502 """ |
371 """ |
509 """ |
372 Public slot to handle a change of preferences. |
510 Public slot to handle a change of preferences. |
373 """ |
511 """ |
374 showMarkdown = Preferences.getDocuViewer("ShowInfoAsMarkdown") |
512 showMarkdown = Preferences.getDocuViewer("ShowInfoAsMarkdown") |
375 if showMarkdown != self.__showMarkdown: |
513 if showMarkdown != self.__showMarkdown: |
376 self.__showMarkdown = showMarkdown |
514 self.__showTextViewer(showMarkdown) |
377 self.documentationReady(self.__lastDocumentation) |
|
378 |
515 |
379 provider = Preferences.getDocuViewer("Provider") |
516 provider = Preferences.getDocuViewer("Provider") |
380 if provider != self.__selectedProvider: |
517 if provider != self.__selectedProvider: |
381 index = self.providerComboBox.findData(provider) |
518 index = self.providerComboBox.findData(provider) |
382 if index < 0: |
519 if index < 0: |
383 index = 0 |
520 index = 0 |
384 self.providerComboBox.setCurrentIndex(index) |
521 self.providerComboBox.setCurrentIndex(index) |
|
522 |
|
523 def __showTextViewer(self, richText): |
|
524 """ |
|
525 Private slot to show the selected viewer. |
|
526 |
|
527 @param richText flag indicating the rich text viewer |
|
528 @type bool |
|
529 """ |
|
530 self.__showMarkdown = richText |
|
531 |
|
532 self.__plainTextViewer.clear() |
|
533 self.__richTextViewer.clear() |
|
534 |
|
535 self.__plainTextViewer.setVisible(not richText) |
|
536 self.__richTextViewer.setVisible(richText) |
|
537 |
|
538 self.__plainTextAct.setChecked(not richText) |
|
539 self.__richTextAct.setChecked(richText) |
|
540 |
|
541 self.documentationReady(self.__lastDocumentation) |
|
542 |
|
543 Preferences.setDocuViewer("ShowInfoAsMarkdown", richText) |
385 |
544 |
386 |
545 |
387 class DocumentProcessingThread(QThread): |
546 class DocumentProcessingThread(QThread): |
388 """ |
547 """ |
389 Class implementing a thread to process some text into HTML usable by the |
548 Class implementing a thread to process some text into HTML usable by the |