|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2021 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing QTextEdit and QPlainTextEdit widgets with embedded spell |
|
8 checking. |
|
9 """ |
|
10 |
|
11 import contextlib |
|
12 |
|
13 try: |
|
14 import enchant |
|
15 import enchant.tokenize |
|
16 from enchant.errors import TokenizerNotFoundError, DictNotFoundError |
|
17 from enchant.utils import trim_suggestions |
|
18 ENCHANT_AVAILABLE = True |
|
19 except ImportError: |
|
20 ENCHANT_AVAILABLE = False |
|
21 |
|
22 from PyQt6.QtCore import pyqtSlot, Qt, QCoreApplication |
|
23 from PyQt6.QtGui import ( |
|
24 QAction, QActionGroup, QSyntaxHighlighter, QTextBlockUserData, |
|
25 QTextCharFormat, QTextCursor |
|
26 ) |
|
27 from PyQt6.QtWidgets import QMenu, QTextEdit, QPlainTextEdit |
|
28 |
|
29 if ENCHANT_AVAILABLE: |
|
30 class SpellCheckMixin(): |
|
31 """ |
|
32 Class implementing the spell-check mixin for the widget classes. |
|
33 """ |
|
34 # don't show more than this to keep the menu manageable |
|
35 MaxSuggestions = 20 |
|
36 |
|
37 # default language to be used when no other is set |
|
38 DefaultLanguage = None |
|
39 |
|
40 # default user lists |
|
41 DefaultUserWordList = None |
|
42 DefaultUserExceptionList = None |
|
43 |
|
44 def __init__(self): |
|
45 """ |
|
46 Constructor |
|
47 """ |
|
48 self.__highlighter = EnchantHighlighter(self.document()) |
|
49 try: |
|
50 # Start with a default dictionary based on the current locale |
|
51 # or the configured default language. |
|
52 spellDict = enchant.DictWithPWL( |
|
53 SpellCheckMixin.DefaultLanguage, |
|
54 SpellCheckMixin.DefaultUserWordList, |
|
55 SpellCheckMixin.DefaultUserExceptionList |
|
56 ) |
|
57 except DictNotFoundError: |
|
58 try: |
|
59 # Use English dictionary if no locale dictionary is |
|
60 # available or the default one could not be found. |
|
61 spellDict = enchant.DictWithPWL( |
|
62 "en", |
|
63 SpellCheckMixin.DefaultUserWordList, |
|
64 SpellCheckMixin.DefaultUserExceptionList |
|
65 ) |
|
66 except DictNotFoundError: |
|
67 # Still no dictionary could be found. Forget about spell |
|
68 # checking. |
|
69 spellDict = None |
|
70 |
|
71 self.__highlighter.setDict(spellDict) |
|
72 |
|
73 def contextMenuEvent(self, evt): |
|
74 """ |
|
75 Protected method to handle context menu events to add a spelling |
|
76 suggestions submenu. |
|
77 |
|
78 @param evt reference to the context menu event |
|
79 @type QContextMenuEvent |
|
80 """ |
|
81 menu = self.__createSpellcheckContextMenu(evt.pos()) |
|
82 menu.exec(evt.globalPos()) |
|
83 |
|
84 def __createSpellcheckContextMenu(self, pos): |
|
85 """ |
|
86 Private method to create the spell-check context menu. |
|
87 |
|
88 @param pos position of the mouse pointer |
|
89 @type QPoint |
|
90 @return context menu with additional spell-check entries |
|
91 @rtype QMenu |
|
92 """ |
|
93 menu = self.createStandardContextMenu(pos) |
|
94 |
|
95 # Add a submenu for setting the spell-check language and |
|
96 # document format. |
|
97 menu.addSeparator() |
|
98 self.__addRemoveEntry(self.__cursorForPosition(pos), menu) |
|
99 menu.addMenu(self.__createLanguagesMenu(menu)) |
|
100 menu.addMenu(self.__createFormatsMenu(menu)) |
|
101 |
|
102 # Try to retrieve a menu of corrections for the right-clicked word |
|
103 spellMenu = self.__createCorrectionsMenu( |
|
104 self.__cursorForMisspelling(pos), menu) |
|
105 |
|
106 if spellMenu: |
|
107 menu.insertSeparator(menu.actions()[0]) |
|
108 menu.insertMenu(menu.actions()[0], spellMenu) |
|
109 |
|
110 return menu |
|
111 |
|
112 def __createCorrectionsMenu(self, cursor, parent=None): |
|
113 """ |
|
114 Private method to create a menu for corrections of the selected |
|
115 word. |
|
116 |
|
117 @param cursor reference to the text cursor |
|
118 @type QTextCursor |
|
119 @param parent reference to the parent widget (defaults to None) |
|
120 @type QWidget (optional) |
|
121 @return menu with corrections |
|
122 @rtype QMenu |
|
123 """ |
|
124 if cursor is None: |
|
125 return None |
|
126 |
|
127 text = cursor.selectedText() |
|
128 suggestions = trim_suggestions( |
|
129 text, self.__highlighter.dict().suggest(text), |
|
130 SpellCheckMixin.MaxSuggestions) |
|
131 |
|
132 spellMenu = QMenu( |
|
133 QCoreApplication.translate("SpellCheckMixin", |
|
134 "Spelling Suggestions"), |
|
135 parent) |
|
136 for word in suggestions: |
|
137 act = spellMenu.addAction(word) |
|
138 act.setData((cursor, word)) |
|
139 |
|
140 if suggestions: |
|
141 spellMenu.addSeparator() |
|
142 |
|
143 # add management entry |
|
144 act = spellMenu.addAction(QCoreApplication.translate( |
|
145 "SpellCheckMixin", "Add to Dictionary")) |
|
146 act.setData((cursor, text, "add")) |
|
147 |
|
148 spellMenu.triggered.connect(self.__spellMenuTriggered) |
|
149 return spellMenu |
|
150 |
|
151 def __addRemoveEntry(self, cursor, menu): |
|
152 """ |
|
153 Private method to create a menu entry to remove the word at the |
|
154 menu position. |
|
155 |
|
156 @param cursor reference to the text cursor for the misspelled word |
|
157 @type QTextCursor |
|
158 @param menu reference to the context menu |
|
159 @type QMenu |
|
160 """ |
|
161 if cursor is None: |
|
162 return |
|
163 |
|
164 text = cursor.selectedText() |
|
165 menu.addAction(QCoreApplication.translate( |
|
166 "SpellCheckMixin", |
|
167 "Remove '{0}' from Dictionary").format(text), |
|
168 lambda: self.__addToUserDict(text, "remove")) |
|
169 |
|
170 def __createLanguagesMenu(self, parent=None): |
|
171 """ |
|
172 Private method to create a menu for selecting the spell-check |
|
173 language. |
|
174 |
|
175 @param parent reference to the parent widget (defaults to None) |
|
176 @type QWidget (optional) |
|
177 @return menu with spell-check languages |
|
178 @rtype QMenu |
|
179 """ |
|
180 curLanguage = self.__highlighter.dict().tag.lower() |
|
181 languageMenu = QMenu( |
|
182 QCoreApplication.translate("SpellCheckMixin", "Language"), |
|
183 parent) |
|
184 languageActions = QActionGroup(languageMenu) |
|
185 |
|
186 for language in sorted(enchant.list_languages()): |
|
187 act = QAction(language, languageActions) |
|
188 act.setCheckable(True) |
|
189 act.setChecked(language.lower() == curLanguage) |
|
190 act.setData(language) |
|
191 languageMenu.addAction(act) |
|
192 |
|
193 languageMenu.triggered.connect(self.__setLanguage) |
|
194 return languageMenu |
|
195 |
|
196 def __createFormatsMenu(self, parent=None): |
|
197 """ |
|
198 Private method to create a menu for selecting the document format. |
|
199 |
|
200 @param parent reference to the parent widget (defaults to None) |
|
201 @type QWidget (optional) |
|
202 @return menu with document formats |
|
203 @rtype QMenu |
|
204 """ |
|
205 formatMenu = QMenu( |
|
206 QCoreApplication.translate("SpellCheckMixin", "Format"), |
|
207 parent) |
|
208 formatActions = QActionGroup(formatMenu) |
|
209 |
|
210 curFormat = self.__highlighter.chunkers() |
|
211 for name, chunkers in ( |
|
212 (QCoreApplication.translate("SpellCheckMixin", "Text"), |
|
213 []), |
|
214 (QCoreApplication.translate("SpellCheckMixin", "HTML"), |
|
215 [enchant.tokenize.HTMLChunker]) |
|
216 ): |
|
217 act = QAction(name, formatActions) |
|
218 act.setCheckable(True) |
|
219 act.setChecked(chunkers == curFormat) |
|
220 act.setData(chunkers) |
|
221 formatMenu.addAction(act) |
|
222 |
|
223 formatMenu.triggered.connect(self.__setFormat) |
|
224 return formatMenu |
|
225 |
|
226 def __cursorForPosition(self, pos): |
|
227 """ |
|
228 Private method to create a text cursor selecting the word at the |
|
229 given position. |
|
230 |
|
231 @param pos position of the misspelled word |
|
232 @type QPoint |
|
233 @return text cursor for the word |
|
234 @rtype QTextCursor |
|
235 """ |
|
236 cursor = self.cursorForPosition(pos) |
|
237 cursor.select(QTextCursor.SelectionType.WordUnderCursor) |
|
238 |
|
239 if cursor.hasSelection(): |
|
240 return cursor |
|
241 else: |
|
242 return None |
|
243 |
|
244 def __cursorForMisspelling(self, pos): |
|
245 """ |
|
246 Private method to create a text cursor selecting the misspelled |
|
247 word. |
|
248 |
|
249 @param pos position of the misspelled word |
|
250 @type QPoint |
|
251 @return text cursor for the misspelled word |
|
252 @rtype QTextCursor |
|
253 """ |
|
254 cursor = self.cursorForPosition(pos) |
|
255 misspelledWords = getattr(cursor.block().userData(), |
|
256 "misspelled", []) |
|
257 |
|
258 # If the cursor is within a misspelling, select the word |
|
259 for (start, end) in misspelledWords: |
|
260 if start <= cursor.positionInBlock() <= end: |
|
261 blockPosition = cursor.block().position() |
|
262 |
|
263 cursor.setPosition(blockPosition + start, |
|
264 QTextCursor.MoveMode.MoveAnchor) |
|
265 cursor.setPosition(blockPosition + end, |
|
266 QTextCursor.MoveMode.KeepAnchor) |
|
267 break |
|
268 |
|
269 if cursor.hasSelection(): |
|
270 return cursor |
|
271 else: |
|
272 return None |
|
273 |
|
274 def __correctWord(self, cursor, word): |
|
275 """ |
|
276 Private method to replace some misspelled text. |
|
277 |
|
278 @param cursor reference to the text cursor for the misspelled word |
|
279 @type QTextCursor |
|
280 @param word replacement text |
|
281 @type str |
|
282 """ |
|
283 cursor.beginEditBlock() |
|
284 cursor.removeSelectedText() |
|
285 cursor.insertText(word) |
|
286 cursor.endEditBlock() |
|
287 |
|
288 def __addToUserDict(self, word, command): |
|
289 """ |
|
290 Private method to add a word to the user word or exclude list. |
|
291 |
|
292 @param word text to be added |
|
293 @type str |
|
294 @param command command indicating the user dictionary type |
|
295 @type str |
|
296 """ |
|
297 if word: |
|
298 dictionary = self.__highlighter.dict() |
|
299 if command == "add": |
|
300 dictionary.add(word) |
|
301 elif command == "remove": |
|
302 dictionary.remove(word) |
|
303 |
|
304 self.__highlighter.rehighlight() |
|
305 |
|
306 @pyqtSlot(QAction) |
|
307 def __spellMenuTriggered(self, act): |
|
308 """ |
|
309 Private slot to handle a selection of the spell menu. |
|
310 |
|
311 @param act reference to the selected action |
|
312 @type QAction |
|
313 """ |
|
314 data = act.data() |
|
315 if len(data) == 2: |
|
316 # replace the misspelled word |
|
317 self.__correctWord(*data) |
|
318 |
|
319 elif len(data) == 3: |
|
320 # dictionary management action |
|
321 _, word, command = data |
|
322 self.__addToUserDict(word, command) |
|
323 |
|
324 @pyqtSlot(QAction) |
|
325 def __setLanguage(self, act): |
|
326 """ |
|
327 Private slot to set the selected language. |
|
328 |
|
329 @param act reference to the selected action |
|
330 @type QAction |
|
331 """ |
|
332 language = act.data() |
|
333 self.setLanguage(language) |
|
334 |
|
335 @pyqtSlot(QAction) |
|
336 def __setFormat(self, act): |
|
337 """ |
|
338 Private slot to set the selected document format. |
|
339 |
|
340 @param act reference to the selected action |
|
341 @type QAction |
|
342 """ |
|
343 chunkers = act.data() |
|
344 self.__highlighter.setChunkers(chunkers) |
|
345 |
|
346 def setFormat(self, formatName): |
|
347 """ |
|
348 Public method to set the document format. |
|
349 |
|
350 @param formatName name of the document format |
|
351 @type str |
|
352 """ |
|
353 self.__highlighter.setChunkers( |
|
354 [enchant.tokenize.HTMLChunker] |
|
355 if format == "html" else |
|
356 [] |
|
357 ) |
|
358 |
|
359 def dict(self): |
|
360 """ |
|
361 Public method to get a reference to the dictionary in use. |
|
362 |
|
363 @return reference to the current dictionary |
|
364 @rtype enchant.Dict |
|
365 """ |
|
366 return self.__highlighter.dict() |
|
367 |
|
368 def setDict(self, spellDict): |
|
369 """ |
|
370 Public method to set the dictionary to be used. |
|
371 |
|
372 @param spellDict reference to the spell-check dictionary |
|
373 @type emchant.Dict |
|
374 """ |
|
375 self.__highlighter.setDict(spellDict) |
|
376 |
|
377 @pyqtSlot(str) |
|
378 def setLanguage(self, language): |
|
379 """ |
|
380 Public slot to set the spellchecker language. |
|
381 |
|
382 @param language language to be set |
|
383 @type str |
|
384 """ |
|
385 epwl = self.dict().pwl |
|
386 pwl = ( |
|
387 epwl.provider.file |
|
388 if isinstance(epwl, enchant.Dict) else |
|
389 None |
|
390 ) |
|
391 |
|
392 epel = self.dict().pel |
|
393 pel = ( |
|
394 epel.provider.file |
|
395 if isinstance(epel, enchant.Dict) else |
|
396 None |
|
397 ) |
|
398 self.setLanguageWithPWL(language, pwl, pel) |
|
399 |
|
400 @pyqtSlot(str, str, str) |
|
401 def setLanguageWithPWL(self, language, pwl, pel): |
|
402 """ |
|
403 Public slot to set the spellchecker language and associated user |
|
404 word lists. |
|
405 |
|
406 @param language language to be set |
|
407 @type str |
|
408 @param pwl file name of the personal word list |
|
409 @type str |
|
410 @param pel file name of the personal exclude list |
|
411 @type str |
|
412 """ |
|
413 try: |
|
414 spellDict = enchant.DictWithPWL(language, pwl, pel) |
|
415 except DictNotFoundError: |
|
416 try: |
|
417 # Use English dictionary if a dictionary for the given |
|
418 # language is not available. |
|
419 spellDict = enchant.DictWithPWL("en", pwl, pel) |
|
420 except DictNotFoundError: |
|
421 # Still no dictionary could be found. Forget about spell |
|
422 # checking. |
|
423 spellDict = None |
|
424 self.__highlighter.setDict(spellDict) |
|
425 |
|
426 @classmethod |
|
427 def setDefaultLanguage(cls, language, pwl=None, pel=None): |
|
428 """ |
|
429 Class method to set the default spell-check language. |
|
430 |
|
431 @param language language to be set as default |
|
432 @type str |
|
433 @param pwl file name of the personal word list |
|
434 @type str |
|
435 @param pel file name of the personal exclude list |
|
436 @type str |
|
437 """ |
|
438 with contextlib.suppress(DictNotFoundError): |
|
439 cls.DefaultUserWordList = pwl |
|
440 cls.DefaultUserExceptionList = pel |
|
441 |
|
442 # set default language only, if a dictionary is available |
|
443 enchant.Dict(language) |
|
444 cls.DefaultLanguage = language |
|
445 |
|
446 class EnchantHighlighter(QSyntaxHighlighter): |
|
447 """ |
|
448 Class implementing a QSyntaxHighlighter subclass that consults a |
|
449 pyEnchant dictionary to highlight misspelled words. |
|
450 """ |
|
451 TokenFilters = (enchant.tokenize.EmailFilter, |
|
452 enchant.tokenize.URLFilter) |
|
453 |
|
454 # Define the spell-check style once and just assign it as necessary |
|
455 ErrorFormat = QTextCharFormat() |
|
456 ErrorFormat.setUnderlineColor(Qt.GlobalColor.red) |
|
457 ErrorFormat.setUnderlineStyle( |
|
458 QTextCharFormat.UnderlineStyle.SpellCheckUnderline) |
|
459 |
|
460 def __init__(self, *args): |
|
461 """ |
|
462 Constructor |
|
463 |
|
464 @param *args list of arguments for the QSyntaxHighlighter |
|
465 @type list |
|
466 """ |
|
467 QSyntaxHighlighter.__init__(self, *args) |
|
468 |
|
469 self.__spellDict = None |
|
470 self.__tokenizer = None |
|
471 self.__chunkers = [] |
|
472 |
|
473 def chunkers(self): |
|
474 """ |
|
475 Public method to get the chunkers in use. |
|
476 |
|
477 @return list of chunkers in use |
|
478 @rtype list |
|
479 """ |
|
480 return self.__chunkers |
|
481 |
|
482 def setChunkers(self, chunkers): |
|
483 """ |
|
484 Public method to set the chunkers to be used. |
|
485 |
|
486 @param chunkers chunkers to be used |
|
487 @type list |
|
488 """ |
|
489 self.__chunkers = chunkers |
|
490 self.setDict(self.dict()) |
|
491 |
|
492 def dict(self): |
|
493 """ |
|
494 Public method to get the spelling dictionary in use. |
|
495 |
|
496 @return spelling dictionary |
|
497 @rtype enchant.Dict |
|
498 """ |
|
499 return self.__spellDict |
|
500 |
|
501 def setDict(self, spellDict): |
|
502 """ |
|
503 Public method to set the spelling dictionary to be used. |
|
504 |
|
505 @param spellDict spelling dictionary |
|
506 @type enchant.Dict |
|
507 """ |
|
508 if spellDict: |
|
509 try: |
|
510 self.__tokenizer = enchant.tokenize.get_tokenizer( |
|
511 spellDict.tag, |
|
512 chunkers=self.__chunkers, |
|
513 filters=EnchantHighlighter.TokenFilters) |
|
514 except TokenizerNotFoundError: |
|
515 # Fall back to the "good for most euro languages" |
|
516 # English tokenizer |
|
517 self.__tokenizer = enchant.tokenize.get_tokenizer( |
|
518 chunkers=self.__chunkers, |
|
519 filters=EnchantHighlighter.TokenFilters) |
|
520 else: |
|
521 self.__tokenizer = None |
|
522 |
|
523 self.__spellDict = spellDict |
|
524 |
|
525 self.rehighlight() |
|
526 |
|
527 def highlightBlock(self, text): |
|
528 """ |
|
529 Public method to apply the text highlight. |
|
530 |
|
531 @param text text to be spell-checked |
|
532 @type str |
|
533 """ |
|
534 """Overridden QSyntaxHighlighter method to apply the highlight""" |
|
535 if self.__spellDict is None or self.__tokenizer is None: |
|
536 return |
|
537 |
|
538 # Build a list of all misspelled words and highlight them |
|
539 misspellings = [] |
|
540 for (word, pos) in self.__tokenizer(text): |
|
541 if not self.__spellDict.check(word): |
|
542 self.setFormat(pos, len(word), |
|
543 EnchantHighlighter.ErrorFormat) |
|
544 misspellings.append((pos, pos + len(word))) |
|
545 |
|
546 # Store the list so the context menu can reuse this tokenization |
|
547 # pass (Block-relative values so editing other blocks won't |
|
548 # invalidate them) |
|
549 data = QTextBlockUserData() |
|
550 data.misspelled = misspellings |
|
551 self.setCurrentBlockUserData(data) |
|
552 |
|
553 else: |
|
554 |
|
555 class SpellCheckMixin(): |
|
556 """ |
|
557 Class implementing the spell-check mixin for the widget classes. |
|
558 """ |
|
559 # |
|
560 # This is just a stub to provide the same API as the enchant enabled |
|
561 # one. |
|
562 # |
|
563 def __init__(self): |
|
564 """ |
|
565 Constructor |
|
566 """ |
|
567 pass |
|
568 |
|
569 def setFormat(self, formatName): |
|
570 """ |
|
571 Public method to set the document format. |
|
572 |
|
573 @param formatName name of the document format |
|
574 @type str |
|
575 """ |
|
576 pass |
|
577 |
|
578 def dict(self): |
|
579 """ |
|
580 Public method to get a reference to the dictionary in use. |
|
581 |
|
582 @return reference to the current dictionary |
|
583 @rtype enchant.Dict |
|
584 """ |
|
585 pass |
|
586 |
|
587 def setDict(self, spellDict): |
|
588 """ |
|
589 Public method to set the dictionary to be used. |
|
590 |
|
591 @param spellDict reference to the spell-check dictionary |
|
592 @type emchant.Dict |
|
593 """ |
|
594 pass |
|
595 |
|
596 @pyqtSlot(str) |
|
597 def setLanguage(self, language): |
|
598 """ |
|
599 Public slot to set the spellchecker language. |
|
600 |
|
601 @param language language to be set |
|
602 @type str |
|
603 """ |
|
604 pass |
|
605 |
|
606 @pyqtSlot(str, str, str) |
|
607 def setLanguageWithPWL(self, language, pwl, pel): |
|
608 """ |
|
609 Public slot to set the spellchecker language and associated user |
|
610 word lists. |
|
611 |
|
612 @param language language to be set |
|
613 @type str |
|
614 @param pwl file name of the personal word list |
|
615 @type str |
|
616 @param pel file name of the personal exclude list |
|
617 @type str |
|
618 """ |
|
619 pass |
|
620 |
|
621 @classmethod |
|
622 def setDefaultLanguage(cls, language, pwl=None, pel=None): |
|
623 """ |
|
624 Class method to set the default spell-check language. |
|
625 |
|
626 @param language language to be set as default |
|
627 @type str |
|
628 @param pwl file name of the personal word list |
|
629 @type str |
|
630 @param pel file name of the personal exclude list |
|
631 @type str |
|
632 """ |
|
633 pass |
|
634 |
|
635 |
|
636 class EricSpellCheckedPlainTextEdit(QPlainTextEdit, SpellCheckMixin): |
|
637 """ |
|
638 Class implementing a QPlainTextEdit with built-in spell checker. |
|
639 """ |
|
640 def __init__(self, *args): |
|
641 """ |
|
642 Constructor |
|
643 |
|
644 @param *args list of arguments for the QPlainTextEdit constructor. |
|
645 @type list |
|
646 """ |
|
647 QPlainTextEdit.__init__(self, *args) |
|
648 SpellCheckMixin.__init__(self) |
|
649 |
|
650 |
|
651 class EricSpellCheckedTextEdit(QTextEdit, SpellCheckMixin): |
|
652 """ |
|
653 Class implementing a QTextEdit with built-in spell checker. |
|
654 """ |
|
655 def __init__(self, *args): |
|
656 """ |
|
657 Constructor |
|
658 |
|
659 @param *args list of arguments for the QPlainTextEdit constructor. |
|
660 @type list |
|
661 """ |
|
662 QTextEdit.__init__(self, *args) |
|
663 SpellCheckMixin.__init__(self) |
|
664 |
|
665 self.setFormat("html") |
|
666 |
|
667 def setAcceptRichText(self, accept): |
|
668 """ |
|
669 Public method to set the text edit mode. |
|
670 |
|
671 @param accept flag indicating to accept rich text |
|
672 @type bool |
|
673 """ |
|
674 QTextEdit.setAcceptRichText(self, accept) |
|
675 self.setFormat("html" if accept else "text") |
|
676 |
|
677 if __name__ == '__main__': |
|
678 import sys |
|
679 import os |
|
680 from PyQt6.QtWidgets import QApplication |
|
681 |
|
682 if ENCHANT_AVAILABLE: |
|
683 dictPath = os.path.expanduser(os.path.join("~", ".eric7", "spelling")) |
|
684 SpellCheckMixin.setDefaultLanguage( |
|
685 "en_US", |
|
686 os.path.join(dictPath, "pwl.dic"), |
|
687 os.path.join(dictPath, "pel.dic") |
|
688 ) |
|
689 |
|
690 app = QApplication(sys.argv) |
|
691 spellEdit = EricSpellCheckedPlainTextEdit() |
|
692 spellEdit.show() |
|
693 |
|
694 sys.exit(app.exec()) |