-
-
Save tritium21/f09ce417edd035fca488fd2b1f7ec2a8 to your computer and use it in GitHub Desktop.
Revisions
-
Stephan Sokolow revised this gist
Feb 22, 2018 . 1 changed file with 41 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -46,8 +46,10 @@ QPlainTextEdit) try: # pylint: disable=ungrouped-imports from enchant.utils import trim_suggestions except ImportError: # Older versions of PyEnchant as on *buntu 14.04 # pylint: disable=unused-argument def trim_suggestions(word, suggs, maxlen, calcdist=None): """API Polyfill for earlier versions of PyEnchant. @@ -95,6 +97,7 @@ def createSpellcheckContextMenu(self, pos): # Add a submenu for setting the spell-check language menu.addSeparator() menu.addMenu(self.createLanguagesMenu(menu)) menu.addMenu(self.createFormatsMenu(menu)) # Try to retrieve a menu of corrections for the right-clicked word spell_menu = self.createCorrectionsMenu( @@ -145,6 +148,22 @@ def createLanguagesMenu(self, parent=None): lang_menu.triggered.connect(self.cb_set_language) return lang_menu def createFormatsMenu(self, parent=None): """Create and return a menu for selecting the spell-check language.""" fmt_menu = QMenu("Format", parent) fmt_actions = QActionGroup(fmt_menu) curr_format = self.highlighter.chunkers() for name, chunkers in (('Text', []), ('HTML', [tokenize.HTMLChunker])): action = fmt_actions.addAction(name) action.setCheckable(True) action.setChecked(chunkers == curr_format) action.setData(chunkers) fmt_menu.addAction(action) fmt_menu.triggered.connect(self.cb_set_format) return fmt_menu def cursorForMisspelling(self, pos): """Return a cursor selecting the misspelled word at ``pos`` or ``None`` @@ -182,6 +201,12 @@ def cb_set_language(self, action): lang = action.data() self.highlighter.setDict(enchant.Dict(lang)) def cb_set_format(self, action): """Event handler for 'Language' menu entries.""" chunkers = action.data() self.highlighter.setChunkers(chunkers) # TODO: Emit an event so this menu can trigger other things class EnchantHighlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" tokenizer = None @@ -199,21 +224,32 @@ def __init__(self, *args): # Initialize private members self._sp_dict = None self._chunkers = [] def chunkers(self): """Gets the chunkers in use""" return self._chunkers def dict(self): """Gets the spelling dictionary in use""" return self._sp_dict def setChunkers(self, chunkers): """Sets the list of chunkers to be used""" self._chunkers = chunkers self.setDict(self.dict()) # FIXME: Revert self._chunkers on failure to ensure consistent state def setDict(self, sp_dict): """Sets the spelling dictionary to be used""" try: self.tokenizer = tokenize.get_tokenizer(sp_dict.tag, chunkers=self._chunkers, filters=self.token_filters) except TokenizerNotFoundError: # Fall back to the "good for most euro languages" English tokenizer self.tokenizer = tokenize.get_tokenizer( chunkers=self._chunkers, filters=self.token_filters) self._sp_dict = sp_dict self.rehighlight() -
Stephan Sokolow revised this gist
Feb 21, 2018 . 1 changed file with 4 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -82,7 +82,7 @@ def contextMenuEvent(self, event): self.focusInEvent(QFocusEvent(QEvent.FocusIn)) def createSpellcheckContextMenu(self, pos): """Create and return an augmented default context menu. This may be used as an alternative to the QPoint-taking form of ``createStandardContextMenu`` and will work on pre-5.5 Qt. @@ -146,7 +146,7 @@ def createLanguagesMenu(self, parent=None): return lang_menu def cursorForMisspelling(self, pos): """Return a cursor selecting the misspelled word at ``pos`` or ``None`` This leverages the fact that QPlainTextEdit already has a system for processing its contents in limited-size blocks to keep things fast. @@ -169,7 +169,7 @@ def cursorForMisspelling(self, pos): return None def cb_correct_word(self, action): # pylint: disable=no-self-use """Event handler for 'Spelling Suggestions' entries.""" cursor, word = action.data() cursor.beginEditBlock() @@ -178,6 +178,7 @@ def cb_correct_word(self, action): # pylint: disable=no-self-use cursor.endEditBlock() def cb_set_language(self, action): """Event handler for 'Language' menu entries.""" lang = action.data() self.highlighter.setDict(enchant.Dict(lang)) -
Stephan Sokolow revised this gist
Feb 21, 2018 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -106,7 +106,7 @@ def createSpellcheckContextMenu(self, pos): return menu def createCorrectionsMenu(self, cursor, parent=None): """Create and return a menu for correcting the selected word.""" if not cursor: return None @@ -129,7 +129,7 @@ def createCorrectionsMenu(self, cursor, parent): return None def createLanguagesMenu(self, parent=None): """Create and return a menu for selecting the spell-check language.""" curr_lang = self.highlighter.dict().tag lang_menu = QMenu("Language", parent) -
Stephan Sokolow revised this gist
Feb 21, 2018 . 1 changed file with 30 additions and 20 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -96,29 +96,39 @@ def createSpellcheckContextMenu(self, pos): menu.addSeparator() menu.addMenu(self.createLanguagesMenu(menu)) # Try to retrieve a menu of corrections for the right-clicked word spell_menu = self.createCorrectionsMenu( self.cursorForMisspelling(pos), menu) if spell_menu: menu.insertSeparator(menu.actions()[0]) menu.insertMenu(menu.actions()[0], spell_menu) return menu def createCorrectionsMenu(self, cursor, parent): """Create and return a menu for correcting the selected word.""" if not cursor: return None text = cursor.selectedText() suggests = trim_suggestions(text, self.highlighter.dict().suggest(text), self.max_suggestions) spell_menu = QMenu('Spelling Suggestions', parent) for word in suggests: action = QAction(word, spell_menu) action.setData((cursor, word)) spell_menu.addAction(action) # Only return the menu if it's non-empty if spell_menu.actions(): spell_menu.triggered.connect(self.cb_correct_word) return spell_menu return None def createLanguagesMenu(self, parent): """Create and return a menu for selecting the spell-check language.""" curr_lang = self.highlighter.dict().tag -
Stephan Sokolow revised this gist
Feb 21, 2018 . 1 changed file with 26 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -42,7 +42,8 @@ from PyQt5.QtCore import QEvent from PyQt5.QtGui import (QFocusEvent, QSyntaxHighlighter, QTextBlockUserData, QTextCharFormat, QTextCursor) from PyQt5.QtWidgets import (QAction, QActionGroup, QApplication, QMenu, QPlainTextEdit) try: from enchant.utils import trim_suggestions @@ -91,6 +92,10 @@ def createSpellcheckContextMenu(self, pos): except TypeError: # Before Qt 5.5 menu = self.createStandardContextMenu() # Add a submenu for setting the spell-check language menu.addSeparator() menu.addMenu(self.createLanguagesMenu(menu)) # Check if the click happened inside a misspelling cursor = self.cursorForMisspelling(pos) @@ -114,6 +119,22 @@ def createSpellcheckContextMenu(self, pos): return menu def createLanguagesMenu(self, parent): """Create and return a menu for selecting the spell-check language.""" curr_lang = self.highlighter.dict().tag lang_menu = QMenu("Language", parent) lang_actions = QActionGroup(lang_menu) for lang in enchant.list_languages(): action = lang_actions.addAction(lang) action.setCheckable(True) action.setChecked(lang == curr_lang) action.setData(lang) lang_menu.addAction(action) lang_menu.triggered.connect(self.cb_set_language) return lang_menu def cursorForMisspelling(self, pos): """Select the misspelled word at ``pos`` or return ``None``. @@ -146,6 +167,10 @@ def cb_correct_word(self, action): # pylint: disable=no-self-use cursor.insertText(word) cursor.endEditBlock() def cb_set_language(self, action): lang = action.data() self.highlighter.setDict(enchant.Dict(lang)) class EnchantHighlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" tokenizer = None -
Stephan Sokolow revised this gist
Feb 20, 2018 . 1 changed file with 60 additions and 26 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,10 +40,20 @@ # pylint: disable=no-name-in-module from PyQt5.Qt import Qt from PyQt5.QtCore import QEvent from PyQt5.QtGui import (QFocusEvent, QSyntaxHighlighter, QTextBlockUserData, QTextCharFormat, QTextCursor) from PyQt5.QtWidgets import QAction, QApplication, QMenu, QPlainTextEdit try: from enchant.utils import trim_suggestions except ImportError: # Older versions of PyEnchant as on *buntu 14.04 def trim_suggestions(word, suggs, maxlen, calcdist=None): """API Polyfill for earlier versions of PyEnchant. TODO: Make this actually do some sorting """ return suggs[:maxlen] class SpellTextEdit(QPlainTextEdit): """QPlainTextEdit subclass which does spell-checking using PyEnchant""" @@ -81,36 +91,51 @@ def createSpellcheckContextMenu(self, pos): except TypeError: # Before Qt 5.5 menu = self.createStandardContextMenu() # Check if the click happened inside a misspelling cursor = self.cursorForMisspelling(pos) if cursor: text = cursor.selectedText() suggests = trim_suggestions(text, self.highlighter.dict().suggest(text), self.max_suggestions) spell_menu = QMenu('Spelling Suggestions', menu) for word in suggests: action = QAction(word, spell_menu) action.setData((cursor, word)) spell_menu.addAction(action) # Only prepend "Spelling Suggestions" submenu if it's non-empty if spell_menu.actions(): spell_menu.triggered.connect(self.cb_correct_word) menu.insertSeparator(menu.actions()[0]) menu.insertMenu(menu.actions()[0], spell_menu) return menu def cursorForMisspelling(self, pos): """Select the misspelled word at ``pos`` or return ``None``. This leverages the fact that QPlainTextEdit already has a system for processing its contents in limited-size blocks to keep things fast. """ cursor = self.cursorForPosition(pos) misspelled_words = getattr(cursor.block().userData(), 'misspelled', []) # If the cursor is within a misspelling, select the word for (start, end) in misspelled_words: if start <= cursor.positionInBlock() <= end: block_pos = cursor.block().position() cursor.setPosition(block_pos + start, QTextCursor.MoveAnchor) cursor.setPosition(block_pos + end, QTextCursor.KeepAnchor) break if cursor.hasSelection(): return cursor else: return None def cb_correct_word(self, action): # pylint: disable=no-self-use """Event handler Handler for 'Spelling Suggestions' entries.""" @@ -161,9 +186,18 @@ def highlightBlock(self, text): if not self._sp_dict: return # Build a list of all misspelled words and highlight them misspellings = [] for (word, pos) in self.tokenizer(text): if not self._sp_dict.check(word): self.setFormat(pos, len(word), self.err_format) misspellings.append((pos, pos + len(word))) # Store the list so the context menu can reuse this tokenization pass # (Block-relative values so editing other blocks won't invalidate them) data = QTextBlockUserData() data.misspelled = misspellings self.setCurrentBlockUserData(data) if __name__ == '__main__': app = QApplication(sys.argv) -
Stephan Sokolow revised this gist
Feb 20, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -106,7 +106,7 @@ def createSpellcheckContextMenu(self, pos): spell_menu.addAction(action) # Only prepend "Spelling Suggestions" submenu if it's non-empty if spell_menu.actions(): menu.insertSeparator(menu.actions()[0]) menu.insertMenu(menu.actions()[0], spell_menu) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 8 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -126,18 +126,19 @@ class EnchantHighlighter(QSyntaxHighlighter): tokenizer = None token_filters = (tokenize.EmailFilter, tokenize.URLFilter) # Define the spellcheck style once and just assign it as necessary # XXX: Does QSyntaxHighlighter.setFormat handle keeping this from # clobbering styles set in the data itself? err_format = QTextCharFormat() err_format.setUnderlineColor(Qt.red) err_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) # Initialize private members self._sp_dict = None def dict(self): """Gets the spelling dictionary in use""" return self._sp_dict -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 8 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -46,7 +46,11 @@ class SpellTextEdit(QPlainTextEdit): """QPlainTextEdit subclass which does spell-checking using PyEnchant""" # Clamping value for words like "regex" which suggest so many things that # the menu runs from the top to the bottom of the screen and spills over # into a second column. max_suggestions = 20 def __init__(self, *args): QPlainTextEdit.__init__(self, *args) @@ -88,15 +92,15 @@ def createSpellcheckContextMenu(self, pos): # suggestions if it is if cursor.hasSelection(): text = cursor.selectedText() spell_dict = self.highlighter.dict() if not spell_dict.check(text): spell_menu = QMenu('Spelling Suggestions', menu) spell_menu.triggered.connect(self.cb_correct_word) # TODO: Use enchant.utils.trim_suggestions once I don't have to # support old PyEnchant versions. for word in spell_dict.suggest(text)[:self.max_suggestions]: action = QAction(word, spell_menu) action.setData((cursor, word)) spell_menu.addAction(action) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 24 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -57,16 +57,31 @@ def __init__(self, *args): def contextMenuEvent(self, event): """Custom context menu handler to add a spelling suggestions submenu""" popup_menu = self.createSpellcheckContextMenu(event.pos()) popup_menu.exec_(event.globalPos()) # Fix bug observed in Qt 5.2.1 on *buntu 14.04 LTS where: # 1. The cursor remains invisible after closing the context menu # 2. Keyboard input causes it to appear, but it doesn't blink # 3. Switching focus away from and back to the window fixes it self.focusInEvent(QFocusEvent(QEvent.FocusIn)) def createSpellcheckContextMenu(self, pos): """Create and return a context menu with spelling suggestions. This may be used as an alternative to the QPoint-taking form of ``createStandardContextMenu`` and will work on pre-5.5 Qt. """ try: # Recommended for Qt 5.5+ (Allows contextual Qt-provided entries) menu = self.createStandardContextMenu(pos) except TypeError: # Before Qt 5.5 menu = self.createStandardContextMenu() # Create a cursor object selecting the word under the mouse pointer # FIXME: This method tokenizes differently than the highlighter and # allows one to right-click an un-highlighted URL and find a # suggestions menu when there shouldn't be one. cursor = self.cursorForPosition(pos) cursor.select(QTextCursor.WordUnderCursor) # Check if the selected word is misspelled and offer spelling @@ -76,7 +91,7 @@ def contextMenuEvent(self, event): spl_dict = self.highlighter.dict() if not spl_dict.check(text): spell_menu = QMenu('Spelling Suggestions', menu) spell_menu.triggered.connect(self.cb_correct_word) # TODO: Use enchant.utils.trim_suggestions once I don't have to @@ -88,16 +103,10 @@ def contextMenuEvent(self, event): # Only prepend "Spelling Suggestions" submenu if it's non-empty if len(spell_menu.actions()) != 0: menu.insertSeparator(menu.actions()[0]) menu.insertMenu(menu.actions()[0], spell_menu) return menu def cb_correct_word(self, action): # pylint: disable=no-self-use """Event handler Handler for 'Spelling Suggestions' entries.""" -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 7 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -110,13 +110,13 @@ def cb_correct_word(self, action): # pylint: disable=no-self-use class EnchantHighlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" tokenizer = None token_filters = (tokenize.EmailFilter, tokenize.URLFilter) def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) self._sp_dict = None # Define the spellcheck style once and assign it as necessary # XXX: This will probably cause problems if I need to set underline @@ -127,15 +127,15 @@ def __init__(self, *args): def dict(self): """Gets the spelling dictionary in use""" return self._sp_dict def setDict(self, sp_dict): """Sets the spelling dictionary to be used""" self._sp_dict = sp_dict try: self.tokenizer = tokenize.get_tokenizer( self._sp_dict.tag, filters=self.token_filters) except TokenizerNotFoundError: # Fall back to the "good for most euro languages" English tokenizer self.tokenizer = tokenize.get_tokenizer(filters=self.token_filters) @@ -144,11 +144,11 @@ def setDict(self, sp_dict): def highlightBlock(self, text): """Overridden QSyntaxHighlighter method to apply the highlight""" if not self._sp_dict: return for (word, pos) in self.tokenizer(text): if not self._sp_dict.check(word): self.setFormat(pos, len(word), self.err_format) if __name__ == '__main__': -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 10 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -57,24 +57,31 @@ def __init__(self, *args): def contextMenuEvent(self, event): """Custom context menu handler to add a spelling suggestions submenu""" # TODO: Use the version of createStandardContextMenu which takes # QPoint(event.x(), event.y()) once I don't need to support Qt versions # before 5.5. (It's recommended as it allows more contextual contents) popup_menu = self.createStandardContextMenu() # Create a cursor object selecting the word under the mouse pointer # FIXME: This method tokenizes differently than the highlighter and # allows one to right-click an un-highlighted URL and find a # suggestions menu when there shouldn't be one. cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) # Check if the selected word is misspelled and offer spelling # suggestions if it is if cursor.hasSelection(): text = cursor.selectedText() spl_dict = self.highlighter.dict() if not spl_dict.check(text): spell_menu = QMenu('Spelling Suggestions') spell_menu.triggered.connect(self.cb_correct_word) # TODO: Use enchant.utils.trim_suggestions once I don't have to # support old PyEnchant versions. for word in spl_dict.suggest(text)[:self.max_suggestion_limit]: action = QAction(word, spell_menu) action.setData((cursor, word)) spell_menu.addAction(action) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 9 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -39,7 +39,9 @@ # pylint: disable=no-name-in-module from PyQt5.Qt import Qt from PyQt5.QtCore import QEvent from PyQt5.QtGui import (QFocusEvent, QSyntaxHighlighter, QTextCharFormat, QTextCursor) from PyQt5.QtWidgets import QAction, QApplication, QMenu, QPlainTextEdit class SpellTextEdit(QPlainTextEdit): @@ -84,6 +86,12 @@ def contextMenuEvent(self, event): popup_menu.exec_(event.globalPos()) # Fix bug observed in Qt 5.2.1 on *buntu 14.04 LTS where: # 1. The cursor remains invisible after closing the context menu # 2. Keyboard input causes it to appear, but it doesn't blink # 3. Switching focus away from and back to the window fixes it self.focusInEvent(QFocusEvent(QEvent.FocusIn)) def cb_correct_word(self, action): # pylint: disable=no-self-use """Event handler Handler for 'Spelling Suggestions' entries.""" cursor, word = action.data() -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 4 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -110,6 +110,10 @@ def __init__(self, *args): self.err_format.setUnderlineColor(Qt.red) self.err_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) def dict(self): """Gets the spelling dictionary in use""" return self.sp_dict def setDict(self, sp_dict): """Sets the spelling dictionary to be used""" self.sp_dict = sp_dict -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 7 additions and 19 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -41,7 +41,6 @@ from PyQt5.Qt import Qt from PyQt5.QtGui import QSyntaxHighlighter, QTextCharFormat, QTextCursor from PyQt5.QtWidgets import QAction, QApplication, QMenu, QPlainTextEdit class SpellTextEdit(QPlainTextEdit): """QPlainTextEdit subclass which does spell-checking using PyEnchant""" @@ -69,13 +68,13 @@ def contextMenuEvent(self, event): text = cursor.selectedText() if not self.highlighter.sp_dict.check(text): spell_menu = QMenu('Spelling Suggestions') spell_menu.triggered.connect(self.cb_correct_word) # TODO: Use enchant.utils.trim_suggestions once I don't have to # support old PyEnchant versions. for word in self.highlighter.sp_dict.suggest(text)[:max_sugg]: action = QAction(word, spell_menu) action.setData((cursor, word)) spell_menu.addAction(action) # Only prepend "Spelling Suggestions" submenu if it's non-empty @@ -85,14 +84,15 @@ def contextMenuEvent(self, event): popup_menu.exec_(event.globalPos()) def cb_correct_word(self, action): # pylint: disable=no-self-use """Event handler Handler for 'Spelling Suggestions' entries.""" cursor, word = action.data() cursor.beginEditBlock() cursor.removeSelectedText() cursor.insertText(word) cursor.endEditBlock() class EnchantHighlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" token_filters = (tokenize.EmailFilter, tokenize.URLFilter) @@ -129,21 +129,9 @@ def highlightBlock(self, text): return for (word, pos) in self.tokenizer(text): if not self.sp_dict.check(word): self.setFormat(pos, len(word), self.err_format) if __name__ == '__main__': app = QApplication(sys.argv) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 33 additions and 25 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -31,10 +31,11 @@ __author__ = 'John Schember; Stephan Sokolow' __docformat__ = 'restructuredtext en' import sys import enchant from enchant import tokenize from enchant.errors import TokenizerNotFoundError # pylint: disable=no-name-in-module from PyQt5.Qt import Qt @@ -44,37 +45,40 @@ class SpellTextEdit(QPlainTextEdit): """QPlainTextEdit subclass which does spell-checking using PyEnchant""" max_suggestion_limit = 20 def __init__(self, *args): QPlainTextEdit.__init__(self, *args) # Start with a default dictionary based on the current locale. self.highlighter = EnchantHighlighter(self.document()) self.highlighter.setDict(enchant.Dict()) def contextMenuEvent(self, event): """Custom context menu handler to add a spelling suggestions submenu""" max_sugg = self.max_suggestion_limit popup_menu = self.createStandardContextMenu() # Create a cursor object selecting the word under the mouse pointer cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) # Check if the selected word is misspelled and offer spelling # suggestions if it is if cursor.hasSelection(): text = cursor.selectedText() if not self.highlighter.sp_dict.check(text): spell_menu = QMenu('Spelling Suggestions') # TODO: Use enchant.utils.trim_suggestions once I don't have to # support old PyEnchant versions. for word in self.highlighter.sp_dict.suggest(text)[:max_sugg]: action = SpellAction(word, spell_menu) action.setData(cursor) action.correct.connect(self.correctWord) spell_menu.addAction(action) # Only prepend "Spelling Suggestions" submenu if it's non-empty if len(spell_menu.actions()) != 0: popup_menu.insertSeparator(popup_menu.actions()[0]) popup_menu.insertMenu(popup_menu.actions()[0], spell_menu) @@ -91,39 +95,43 @@ def correctWord(self, cursor, word): class EnchantHighlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" token_filters = (tokenize.EmailFilter, tokenize.URLFilter) def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) self.sp_dict = None self.tokenizer = None # Define the spellcheck style once and assign it as necessary # XXX: This will probably cause problems if I need to set underline # styles on rich text. self.err_format = QTextCharFormat() self.err_format.setUnderlineColor(Qt.red) self.err_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) def setDict(self, sp_dict): """Sets the spelling dictionary to be used""" self.sp_dict = sp_dict try: self.tokenizer = tokenize.get_tokenizer( sp_dict.tag, filters=self.token_filters) except TokenizerNotFoundError: # Fall back to the "good for most euro languages" English tokenizer self.tokenizer = tokenize.get_tokenizer(filters=self.token_filters) self.rehighlight() def highlightBlock(self, text): """Overridden QSyntaxHighlighter method to apply the highlight""" if not self.sp_dict: return for (word, pos) in self.tokenizer(text): if not self.sp_dict.check(word): self.setFormat(pos, len(word), self.err_format) class SpellAction(QAction): # pylint: disable=too-few-public-methods """A special QAction that returns the text in a signal.""" -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 7 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """QPlainTextEdit With Inline Spell Check Original PyQt4 Version: https://nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/ Copyright 2009 John Schember @@ -50,7 +50,7 @@ def __init__(self, *args): # Default dictionary based on the current locale. self.sp_dict = enchant.Dict() self.highlighter = EnchantHighlighter(self.document()) self.highlighter.setDict(self.sp_dict) def contextMenuEvent(self, event): @@ -67,13 +67,14 @@ def contextMenuEvent(self, event): text = cursor.selectedText() if not self.sp_dict.check(text): spell_menu = QMenu('Spelling Suggestions') for word in self.sp_dict.suggest(text): action = SpellAction(word, spell_menu) action.setData(cursor) action.correct.connect(self.correctWord) spell_menu.addAction(action) # Only add the Spelling Suggestions submenu if it's non-empty if len(spell_menu.actions()) != 0: popup_menu.insertSeparator(popup_menu.actions()[0]) popup_menu.insertMenu(popup_menu.actions()[0], spell_menu) @@ -88,7 +89,7 @@ def correctWord(self, cursor, word): cursor.endEditBlock() class EnchantHighlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" WORDS = r'(?iu)\w([\w\']*\w)?' -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 6 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -60,7 +60,6 @@ def contextMenuEvent(self, event): # Select the word under the cursor. cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) # Check if the selected word is misspelled and offer spelling # suggestions if it is. @@ -70,6 +69,7 @@ def contextMenuEvent(self, event): spell_menu = QMenu('Spelling Suggestions') for word in self.sp_dict.suggest(text): action = SpellAction(word, spell_menu) action.setData(cursor) action.correct.connect(self.correctWord) spell_menu.addAction(action) # Only add the spelling suggests to the menu if there are @@ -80,10 +80,8 @@ def contextMenuEvent(self, event): popup_menu.exec_(event.globalPos()) def correctWord(self, cursor, word): """Replaces the text selected by ``cursor`` with ``word``.""" cursor.beginEditBlock() cursor.removeSelectedText() cursor.insertText(word) @@ -129,13 +127,13 @@ def highlightBlock(self, text): class SpellAction(QAction): # pylint: disable=too-few-public-methods """A special QAction that returns the text in a signal.""" correct = pyqtSignal((QTextCursor, str)) def __init__(self, *args): QAction.__init__(self, *args) self.triggered.connect( lambda x: self.correct.emit(self.data(), self.text())) if __name__ == '__main__': app = QApplication(sys.argv) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 8 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -92,7 +92,7 @@ def correctWord(self, word): class Highlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" WORDS = r'(?iu)\w([\w\']*\w)?' def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) @@ -108,17 +108,20 @@ def highlightBlock(self, text): if not self.sp_dict: return char_format = QTextCharFormat() char_format.setUnderlineColor(Qt.red) char_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) # TODO: Don't spell-check "words" within URLs for word_object in re.finditer(self.WORDS, text): word = word_object.group() # PyEnchant throws an exception if we ask it to check empty strings # and we don't want it to spell-check purely numeric "words" if len(word) == 0 or word.isdigit(): continue if not self.sp_dict.check(word): self.setFormat(word_object.start(), word_object.end() - word_object.start(), char_format) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 11 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """QPlainTextEdit With In Line Spell Check @@ -36,9 +36,11 @@ import enchant # pylint: disable=no-name-in-module from PyQt5.Qt import Qt from PyQt5.QtGui import QSyntaxHighlighter, QTextCharFormat, QTextCursor from PyQt5.QtWidgets import QAction, QApplication, QMenu, QPlainTextEdit from PyQt5.QtCore import pyqtSignal class SpellTextEdit(QPlainTextEdit): """QPlainTextEdit subclass which does spell-checking using PyEnchant""" @@ -63,7 +65,7 @@ def contextMenuEvent(self, event): # Check if the selected word is misspelled and offer spelling # suggestions if it is. if cursor.hasSelection(): text = cursor.selectedText() if not self.sp_dict.check(text): spell_menu = QMenu('Spelling Suggestions') for word in self.sp_dict.suggest(text): @@ -90,7 +92,7 @@ def correctWord(self, word): class Highlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" WORDS = r'(?iu)[\w\']+' def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) @@ -106,7 +108,6 @@ def highlightBlock(self, text): if not self.sp_dict: return char_format = QTextCharFormat() char_format.setUnderlineColor(Qt.red) @@ -122,16 +123,16 @@ def highlightBlock(self, text): word_object.end() - word_object.start(), char_format) class SpellAction(QAction): # pylint: disable=too-few-public-methods """A special QAction that returns the text in a signal.""" correct = pyqtSignal(str) def __init__(self, *args): QAction.__init__(self, *args) self.triggered.connect(lambda x: self.correct.emit( self.text())) if __name__ == '__main__': app = QApplication(sys.argv) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 22 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,10 +4,31 @@ Source: https://nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/ Copyright 2009 John Schember Copyright 2018 Stephan Sokolow Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ __license__ = 'MIT' __author__ = 'John Schember; Stephan Sokolow' __docformat__ = 'restructuredtext en' import re -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 4 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -92,6 +92,10 @@ def highlightBlock(self, text): char_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) for word_object in re.finditer(self.WORDS, text): # Don't spell-check purely numeric "words" if word_object.group().isdigit(): continue if not self.sp_dict.check(word_object.group()): self.setFormat(word_object.start(), word_object.end() - word_object.start(), char_format) -
Stephan Sokolow renamed this gist
Feb 19, 2018 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -45,7 +45,7 @@ def contextMenuEvent(self, event): text = unicode(cursor.selectedText()) if not self.sp_dict.check(text): spell_menu = QMenu('Spelling Suggestions') for word in self.sp_dict.suggest(text): action = SpellAction(word, spell_menu) action.correct.connect(self.correctWord) spell_menu.addAction(action) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 27 additions and 38 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,35 +7,31 @@ """ __license__ = 'MIT' __author__ = '2009, John Schember ' __docformat__ = 'restructuredtext en' import re import sys import enchant from PyQt4.Qt import (QAction, QApplication, QMenu, QPlainTextEdit, QSyntaxHighlighter, QTextCharFormat, QTextCursor, Qt) from PyQt4.QtCore import pyqtSignal class SpellTextEdit(QPlainTextEdit): """QPlainTextEdit subclass which does spell-checking using PyEnchant""" def __init__(self, *args): QPlainTextEdit.__init__(self, *args) # Default dictionary based on the current locale. self.sp_dict = enchant.Dict() self.highlighter = Highlighter(self.document()) self.highlighter.setDict(self.sp_dict) def contextMenuEvent(self, event): """Custom context menu handler to add a spelling suggestions submenu""" popup_menu = self.createStandardContextMenu() # Select the word under the cursor. @@ -47,7 +43,7 @@ def contextMenuEvent(self, event): # suggestions if it is. if cursor.hasSelection(): text = unicode(cursor.selectedText()) if not self.sp_dict.check(text): spell_menu = QMenu('Spelling Suggestions') for word in self.dict.suggest(text): action = SpellAction(word, spell_menu) @@ -62,50 +58,47 @@ def contextMenuEvent(self, event): popup_menu.exec_(event.globalPos()) def correctWord(self, word): """Replaces the selected text with word.""" cursor = self.textCursor() cursor.beginEditBlock() cursor.removeSelectedText() cursor.insertText(word) cursor.endEditBlock() class Highlighter(QSyntaxHighlighter): """QSyntaxHighlighter subclass which consults a PyEnchant dictionary""" WORDS = ur'(?iu)[\w\']+' def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) self.sp_dict = None def setDict(self, sp_dict): """Sets the spelling dictionary to be used""" self.sp_dict = sp_dict def highlightBlock(self, text): """Overridden QSyntaxHighlighter method to apply the highlight""" if not self.sp_dict: return text = unicode(text) char_format = QTextCharFormat() char_format.setUnderlineColor(Qt.red) char_format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) for word_object in re.finditer(self.WORDS, text): if not self.sp_dict.check(word_object.group()): self.setFormat(word_object.start(), word_object.end() - word_object.start(), char_format) class SpellAction(QAction): """A special QAction that returns the text in a signal.""" correct = pyqtSignal(unicode) @@ -115,14 +108,10 @@ def __init__(self, *args): self.triggered.connect(lambda x: self.correct.emit( unicode(self.text()))) if __name__ == '__main__': app = QApplication(sys.argv) spellEdit = SpellTextEdit() spellEdit.show() sys.exit(app.exec_()) -
Stephan Sokolow revised this gist
Feb 19, 2018 . 1 changed file with 3 additions and 13 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,9 +17,7 @@ from PyQt4.Qt import QAction from PyQt4.Qt import QApplication from PyQt4.Qt import QMenu from PyQt4.Qt import QPlainTextEdit from PyQt4.Qt import QSyntaxHighlighter from PyQt4.Qt import QTextCharFormat @@ -37,26 +35,18 @@ def __init__(self, *args): self.highlighter = Highlighter(self.document()) self.highlighter.setDict(self.dict) def contextMenuEvent(self, event): popup_menu = self.createStandardContextMenu() # Select the word under the cursor. cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) self.setTextCursor(cursor) # Check if the selected word is misspelled and offer spelling # suggestions if it is. if cursor.hasSelection(): text = unicode(cursor.selectedText()) if not self.dict.check(text): spell_menu = QMenu('Spelling Suggestions') for word in self.dict.suggest(text): -
Stephan Sokolow created this gist
Feb 19, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,138 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """QPlainTextEdit With In Line Spell Check Source: https://nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/ """ __license__ = 'MIT' __copyright__ = '2009, John Schember ' __docformat__ = 'restructuredtext en' import re import sys import enchant from PyQt4.Qt import QAction from PyQt4.Qt import QApplication from PyQt4.Qt import QEvent from PyQt4.Qt import QMenu from PyQt4.Qt import QMouseEvent from PyQt4.Qt import QPlainTextEdit from PyQt4.Qt import QSyntaxHighlighter from PyQt4.Qt import QTextCharFormat from PyQt4.Qt import QTextCursor from PyQt4.Qt import Qt from PyQt4.QtCore import pyqtSignal class SpellTextEdit(QPlainTextEdit): def __init__(self, *args): QPlainTextEdit.__init__(self, *args) # Default dictionary based on the current locale. self.dict = enchant.Dict() self.highlighter = Highlighter(self.document()) self.highlighter.setDict(self.dict) def mousePressEvent(self, event): if event.button() == Qt.RightButton: # Rewrite the mouse event to a left button event so the cursor is # moved to the location of the pointer. event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QPlainTextEdit.mousePressEvent(self, event) def contextMenuEvent(self, event): popup_menu = self.createStandardContextMenu() # Select the word under the cursor. cursor = self.textCursor() cursor.select(QTextCursor.WordUnderCursor) self.setTextCursor(cursor) # Check if the selected word is misspelled and offer spelling # suggestions if it is. if self.textCursor().hasSelection(): text = unicode(self.textCursor().selectedText()) if not self.dict.check(text): spell_menu = QMenu('Spelling Suggestions') for word in self.dict.suggest(text): action = SpellAction(word, spell_menu) action.correct.connect(self.correctWord) spell_menu.addAction(action) # Only add the spelling suggests to the menu if there are # suggestions. if len(spell_menu.actions()) != 0: popup_menu.insertSeparator(popup_menu.actions()[0]) popup_menu.insertMenu(popup_menu.actions()[0], spell_menu) popup_menu.exec_(event.globalPos()) def correctWord(self, word): ''' Replaces the selected text with word. ''' cursor = self.textCursor() cursor.beginEditBlock() cursor.removeSelectedText() cursor.insertText(word) cursor.endEditBlock() class Highlighter(QSyntaxHighlighter): WORDS = u'(?iu)[\w\']+' def __init__(self, *args): QSyntaxHighlighter.__init__(self, *args) self.dict = None def setDict(self, dict): self.dict = dict def highlightBlock(self, text): if not self.dict: return text = unicode(text) format = QTextCharFormat() format.setUnderlineColor(Qt.red) format.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline) for word_object in re.finditer(self.WORDS, text): if not self.dict.check(word_object.group()): self.setFormat(word_object.start(), word_object.end() - word_object.start(), format) class SpellAction(QAction): ''' A special QAction that returns the text in a signal. ''' correct = pyqtSignal(unicode) def __init__(self, *args): QAction.__init__(self, *args) self.triggered.connect(lambda x: self.correct.emit( unicode(self.text()))) def main(args=sys.argv): app = QApplication(args) spellEdit = SpellTextEdit() spellEdit.show() return app.exec_() if __name__ == '__main__': sys.exit(main())