sarbjit
sarbjit

Reputation: 3894

tkinter prevent deletion of selected text with return key

I am implementing a tkinter text widget based console application and as a part of auto complete feature, I am observing one problem that on pressing return key results in the deletion of the selected text. Below example shows the similar problem :

from Tkinter import *

def getCommand(*args):
    global text
    text.insert(END, "\n")
    text.insert(END, "command>")
    x = text.get("1.0",END)
    print "command is %s" %(x)
    return 'break'

def handle_keyrelease(event):
    global text
    if event.keysym == "Return":
        text.tag_remove(SEL,"1.9",END)
        text.mark_set("insert",END)
        getCommand()
        return 'break'

root = Tk()
text = Text(root)
text.pack()
text.insert(END,"command>")
text.focus()
text.bind("<KeyRelease>", handle_keyrelease)
text.insert(END,"sometext")
text.tag_add(SEL,"1.9",END)
text.mark_set("insert","1.9") 
root.mainloop()

In this code, when I hit the return key, I want to get the complete command sometext, however with the current code only s is retrieved. I have tried setting the cursor position to end and deletion of selection tag when the return key event is recieved.

EDIT

Problem with KeyPress event handler :

from Tkinter import *

def getCommand(*args):
    global text
    text.insert(END, "\n")
    text.insert(END, "command>")
    x = text.get("1.0",END)
    print x
    return 'break'

validkeysymchars = []
validkeysymchars = validkeysymchars + map(chr, range(65,91))
validkeysymchars = validkeysymchars + map(chr, range(97,123))

def handle_keyrelease(event):
    global text
    if event.keysym == "Return":
        text.tag_remove(SEL,"1.9",END)
        text.mark_set("insert",END)
        getCommand()
        return 'break'
    if event.keysym in validkeysymchars:
        for x in ['testcommand']:
            strtocmp = text.get("MARK","end")
            strtocmp = strtocmp.encode('ascii','ignore')
            strtocmp = strtocmp.strip()
            print strtocmp
            if x.startswith(strtocmp):
                currpos = text.index(INSERT)
                text.insert(END,x[len(strtocmp):])
                text.tag_add(SEL,currpos,"%s+%dc"%(currpos,len(x)-len(strtocmp)))
                text.mark_set("insert",currpos)   

root = Tk()
text = Text(root)
text.pack()
text.insert(END,"command>")
text.mark_set("MARK",INSERT)
text.mark_gravity("MARK",LEFT)
text.focus()
text.bind("<KeyPress>", handle_keyrelease)
root.mainloop()

Upvotes: 2

Views: 542

Answers (1)

sloth
sloth

Reputation: 101052

The problem is that you handle <KeyRelease> instead of <KeyPress> (or <Return>).

At the time the <KeyRelease> event is raised, the text widget has already been updated with the newline. And because text was selected, that selected text got simply replaced with the newline (as it would have been replaced by pressing any other key).

So better bind to <KeyPress> or <Return> instead of <KeyRelease>.


I think the easiest way to solve your issue is indeed to handle both <KeyRelease> and <Return> seperatly.

(You could get it to work using <KeyPress>, but this would involve checking which key is pressed, manually inserting this key to the text, checking which text is selected, and replacing the selected text; so, it will be rather clunky).

def handle_keyrelease(event):
    global text
    if event.keysym in validkeysymchars:
        for x in ['testcommand']:
            strtocmp = text.get("MARK","end")
            strtocmp = strtocmp.encode('ascii','ignore')
            strtocmp = strtocmp.strip()
            print strtocmp
            if x.startswith(strtocmp):
                currpos = text.index(INSERT)
                text.insert(END,x[len(strtocmp):])
                text.tag_add(SEL,currpos,"%s+%dc"%(currpos,len(x)-len(strtocmp)))
                text.mark_set("insert",currpos)
                return

def handle_return(event):
    text.tag_remove(SEL,"1.9",END)
    text.mark_set("insert",END)
    text.insert(END, "\n")
    text.insert(END, "command>")
    text.mark_set("MARK",INSERT)
    text.mark_gravity("MARK",LEFT)
    return "break"

...
text.bind("<KeyRelease>", handle_keyrelease)
text.bind("<Return>", handle_return)
...

Upvotes: 1

Related Questions