Tkinter 'space' binding and entry clearing - python

Okay, so I am making a typing speed test in Tkinter. I want the entry to clear when pressing space. Which it does, however, because space is bound as the key to clear the entry it's as if though Tkinter clears the entry and then adds space to the entry thereafter. Meaning, that after entering the first word, from there on out there is always a space at the beginning of the entry before you start typing the next word.

You can get your desired behaviour by returning "break". This will make sure that the event doesn't propagate to other handlers.
Here is a minimal example:
import tkinter as tk
def handle(event):
entry.delete("0", "end")
return "break"
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
entry.bind("<space>", handle)
root.mainloop()
Also as #TheLizzard pointed out you could also try: entry.bind("<KeyRelease-space>", handle)
import tkinter as tk
def handle(event):
entry.delete("0", "end")
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
entry.bind("<KeyRelease-space>", handle)
root.mainloop()
Also, something to note is that the second solution will add a space then delete, whereas the first one simply deletes the text without any added space.

Related

Why has the tkinter key-event <Tab> a higher priority than the tkinter key-event <key>?

I am writing an editor (using the tkinter text widget), which replaces tab-characters (inserted by the user) on the fly by 4 blanks.
The replacement is done by a binding to the tabulator-key-event ("Tab"), which inserts 4 blanks and returns with "break". Returning with "break" prevents the tabulator-character from being inserted. This works fine.
Additionally I need a second binding to any key-event ("Key", for syntax highlighting and similar stuff). So I implemented a second binding to "Key". This also works fine.
As I found, the binding of <Tab> has a higher priority as the binding of <key>:
Whenever the tab-key is pressed, only the tab-event gets active but never the key-event.
Why is that?
Is there any priority order defined for events?
This is my example code:
import tkinter as tk
def key_event():
print("Key")
def tab_event(text):
print("Tab")
text.insert("insert", " ")
return("break")
root = tk.Tk()
text = tk.Text(root, height=8, width=20)
text.grid()
text.bind("<Tab>", lambda event : tab_event(text))
text.bind("<Key>", lambda event : key_event())
root.mainloop()
According to the documentation, the more specific binding is chosen over the other. A simple but effective way around this is to use a broad binding like '<Key>' and delegate the event accordingly by it's keysym, that you can access by event.keysym.
As example:
import tkinter as tk
def key_event(event):
if event.keysym == 'Tab':
text.insert("insert", " "*4)
return 'break'
root = tk.Tk()
text = tk.Text(root, height=8, width=20)
text.grid()
text.bind("<Key>", key_event)
root.mainloop()

Bind only significant keys on tk/tkinter

Can you please tell me if there's a convenient way to have a bind only on "significant" keys with tk/tkinter?
So, the situation is I have a bind on a tk.Entry (because I need a callback to trigger while keys are being pressed), like this:
myEntry.bind('<Key>', ...)
Right now, the bind callback is triggered whatever key is pressed (even if it is the shift!) but I'd want it to trigger only on "significant" keys... By "significant" I mean every key that involve changes on the text in the Entry, so "significant" keys are for sure letters, numbers, symbols and the backspace or delete keys, but NOT arrow keys, home, end, pgUp/Down or also the tab, caps-lock, shift, ctrl, etc... (I'm still thinking if I will need it to trigger on return key or not but that's not a problem, because if I need it too I could add a specific bind on it or otherwise have it ignored in the callback later)
I don't know if perhaps is there something different from <Key> I could bind to get what I need, is it?
Otherwise, if not, I know I can get which key was pressed looking at event keycode... is that the way to go? If yes, can you suggest me some suitable keycode interval please?
Thank you
In a comment, you say you are doing real-time filtering of a listbox. For that, it would arguably be better to set a trace on a variable rather than setting a binding on the entry widget. The trace will only be called when the value changes, so you don't have to distinguish which key triggered the change.
Here's a simple example that illustrates the concept:
import tkinter as tk
widgets = [w.__name__ for w in tk.Widget.__subclasses__()]
root = tk.Tk()
entryvar = tk.StringVar()
entry = tk.Entry(root, textvariable=entryvar)
listbox = tk.Listbox(root)
entry.pack(side="top", fill="x")
listbox.pack(side="bottom", fill="both", expand=True)
listbox.insert("end", *sorted(widgets))
after_id = None
def filter_changed(*args):
global after_id
if after_id:
root.after_cancel(after_id)
# delay actual filtering slightly, in case the user is typing fast.
after_id = root.after(500, filter_listbox)
def filter_listbox(*args):
pattern = entryvar.get()
filtered_widgets = [w for w in widgets if w.startswith(pattern) ]
listbox.delete(0, "end")
listbox.insert("end", *filtered_widgets)
entryvar.trace("wu", filter_changed)
root.mainloop()
Here is my solution, it uses event.char and evaluates against a string with characters (can add more characters if needed):
from tkinter import Tk, Entry
string = r"""0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""
def key_press(event):
if event.char and event.char in string:
print(event.char)
root = Tk()
entry = Entry(root)
entry.pack()
entry.bind('<Key>', key_press)
root.mainloop()

How to view the end of a tkinter entry widget

I am using a TKinter Entry widget to visualize a path. Now, the path string is actually longer than the widget and after inserting it I can only see the beginning of the path. Since I do this to select a particular file, it would be better to show the end of the path string where the file name is. Is there a way to view the end of the Entry widget?
Thanls
There are two approaches:
the first is to set the cursor towards the end of the string in order to display the filename.
the other is to set a label that will get the value of the entry and display its entirety.
This example combines both :
import tkinter as tk
def update_label(e):
var.set(entry.get())
if __name__ == '__main__':
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
var = tk.StringVar()
label = tk.Label(root, textvar=var)
label.pack()
var.set('this is a very long link with the filename at the end filename.txt')
entry.insert(0, var.get())
entry.bind('<Enter>', update_label)
entry.xview(len(var.get()) - 10)
root.mainloop()
Thanks a lot Reblochon Masque for your answer. The code you added actually led me to an even easier solution, that is the use of the attribute xview_moveto. Indeed, if I use the following code:
myEntry.xview_moveto(1)
The end of the entry is visualized together with the file name as I needed.

Why is first letter of tkinter text widget not justifying as expected?

I have an issue where I've created a text widget inside a tkinter Frame that is suppose to center the text on the first line when the user types. It does this, except the first letter they type it justify's it to the left then after a second letter is pressed it justify's to the center as expected.
I would like to know how to fix this so the first line stays centered.
Here is the code I've gotten so far:
import Tkinter as tk
def testing(event=None, text=None):
LineNumber = text.index(tk.INSERT) # returns line number
if "1." in LineNumber:
text.tag_add("center", "1.0", "end")
root = tk.Tk()
F1 = tk.Frame(root, bg="blue", width=300, height=300)
F1.pack()
text = tk.Text(F1, padx=5, pady=10, bg="white")
text.tag_configure("center", justify='center')
text.pack(expand=1, fill="both")
text.bind('<Key>', lambda event: testing(None, text))
root.mainloop()
It is because your custom binding fires before tkinter has a chance to insert the character. When you press a key, the first thing that happens is that your custom binding is triggered, before any other bindings. The character has not been inserted yet.
For a full explanation of how key events are processed, see this answer: https://stackoverflow.com/a/11542200/7432
A simple fix is to bind on <KeyRelease> instead of <Key>. Since the default behavior happens on a press (but after your custom behavior), a binding on the release of the key will be guaranteed to run after the character has been inserted into the widget.

Tkinter Entry returns float values regardless of input

I have some pretty simple code right now that I am having issues with.
root = Tk()
label1 = Label(root, text ="Enter String:")
userInputString = Entry(root)
label1.pack()
userInputString.pack()
submit = Button(root,text = "Submit", command = root.destroy)
submit.pack(side =BOTTOM)
root.mainloop()
print(userInputString)
When I run the code everything operates as I would expect except
print(userInputString)
for an input asdf in the Entry print will return something like 0.9355325
But it will never be the same value back to back always random.
I am using python 3.5 and Eclipse Neon on a Windows 7 Machine.
Ultimately the goal is to accept a string from the user in the box that pops up and then be able to use that value as string later on. For example, it might be a file path that needs to be modified or opened.
Is Entry not the correct widget I should be using for this? Is there something inherently wrong with the code here? I am new to python and don't have a lot of strong programming experience so I am not even certain that this is set up right to receive a string.
Thanks in advance if anyone has any ideas.
There are two things wrong with your print statement. First, you print the widget, not the text in the widget. print(widget) prints str(widget), which is the tk pathname of the widget. The '.' represents the root window. The integer that follows is a number that tkinter assigned as the name of the widget. In current 3.6, it would instead be 'entry', so you would see ".entry".
Second, you try to print the widget text after you destroy the widget. After root.destroy, the python tkinter wrapper still exists, but the tk widget that it wrapped is gone. The following works on 3.6, Win10.
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text="Enter String:")
entry = tk.Entry(root)
def print_entry(event=None):
print(entry.get())
entry.bind('<Key-Return>', print_entry)
entry.focus_set()
submit = tk.Button(root, text="Submit", command=print_entry)
label.pack()
entry.pack()
submit.pack()
root.mainloop()
Bonus 1: I set the focus to the entry box so one can start typing without tabbing to the box or clicking on it.
Bonus 2: I bound the key to the submit function so one can submit without using the mouse. Note that the command then requires an 'event' parameter, but it must default to None to use it with the button.
The NMT Reference, which I use constantly, is fairly complete and mostly correct.

Categories

Resources