Adding a spell check to QGIS
Adding a Spell Check to QGIS
(Or what to do on a rainy bank holiday in Glasgow)
This Monday was a local bank holiday in Glasgow (or at least the university) as a remnant of when the whole
town took a train to Blackpool in the same two weeks so that the ship builders and steel works could stop in a
coordinated fashion. As is required in the UK the weather was awful so I stayed in and being bored looked at
my long list of possible projects. I picked one that has been kicking around on the list for a while adding a
spell checker for QGIS. As a dyslexic I have spell checking turned on in nearly every program I enter text
into including vim
, InteliJ
and my browser. So I have always felt that what QGIS really needed was a way
to spell check maps before I printed them at A3 and put them on the wall.
Back in 2019 North Road wrote a iblog post about custom layout checks and ended it with a throw away comment “It’d even be possible to hook into one of the available Python spell checking libraries to write a spelling check!”. I came across this when I was trying to see if there was an easy way for my students (many of whom have English as a second language) to avoid handing in projects with glaring (i.e. I can see them) spelling errors in the title. So I stuck the link on my backlog, until the proverbial rainy day came along.
Implementation
Obviously I’m the last person who should be allowed to write spell checking software, but the joy of open
source is that for things like this someone else has almost certainly already done it. So a quick duck-duck-go
found me installing pyspellcheck
which seemed like it would do what I want. It has a pretty easy interface
in that once you’ve created a spell checker object, you can just pass in a list of words and it will return a
list of (probably) misspelled words and a method to give the most likely correction and another method to give
you list of other possibilities. Armed with this I could create a method to find and check all the text
elements of a print layout.
@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
def layout_check_spelling(context, feedback):
layout = context.layout
results = []
checker = SpellChecker()
for i in layout.items():
if isinstance(i, QgsLayoutItemLabel):
text = i.currentText()
tokens = [word.strip(string.punctuation) for word in text.split()]
misspelled = checker.unknown(tokens)
for word in misspelled:
res = QgsValidityCheckResult()
res.type = QgsValidityCheckResult.Warning
res.title = 'Spelling Error?'
template = f"""
<strong>'{word}</strong>' may be misspelled, would
'<strong>{checker.correction(word)}</strong>' be a better choice?
"""
possibles = checker.candidates(word)
if len(possibles) > 1:
template += """
Or one of:<br/>
<ul>
"""
for t in possibles:
template += f"<li>{t}</li>\n"
template += '</ul>'
res.detailedDescription = template
results.append(res)
return results
And in theory, that was that! But I’m pretty sure that my students (and everyone else) probably didn’t want to
cut and paste that into the console every time they wanted to spell check a map. So, I looked at how to
package this up for QGIS. I built a plugin (using the plugin builder tool), but then things got a little
tricky as I can’t see any way for a plugin to add itself to the print layout rather than the main QGIS window
(please let me know if it is possible), and it seemed unintuitive to make people press a button in one window
to effect another one, besides the whole point of being a QgsAbstractValidityCheck
was that the method is
automatically run on print. So I didn’t need most of the plugin code or did I? On further thought I did, there
is a need for some GUI as the user can pick which language they want to use in the spell check. pyspellcheck
can spell check English, Spanish, French, Portuguese, German, Italian, Russian, Arabic, Basque, Latvian and
Dutch (so if those are your language then please test this for me). I also thought that providing the option
to supply a different to the default personal dictionary might be useful. So that made use of the dialog that
pops up when you hit the plugin.
But it turns out you can’t register a class method as as a QgsAbstractValidityCheck
since it gets confused
when QGIS calls it later. So I had to move my checker method outside the plugin class. But then I couldn’t
access the language and dictionary that was set in the GUI! Some more searching gave me the following code:
_instance = plugins['qgis-spellcheck']
checker = _instance.checker
Whereby I can pull out the named plugin and grab it’s spell checker, which was created in the plugin’s
__init__
method. I seem to have a small issue that the user’s profile is not set when that runs which messes
up where the personal dictionary is put (again if you know how to fix this let me know).
Future Work
Ideally, I’d like the spell checker to scan and highlight the text in the boxes as I typed but I fear that is beyond my understanding of the QGIS/Qt interface. Next highest on my wish list is for the list of spelling issues to be non-modal so I can cut and paste fixes into the text box, rather than having to memorise the correct spelling, close the window and then type it in (again answers on a github issue).
I’m sure all sorts of things will come up once people start using it, so as usual issues and PRs are welcome at https://github.com/ianturton/qgis-spellcheck.