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.
Related
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()
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.
I've been working on text widget using tkinter. My requirement is to restrict the functionality of Copy(ctrl+c), Paste(ctrl+v) and backspace. It's like once entered into the text widget there is no editing like clearing, and adding from somewhere. The user has to type and cannot backspace.
self.inputfeild = tk.Text(self, bg="White")
self.inputfeild.pack(fill="both", expand=True)
This is my Text widget which was declared inside a class.
You can use event_delete method to delete virtual event associated with it.
eg:
inputfield.event_delete('<<Paste>>', '<Control-v>')
inputfield.event_delete('<<Copy>>', '<Control-c>')
Check out more Here
Or you can simply bind that event to an event handler and return 'break' like this:
from tkinter import *
root = Tk()
inputfield = Text(root, bg="White")
inputfield.pack(fill="both", expand=True)
inputfield.bind('<Control-v>', lambda _: 'break')
inputfield.bind('<Control-c>', lambda _: 'break')
inputfield.bind('<BackSpace>', lambda _: 'break')
root.mainloop()
In addition to #JacksonPro 's answer, you could also try this approach,
from tkinter import *
def backspace(event):
if text.tag_ranges(SEL):
text.insert(SEL_FIRST,text.get(SEL_FIRST, SEL_LAST))
else:
last_char=text.get('1.0','end-1c')[-1]
text.insert(END,last_char)
root=Tk()
text=Text(root)
text.pack()
text.bind('<KeyRelease>', lambda event=None:root.clipboard_clear())
text.bind('<KeyPress>', lambda event=None:root.clipboard_clear())
text.bind('<BackSpace>', backspace)
root.mainloop()
This basically will clear your clipboard everytime you perform a KeyPress or a KeyRelease and hence copying/pasting will not be possible. The backspace() function obtains the last character and re-inserts it at the last position wherever backspace is used and indirectly restrics it's function. My previous appreach to backspace() wasn't right since it didn't take selection into account, but now it should work in all the cases, if there is something selected, it will obtain the selected text and insert it at the beginning of the selection (SEL_FIRST) else it will just obtain and reinsert the last charecter.
I am working on an app using Tkinter and I have a small question.
For example, I have a label placed like this:
Label = Label(root, text="Hello").place(x=5, y=5)
Is there any way to hide the label when a button is pressed or when a function is called?
Any help will be appreciated
Thanks!
You should simply never chain .place() or any other geometry manager to the widget creator. It returns None.
lbl_name = Label(root, text="Hello")
lbl_name.place(x=5, y=5)
Now you can handle lbl_name as a label object. To hide it you can use:
lbl_name.place_forget()
Unfortunately, now its gone. Therefore, first save the place properties:
lbl_name = Label(root, text="Hello")
lbl_name.place(x=5, y=5)
lbl_name.saved = lbl_name.place_info()
You can now show it again with:
lbl_name.place(lbl_name.saved)
Note: You can print lbl_name.saved. It is a dictionary with all place-properties of the lbl_name Label object.
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.