0

I want to print the text of an Entry each time a new character is written. By doing this with binding and a command to the widget the last character isn't printed.

I guess that the parameter 'textvariable' is getting updated after binding command had been executed. How to fix this?

from tkinter import *
master = Tk()
var = StringVar()

def execute_e(key):
    print(var.get())

E = Entry(master, width=30, textvariable=var)
E.pack()
E.bind('<Key>', execute_e)
FinnK
  • 210
  • 4
  • 10
  • You could append a random character to `textvariable`, which will then be omitted. This isn't really an answer, just a way to get around the problem. – Eric Jin May 31 '19 at 22:51
  • The binding is being executed before the Entry gets to see the keypress - this could be used to block certain keys from being entered, for example. You could do `.after(1, ...)` to delay the code that checks the Entry until after the key has been handled. Or, you could use a write trace on the StringVar instead of an event binding - this would give you ALL changes to the actual contents, even those that weren't triggered by a keypress (such as doing a Paste via a menu). – jasonharper May 31 '19 at 23:14
  • 1
    @EricJin: that's not a solution in the slightest. – Bryan Oakley May 31 '19 at 23:15

2 Answers2

1

It's because the bound event function is being executed before the new key has been added.

Here's a simple workaround that uses the ability to add validation to an Entry widget (the validator accepts any key because it always returns True). The trick is that validator function is set-up to receive the value that the text will have if the change is allowed by specifying the %P when it's configuration as part of the Entry construction via the validatecommand=(validator_command, '%P').

Here's some documentation about adding validation to Entry widgets with details about how it works.

from tkinter import *

master = Tk()
var = StringVar()

def validator(new_value):
    print(f'new_value: {new_value}')
    return True

validator_command = master.register(validator)

E = Entry(master, width=30, textvariable=var,
          validate='key',
          validatecommand=(validator_command, '%P'))
E.pack()

master.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
0

I feel like this is a question of event handler. When a key is typed, your code first register a key press and execute the bound command execute_e. Only after it has processed this and your event handler has return will it update the entry with the character and proceed to update the tkinter variable.

Your print command therefore comes in before your variable have been updated and you print the previous version of your variable. If you try deleting a character from your entry, you'll see that you get the previous string with the character you have just erased.

The easiest way around that problem for you is probably to bind the command to the tkinter variable rather than the keybind. Do so using trace when the variable is writen like so :

var.trace('w', execute_e)

There are also some methods to manipulate the event handler and decide in which order to execute commands. root.after_idle will execute a command when the code has nothing else to do (when it has computed everything else you asked it to do). Try out this version of your code :

from tkinter import *
master = Tk()
var = StringVar()

def execute_e(*key):
    def printy():
        print(var.get())
    master.after_idle(printy)

E = Entry(master, width=30, textvariable=var)
E.pack()
E.bind('<Key>', execute_e)
master.mainloop()
Gonzalez87
  • 162
  • 1
  • 9