0

I have an Entry widget with a StringVar as a text variable. I use input_content.trace_id = input_content.trace_add("write", solve) to run solve() each time the content of the Entry widget changes.

I would like to be able to determine what key did the user press that ran the solve function.

Entry().bind() doesn't work for me because it seems that the entry is not updatted in time (Say I type Python, when using .bind() to get the content of the Entry, it will output Pytho). Please refer to the code below that demonstrates this behaviour:

from tkinter import*

master = Tk()

def main(event):
    print(f"Inputted character: {event.keysym}")
    print(f"{entry.get() = }")
    print(f"{entry_var.get() = }")

entry_var = StringVar()
entry = Entry(master, textvariable=entry_var)
entry.pack()

entry.bind("<Key>", main)

mainloop()

As you can see, I'm able to get the last inputted character using event.keysym but the entry is not updated until main() is run again, etc.

I would like to have both the last inputted character and have the entry content updated when the function is run.

KuroN3ko
  • 3
  • 2
  • The key binding happens before the built-in binding that actually performs the insertion - this allows you to block or replace the key before it's inserted. One solution would be for your binding to use `.after(1, ...)` to call another function with a slight delay - the insertion will have been performed by then. – jasonharper May 02 '22 at 15:26
  • Thank you. This seems to have solved my problem completely, but I like to have a somewhat good comprehension of what I use in my programs. Since `.after(...)` is a method of entry (and all widgets for that matter), how does it distinguish itself from the plain `sleep(...)` function ? How does the method, being a method of a particular widget, play a role specific to its widget ? If you were to call the `.after(...)` method on any other widget, would it do the same thing ? – KuroN3ko May 02 '22 at 16:56
  • It makes absolutely no difference which widget you invoke `.after()` on. It schedules a function call for later, allowing normal event processing to proceed in the meantime. `sleep()`, on the other hand, just pauses execution. No matter how long you slept, the key insertion still would not have been processed, because Tkinter's mainloop has had no chance to do so. – jasonharper May 02 '22 at 17:10

1 Answers1

0

I would like to be able to determine what key did the user press that ran the solve function.

Variable traces can't give you that information. You will need to use bindings. You say bindings don't work, but only because of the way you used them.

To gain an understanding of why using bind didn't work for you, see Basic query regarding bindtags in tkinter and How to bind self events in Tkinter Text widget after it will binded by Text widget?.

The second link ask about the Text widget, but the behavior of bind is identical for both Text and Entry widgets. The accepted answer shows examples using an Entry widget.

The short answer is to either bind on <KeyRelease>, adjust the binding tags so that your binding comes after the default bindings, or use after to schedule your code to run after the key has been processed.

The two questions I linked to show how to take advantage of binding tags, here's an example that uses after_idle to run code after whenever the event loop goes "idle" (ie: when it finishes processing pending events):

import tkinter as tk

def handle_keypress():
    print(f"=> {entry.get()}")

root = tk.Tk()
entry = tk.Entry(root)
entry.pack(padx=20, pady=20)
entry.bind("<Any-KeyPress>", lambda event: root.after_idle(handle_keypress))

root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685