0

I was bored this afternoon and decided to implement a speed typing test in Python; in order to allow it to check the current word after each character I used widgetName.bind("<Key>", functionName) on my entry widget in order to call the function whenever any key is pressed within the entry widget.

This works fine and the function is called upon each keypress, my problem occured however when I used widgetName.get() to collect the string from the entry widget; the .get() did not collect the contents of the widget, only those in the widget prior to the function being called.

For example, if I typed 'P' into the entry widget, my function would print 'A key was pressed', however it would only print a blank line to the console. When I continued by pressing 'y', the string 'P' was outputted to the console, upon the third keypress 'Py' was outputted and so forth.

This led me to believe that the contents of the Tkinter widget had not been updated because the .bind() call happened before the widget contents updated, and therefore the contents of the widget were one keypress behind.

I need to know if there is a way around this, I did some research and found that I can called root.update() or widget.update() from within the function, which should force the UI to refresh. However this did not help and I am still faced with the same problem.

My code is as follows

root = Tk()
def keyPress(forSomeReasonThisArgumentFixesABug): ##Argument is because bind passes the event to the function
    entryBox.update()
    entry, word = entryBox.get(), wordBox.get()
    print(entry)

wordBox = Entry(root)
wordBox.grid(row=0, column=0)
entryBox = Entry(root)
entryBox.grid(row=1, column=0)
entryBox.bind("<Key>", keyPress)

Could someone please explain how I can pass the contents of the widget to a function (Through an argument or within the function using .get() ) upon each keypress.

Thanks in advance Edit: I have also tried using the function called by .bind() to call another function, which looks disgusting and still doesn't work

Theo Pearson-Bray
  • 775
  • 1
  • 13
  • 32

2 Answers2

1

The short answer is that the default bindings (the ones that cause the character to get inserted) happen after your custom bindings. The solution is to make sure your binding happens after the default bindings.

This answer goes into a lot more detail about what exactly is happening:

https://stackoverflow.com/a/11542200/7432

The simplest solution is to bind on <KeyRelease> rather than <Key>, since the default bindings happen on the press. A binding on the release of the key will therefore see the inserted character.

Another solution is to alter the bind tags of the widget. This answer gives a working example which illustrates the problem and shows a solution using bind tags:

https://stackoverflow.com/a/3513906/7432

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

Ok, I appear to have fixed my problem, however not in the way I originally intended. In order to see the character entered I used .char on the event argument passed to the function by .bind(). This allows me to see the character, but I still can't see the full string without concatenating the string collected with .get() and the string collected with .char. This works, however it doesn't look very nice and doesn't seem to belong in Python, so I am still open to answers which work more cleanly and efficiently.

def keyPress(event): ##Argument is because bind passes the event to the function
    collectedString = (entryBox.get() + event.char)
    print(collectedString)
Theo Pearson-Bray
  • 775
  • 1
  • 13
  • 32
  • Note: your solution won't work very well if the key that triggered the binding is the delete key, or if the user moved the cursor anywhere but at the end of the string. – Bryan Oakley Jun 01 '15 at 19:30
  • Yes, I have realised that and fixed the problem with the backspace. The movement of the cursor shouldn't be a problem as the user is expected to be typing single words very quickly. – Theo Pearson-Bray Jun 01 '15 at 19:33