0

I'm trying to build a search engine that will check a list and then remove all list items that do not meet the search parameters. I know there is several problems with my program such as it will not add things back to the list when you backspace and in my updating for loop I simply tack on a '*' thinking that it will search for strings only beginning with the current parameters, but I will cross those bridges later.

class StudentFinderWindow(Tkinter.Toplevel):

def __init__(self):
    Tkinter.Toplevel.__init__(self) # Create Window

    searchResultList = ['student1', 'student2', 'student3'] # Test list.

    ##### window attributes
    self.title('Edit Students') #sets window title.

    ##### Puts stuff into the window.

    # text
    editStudentInfoLabel = Tkinter.Label(self,text='Select the student from the list below or search for one in the search box provided')
    editStudentInfoLabel.grid(row=0, column=0)

    # Entry box
    self.searchRepositoryEntry = Tkinter.Entry(self)
    self.searchRepositoryEntry.grid(row=1, column=0)

    # List box
    self.searchResults = Tkinter.Listbox(self)
    self.searchResults.grid(row=2, column=0)

This fills the Tkinter Listbox with the original list.

    # Search results initial updater.
    self.getStudentList()
    for student in self.studentList:
        self.searchResults.insert(Tkinter.END, student)

    ##### Event handler

Right here I bind to run the list updater after a key is entered into the search box

    self.searchRepositoryEntry.bind('<Key>', self.updateSearch)

This is supposed to run every time a key is pressed. It gets the string that is in the Entry then starts a variable count so I know which index the name is at. After that it run a for loop on the current list supposedly checking to see if it fits the requirement of the parameters and any other letter after it. If it does not match it should delete. The problem is the first time I hit a letter the parameters string is just a blank space and then the next letter the string is the first letter and so on. It is always one step behind. And that is the problem

def updateSearch(self, event):
    parameters = self.searchRepositoryEntry.get()
    int = 0
    currentList = self.searchResults.get(0, Tkinter.END)
    for i in currentList:
        if not i == parameters + '*':
            self.searchResults.delete(int)
        int += 1

def getStudentList(self):
    global fileDirectory # Gets the directory that all the files are in.
    fileList = listdir(fileDirectory)
    self.studentList = []
    for file in fileList:
        self.studentList.append(file[:-4])
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brandon
  • 301
  • 3
  • 18
  • possible duplicate of [How to bind self events in Tkinter Text widget after it will binded by Text widget?](http://stackoverflow.com/questions/3501849/how-to-bind-self-events-in-tkinter-text-widget-after-it-will-binded-by-text-widge) The nutshell summary is: bindings on widgets fire before bindings on widget classes, and it's the latter bindings that update the widget. – Bryan Oakley Feb 18 '11 at 19:54

1 Answers1

0

I believe I have run into this same problem you describe before, when attempting to make an actively searching ctrl-F feature in one of my programs.

What I found to work is not bind on Key but instead KeyRelease. I'm not entirely sure why this works (probably just a quirk with Tkinter). However, it works.

Snippet's:

The binding

# self.FW.En is an entry widget.
self.FW.En.bind('<KeyRelease>', self.find)

Which would run

def find (self, event):

        self.Te.tag_remove('found', '1.0', 'end')
        pat = self.FW.En.get()
        if len(pat) > 1:
            index = '1.0'
            while True:
                index = self.Te.search(pat, index, nocase=1, stopindex='end')
                if not index:
                    break
                lastidex = '%s+%dc' % (index, len(pat))
                self.Te.tag_add('found', index, lastidex)
                index = lastidex

            self.Te.tag_config('found', background='#80ff00')
rectangletangle
  • 50,393
  • 94
  • 205
  • 275
  • 1
    it works because widget bindings fire before class bindings, and it is the class bindings that cause data to be inserted into the widget. Since the class binding fires on `KeyPress`, binding to `KeyRelease` naturally happens after, allowing the class binding to work. It's not so much a "quirk" as a feature. This is all described in the question that I referenced in my comment: http://stackoverflow.com/questions/3501849/how-to-bind-self-events-in-tkinter-text-widget-after-it-will-binded-by-text-widge – Bryan Oakley Feb 23 '11 at 15:27