UI/Previewer.py

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

eric ide

mercurial