Search code examples
pythonwxpython

Keep tracking the position of a word when looping over wx.TextCtrl.GetValue() to enable process repetitive words


I've tried to make a spell checker in a wxPython GUI but I'm having trouble in cases where I have a repeating misspelling words. The function needs to set the words in red and then suggest a correction in a MessageDialog. It all works good regarding the suggestions of corrections on the MessageDialog but I can't accomplish to change the colour of a repeated misspelling word. I understand that the problem is when I get the start position of the word... it keeps on considering the first occurence of the word and ignoring the others.

for i in range(self.tx_input.GetNumberOfLines()):
            line = self.tx_input.GetLineText(i)
            for word in text:
                if word in line and word not in suggestion:
                    startPos = self.tx_input.GetValue().find(word)
                    endPos = startPos + len(word)
                    self.tx_input.SetStyle(startPos, endPos, wx.TextAttr("red", "white")) 

Since I'm looping over lines, I would understand if this issue happen to misspelling words is in the same line, but what is most wierd is that it also fail when the repetition is in a different line.

I need help to figure out how to keep track of word's positions that already been processed to ignore in an eventual new occurence.


Full Code

class AddQuestion ( wx.Frame ):

    def __init__(self,parent):

        wx.Frame.__init__(self,parent,id=wx.ID_ANY, title='Grammar Checker', pos=wx.DefaultPosition, size=wx.Size(350,350), style=wx.DEFAULT_FRAME_STYLE)
        self.SetBackgroundColour( wx.Colour( 0, 93, 126 ))
        panel = wx.Panel(self)
        mainBox = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(mainBox)

        self.tx_input = wx.TextCtrl(panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(350,150), wx.TE_MULTILINE|wx.TE_RICH2)
        mainBox.Add( self.tx_input, 1, wx.ALL|wx.EXPAND, 5 )

        btnBox = wx.BoxSizer(wx.VERTICAL)
        self.btn = wx.Button(panel, wx.ID_ANY,u'Grammar Check', wx.DefaultPosition, wx.DefaultSize,0)
        btnBox.Add(self.btn,0,wx.ALL,5)

        mainBox.Add(btnBox,0,wx.ALL|wx.ALIGN_CENTER,5)

        self.btn.Bind(wx.EVT_BUTTON, self.grammarCheck)

    def warn(self, parent, message, caption = 'WARNING!'):
        dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
        dlg.ShowModal()
        dlg.Destroy()

    def grammarCheck(self, event):

        from symspellpy import SymSpell

        sym_spell = SymSpell()
        sym_spell.load_dictionary("frequency_dictionary_en_82_765.txt", 0, 1)
        input_term = self.tx_input.GetValue().lower()
        # filter unecessary character
        ignore = r'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~1234567890“”–'
        text = sym_spell.word_segmentation(input_term.translate(str.maketrans("","",ignore)), max_edit_distance=2 ).segmented_string.split()
        suggestion = sym_spell.word_segmentation(input_term.translate(str.maketrans("","",ignore)), max_edit_distance=2 ).corrected_string.split()

        for i in range(self.tx_input.GetNumberOfLines()):
            line = self.tx_input.GetLineText(i)
            for word in text:
                if word in line and word not in suggestion:
                    startPos = self.tx_input.GetValue().find(word)
                    endPos = startPos + len(word)
                    self.tx_input.SetStyle(startPos, endPos, wx.TextAttr("red", "white"))

        for i in range(len(text)):
            if text[i] != suggestion[i]:
                self.warn(self, "`" + text[i] + "`" + " did you mean " + "`" + suggestion[i] + "` ?")


if __name__ == '__main__':

        app = wx.App(False)
        frame = AddQuestion(None)
        frame.Show()

        app.MainLoop() 

Solution

  • You are always looking for the first occurrence using string.find(word)
    Look up something like "python find all occurrences of sub-string", I found:

    # using list comprehension + startswith() 
    # All occurrences of substring in string  
    res = [i for i in range(len(test_str)) if test_str.startswith(test_sub, i)] 
    

    and

    import re
    
    # using re.finditer() 
    # All occurrences of substring in string  
    res = [i.start() for i in re.finditer(test_sub, test_str)] 
    

    both return a list of start positions within the test string and can be adapted to your code