When I copy some text and paste (crtl + v) it in a tkinter Entry , if there is selected text, it won't remove it from the entry. I'm on Linux (Mint) 64-bit.
Here I copy "d" with (ctrl + c):
Then I select "b":
Now I paste "d" (ctrl + v) onto it but the result is this:
First: I want to know if this is a bug specific to Linux or this is how it was supposed to be?
Second: I was thinking of a workaround for this with validatecommand
but I got another problem:
If I am to remove the selected text in a command, I have to know the index of selection in the entry. Otherwise if there are multiple instances of the selected text directly after and before the cursor, I wouldn't know which one to delete and replace with the new text. Because the cursor could be on either side of the selection (depending on if the person dragged the mouse form right to left or left to right on the text).
Now is there a way to get the index of selection in the entry? or another way to workaround this problem?
Here's some code with an example of the problem:
import tkinter as tk
root = tk.Tk()
def validation(after_text, before_text, validation_text, cursor_index):
cursor_index = int(cursor_index)
print('cursor index:', cursor_index)
print('text after change:', after_text)
print('text before change:', before_text)
print('text in need of validation:', validation_text)
try:
selection = root.selection_get()
except:
selection = ''
print('selection:', selection)
# EXAMPLE:
# validation_text = 'd'
# before text = "bb"
# now if someone dragged from right to left on the 2nd b:
# cursor position will be 1 (imagine | as the cursor): 'b|b'
# cursor_index = 1
# after_text = 'bdb' --> but should be 'bd'
# now if someone dragged from left to right on the 2nd b:
# cursor position will be 2 (imagine | as the cursor): 'bb|'
# cursor_index = 2
# after_text = 'bbd' --> but should be 'bd'
# so there is no way for me to know which of these b's I have
# to replace with d based on cursor position alone. I have to
# know the index of selection itself in before_text to be
# able to replace the text properly.
# I don't know how to get that.
return True
txtvar = tk.StringVar(value = 'a-b-c-d-e')
entry = tk.Entry(root, textvariable = txtvar)
font = tk.font.Font(family = entry.cget('font'), size = -50)
entry.config(validate = 'all',
vcmd = (root.register(validation),'%P', '%s', '%S', '%i'),
font = font)
entry.pack()
root.mainloop()
It's not a bug. If it were a bug, somebody would have noticed it and fixed it a decade ago. Tkinter has been around a long time, and fundamental things like this don't go unnoticed.
The implementation of paste on X11-based systems will not delete the selected text before pasting. The following is the actual underlying Tcl code as of the time I write this:
bind Entry <<Paste>> {
global tcl_platform
catch {
if {[tk windowingsystem] ne "x11"} {
catch {
%W delete sel.first sel.last
}
}
%W insert insert [::tk::GetSelection %W CLIPBOARD]
tk::EntrySeeInsert %W
}
}
Using the validation feature is definitely the wrong way to solve this. Validation is specifically for what the name implies: validation. The right solution is to create your own binding to the <<Paste>>
event.
Now is there a way to get the index of selection in the entry? or another way to workaround this problem?
Yes, the entry widget has the special index sel.first
which represents the first character in the selection, and sel.last
represents the character just after the selection.
A fairly literal translation of the above code into python (minus the check for x11) would look something like this:
def custom_paste(event):
try:
event.widget.delete("sel.first", "sel.last")
except:
pass
event.widget.insert("insert", event.widget.clipboard_get())
return "break"
To have this apply to a specific widget, bind to the <<Paste>>
event for that widget:
entry = tk.Entry(...)
entry.bind("<<Paste>>", custom_paste)
If you want to do a single binding that applies for every Entry
widget, use bind_class
:
root = tk.Tk()
...
root.bind_class("Entry", "<<Paste>>", custom_paste)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With