UI/Previewer.py

changeset 3012
d177226027e2
parent 2965
d133c7edd88a
child 3034
7ce719013078
child 3057
10516539f238
equal deleted inserted replaced
3011:18292228c724 3012:d177226027e2
41 self.__vm = viewmanager 41 self.__vm = viewmanager
42 self.__splitter = splitter 42 self.__splitter = splitter
43 43
44 self.__firstShow = True 44 self.__firstShow = True
45 45
46 self.previewView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) 46 self.previewView.page().setLinkDelegationPolicy(
47 QWebPage.DelegateAllLinks)
47 48
48 # Don't update too often because the UI might become sluggish 49 # Don't update too often because the UI might become sluggish
49 self.__typingTimer = QTimer() 50 self.__typingTimer = QTimer()
50 self.__typingTimer.setInterval(500) # 500ms 51 self.__typingTimer.setInterval(500) # 500ms
51 self.__typingTimer.timeout.connect(self.__runProcessingThread) 52 self.__typingTimer.timeout.connect(self.__runProcessingThread)
73 """ 74 """
74 Public method to show the preview widget. 75 Public method to show the preview widget.
75 """ 76 """
76 super().show() 77 super().show()
77 if self.__firstShow: 78 if self.__firstShow:
78 self.__splitter.restoreState(Preferences.getUI("PreviewSplitterState")) 79 self.__splitter.restoreState(
79 self.jsCheckBox.setChecked(Preferences.getUI("ShowFilePreviewJS")) 80 Preferences.getUI("PreviewSplitterState"))
80 self.ssiCheckBox.setChecked(Preferences.getUI("ShowFilePreviewSSI")) 81 self.jsCheckBox.setChecked(
82 Preferences.getUI("ShowFilePreviewJS"))
83 self.ssiCheckBox.setChecked(
84 Preferences.getUI("ShowFilePreviewSSI"))
81 self.__firstShow = False 85 self.__firstShow = False
82 self.__typingTimer.start() 86 self.__typingTimer.start()
83 87
84 def hide(self): 88 def hide(self):
85 """ 89 """
143 """ 147 """
144 if editor is None: 148 if editor is None:
145 self.hide() 149 self.hide()
146 return 150 return
147 151
148 if Preferences.getUI("ShowFilePreview") and self.__isPreviewable(editor): 152 if Preferences.getUI("ShowFilePreview") and \
153 self.__isPreviewable(editor):
149 self.show() 154 self.show()
150 self.__runProcessingThread() 155 self.__runProcessingThread()
151 else: 156 else:
152 self.hide() 157 self.hide()
153 158
181 else: 186 else:
182 self.hide() 187 self.hide()
183 188
184 def __isPreviewable(self, editor): 189 def __isPreviewable(self, editor):
185 """ 190 """
186 Private method to check, if a preview can be shown for the given editor. 191 Private method to check, if a preview can be shown for the given
192 editor.
187 193
188 @param editor reference to an editor (Editor) 194 @param editor reference to an editor (Editor)
189 @return flag indicating if a preview can be shown (boolean) 195 @return flag indicating if a preview can be shown (boolean)
190 """ 196 """
191 if editor: 197 if editor:
192 if editor.getFileName() is not None: 198 if editor.getFileName() is not None:
193 extension = os.path.normcase( 199 extension = os.path.normcase(
194 os.path.splitext(editor.getFileName())[1][1:]) 200 os.path.splitext(editor.getFileName())[1][1:])
195 return extension in \ 201 return extension in \
196 Preferences.getEditor("PreviewHtmlFileNameExtensions") + \ 202 Preferences.getEditor("PreviewHtmlFileNameExtensions") + \
197 Preferences.getEditor("PreviewMarkdownFileNameExtensions") + \ 203 Preferences.getEditor(
204 "PreviewMarkdownFileNameExtensions") + \
198 Preferences.getEditor("PreviewRestFileNameExtensions") 205 Preferences.getEditor("PreviewRestFileNameExtensions")
199 elif editor.getLanguage() == "HTML": 206 elif editor.getLanguage() == "HTML":
200 return True 207 return True
201 208
202 return False 209 return False
224 language = "Markdown" 231 language = "Markdown"
225 elif extension in \ 232 elif extension in \
226 Preferences.getEditor("PreviewRestFileNameExtensions"): 233 Preferences.getEditor("PreviewRestFileNameExtensions"):
227 language = "ReST" 234 language = "ReST"
228 else: 235 else:
229 self.__setHtml(fn, 236 self.__setHtml(fn, self.trUtf8(
230 self.trUtf8("<p>No preview available for this type of file.</p>")) 237 "<p>No preview available for this type of file.</p>"))
231 return 238 return
232 239
233 if fn: 240 if fn:
234 project = e5App().getObject("Project") 241 project = e5App().getObject("Project")
235 if project.isProjectFile(fn): 242 if project.isProjectFile(fn):
243 fn, language, editor.text(), 250 fn, language, editor.text(),
244 self.ssiCheckBox.isChecked(), rootPath) 251 self.ssiCheckBox.isChecked(), rootPath)
245 252
246 def __setHtml(self, filePath, html): 253 def __setHtml(self, filePath, html):
247 """ 254 """
248 Private method to set the HTML to the view and restore the scroll bars positions. 255 Private method to set the HTML to the view and restore the scroll bars
256 positions.
249 257
250 @param filePath file path of the previewed editor (string) 258 @param filePath file path of the previewed editor (string)
251 @param html processed HTML text ready to be shown (string) 259 @param html processed HTML text ready to be shown (string)
252 """ 260 """
253 self.__saveScrollBarPositions() 261 self.__saveScrollBarPositions()
297 305
298 if self.__previewedPath not in self.__scrollBarPositions: 306 if self.__previewedPath not in self.__scrollBarPositions:
299 return 307 return
300 308
301 frame = self.previewView.page().mainFrame() 309 frame = self.previewView.page().mainFrame()
302 frame.setScrollPosition(self.__scrollBarPositions[self.__previewedPath]) 310 frame.setScrollPosition(
311 self.__scrollBarPositions[self.__previewedPath])
303 312
304 if self.__hScrollBarAtEnd[self.__previewedPath]: 313 if self.__hScrollBarAtEnd[self.__previewedPath]:
305 frame.setScrollBarValue(Qt.Horizontal, frame.scrollBarMaximum(Qt.Horizontal)) 314 frame.setScrollBarValue(
315 Qt.Horizontal, frame.scrollBarMaximum(Qt.Horizontal))
306 316
307 if self.__vScrollBarAtEnd[self.__previewedPath]: 317 if self.__vScrollBarAtEnd[self.__previewedPath]:
308 frame.setScrollBarValue(Qt.Vertical, frame.scrollBarMaximum(Qt.Vertical)) 318 frame.setScrollBarValue(
319 Qt.Vertical, frame.scrollBarMaximum(Qt.Vertical))
309 320
310 @pyqtSlot(QUrl) 321 @pyqtSlot(QUrl)
311 def on_previewView_linkClicked(self, url): 322 def on_previewView_linkClicked(self, url):
312 """ 323 """
313 Private slot handling the clicking of a link. 324 Private slot handling the clicking of a link.
320 class PreviewProcessingThread(QThread): 331 class PreviewProcessingThread(QThread):
321 """ 332 """
322 Class implementing a thread to process some text into HTML usable by the 333 Class implementing a thread to process some text into HTML usable by the
323 previewer view. 334 previewer view.
324 335
325 @signal htmlReady(str,str) emitted with the file name and processed HTML to signal 336 @signal htmlReady(str,str) emitted with the file name and processed HTML
326 the availability of the processed HTML 337 to signal the availability of the processed HTML
327 """ 338 """
328 htmlReady = pyqtSignal(str, str) 339 htmlReady = pyqtSignal(str, str)
329 340
330 def __init__(self, parent=None): 341 def __init__(self, parent=None):
331 """ 342 """
342 Convert the given text to HTML. 353 Convert the given text to HTML.
343 354
344 @param filePath file path of the text (string) 355 @param filePath file path of the text (string)
345 @param language language of the text (string) 356 @param language language of the text (string)
346 @param text text to be processed (string) 357 @param text text to be processed (string)
347 @param ssiEnabled flag indicating to do some (limited) SSI processing (boolean) 358 @param ssiEnabled flag indicating to do some (limited) SSI processing
359 (boolean)
348 @param rootPath root path to be used for SSI processing (str) 360 @param rootPath root path to be used for SSI processing (str)
349 """ 361 """
350 with self.__lock: 362 with self.__lock:
351 self.__filePath = filePath 363 self.__filePath = filePath
352 self.__language = language 364 self.__language = language
369 text = self.__text 381 text = self.__text
370 ssiEnabled = self.__ssiEnabled 382 ssiEnabled = self.__ssiEnabled
371 rootPath = self.__rootPath 383 rootPath = self.__rootPath
372 self.__haveData = False 384 self.__haveData = False
373 385
374 html = self.__getHtml(language, text, ssiEnabled, filePath, rootPath) 386 html = self.__getHtml(language, text, ssiEnabled, filePath,
387 rootPath)
375 388
376 with self.__lock: 389 with self.__lock:
377 if not self.__haveData: 390 if not self.__haveData:
378 self.htmlReady.emit(filePath, html) 391 self.htmlReady.emit(filePath, html)
379 break 392 break
400 elif language == "Markdown": 413 elif language == "Markdown":
401 return self.__convertMarkdown(text) 414 return self.__convertMarkdown(text)
402 elif language == "ReST": 415 elif language == "ReST":
403 return self.__convertReST(text) 416 return self.__convertReST(text)
404 else: 417 else:
405 return self.trUtf8("<p>No preview available for this type of file.</p>") 418 return self.trUtf8(
419 "<p>No preview available for this type of file.</p>")
406 420
407 def __processSSI(self, txt, filename, root): 421 def __processSSI(self, txt, filename, root):
408 """ 422 """
409 Private method to process the given text for SSI statements. 423 Private method to process the given text for SSI statements.
410 424
411 Note: Only a limited subset of SSI statements are supported. 425 Note: Only a limited subset of SSI statements are supported.
412 426
413 @param txt text to be processed (string) 427 @param txt text to be processed (string)
414 @param filename name of the file associated with the given text (string) 428 @param filename name of the file associated with the given text
429 (string)
415 @param root directory of the document root (string) 430 @param root directory of the document root (string)
416 @return processed HTML (string) 431 @return processed HTML (string)
417 """ 432 """
418 if not filename: 433 if not filename:
419 return txt 434 return txt
459 """ 474 """
460 try: 475 try:
461 import docutils.core # __IGNORE_EXCEPTION__ __IGNORE_WARNING__ 476 import docutils.core # __IGNORE_EXCEPTION__ __IGNORE_WARNING__
462 except ImportError: 477 except ImportError:
463 return self.trUtf8( 478 return self.trUtf8(
464 """<p>ReStructuredText preview requires the <b>python-docutils</b> """ 479 """<p>ReStructuredText preview requires the"""
465 """package.<br/>Install it with your package manager or see """ 480 """ <b>python-docutils</b> package.<br/>Install it with"""
466 """<a href="http://pypi.python.org/pypi/docutils">this page.</a></p>""") 481 """ your package manager or see"""
467 482 """ <a href="http://pypi.python.org/pypi/docutils">"""
468 return docutils.core.publish_string(text, writer_name='html').decode("utf-8") 483 """this page.</a></p>""")
484
485 return docutils.core.publish_string(text, writer_name='html')\
486 .decode("utf-8")
469 487
470 def __convertMarkdown(self, text): 488 def __convertMarkdown(self, text):
471 """ 489 """
472 Private method to convert Markdown text into HTML. 490 Private method to convert Markdown text into HTML.
473 491
491 509
492 extensions = ['fenced_code', 'nl2br', 'extra'] 510 extensions = ['fenced_code', 'nl2br', 'extra']
493 511
494 # version 2.0 supports only extension names, not instances 512 # version 2.0 supports only extension names, not instances
495 if markdown.version_info[0] > 2 or \ 513 if markdown.version_info[0] > 2 or \
496 (markdown.version_info[0] == 2 and markdown.version_info[1] > 0): 514 (markdown.version_info[0] == 2 and
497 515 markdown.version_info[1] > 0):
498 class _StrikeThroughExtension(markdown.Extension): 516 class _StrikeThroughExtension(markdown.Extension):
499 """ 517 """
500 Class is placed here, because it depends on imported markdown, 518 Class is placed here, because it depends on imported markdown,
501 and markdown import is lazy. 519 and markdown import is lazy.
502 520
503 (see 521 (see http://achinghead.com/
504 <a href="http://achinghead.com/python-markdown-adding-insert-delete.html"> 522 python-markdown-adding-insert-delete.html this page for
505 this page for details</a>) 523 details)
506 """ 524 """
507 DEL_RE = r'(~~)(.*?)~~' 525 DEL_RE = r'(~~)(.*?)~~'
508 526
509 def extendMarkdown(self, md, md_globals): 527 def extendMarkdown(self, md, md_globals):
510 # Create the del pattern 528 # Create the del pattern
511 del_tag = markdown.inlinepatterns.SimpleTagPattern(self.DEL_RE, 'del') 529 del_tag = markdown.inlinepatterns.SimpleTagPattern(
530 self.DEL_RE, 'del')
512 # Insert del pattern into markdown parser 531 # Insert del pattern into markdown parser
513 md.inlinePatterns.add('del', del_tag, '>not_strong') 532 md.inlinePatterns.add('del', del_tag, '>not_strong')
514 533
515 extensions.append(_StrikeThroughExtension()) 534 extensions.append(_StrikeThroughExtension())
516 535
517 try: 536 try:
518 return markdown.markdown(text, extensions + ['mathjax']) 537 return markdown.markdown(text, extensions + ['mathjax'])
519 except (ImportError, ValueError): 538 except (ImportError, ValueError):
520 # markdown raises ValueError or ImportError, depends on version 539 # markdown raises ValueError or ImportError, depends on version
521 # It is not clear, how to distinguish missing mathjax from other errors. 540 # It is not clear, how to distinguish missing mathjax from other
522 # So keep going without mathjax. 541 # errors. So keep going without mathjax.
523 return markdown.markdown(text, extensions) 542 return markdown.markdown(text, extensions)

eric ide

mercurial