Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind the backspace key in tkinter to delete more than one character?

Tags:

python

tkinter

I am wanting to create bindings that let me press tab to insert a predefined number of spaces, and then press backspace to delete that many spaces, based on a variable.

How can I delete a pre-determined number of spaces when the user presses the backspace key? I don't know how to delete multiple characters, and when I've attempted to solve this, the binding deletes the wrong number of characters.

like image 776
Bryan Oakley Avatar asked May 11 '17 16:05

Bryan Oakley


1 Answers

There are two parts to this question. The first part is related to how to delete more than one character at a time. The second part is how to use that in a binding tied to the backspace keys

Deleting more than one character

The delete method of the text widget takes two indexes, and will delete the characters between those indexes. Tkinter text indexes can be relative by applying modifiers to an index. For example, to reference the four characters before the insertion point you can use the index "insert" plus the modifier "-4 chars".

Example:

self.text.delete("insert -4 chars", "insert")

Because these indexes are normal strings, you can use string formatting if you want to use a variable.

Example:

tabWidth = 4
self.text.delete("insert -%d chars" % tabWidth, "insert")

Using the function with a binding

To run a function when the user presses the backspace key, you can bind a function to the <BackSpace> event. This function will be passed one argument which represents the event.

For example:

self.text.bind("<BackSpace>", self.do_backspace)
...
def do_backspace(self, event):
    ...

An important part of doing custom bindings to standard keys is to know that your binding by default will not replace the default behavior. For example, if your binding deletes one character and then returns, two characters will get deleted because your binding will remove one and the default binding will remove one.

The way to override this behavior is to return the string "break". Because your custom binding happens before the default binding, the default binding will see that you returned "break" and do nothing. This makes it very simple to either override the default behavior or do additional work while keeping the default behavior.

Putting it all together, this is how you can define a function that deletes the previous four characters if they are four consecutive spaces, and do the default behavior if not:

def do_backspace(self, event):
    # get previous <tabWidth> characters; if they are all spaces, remove them

    previous = self.text.get("insert -%d chars" % self.tabWidth, "insert")
    if previous == " " * self.tabWidth:
        self.text.delete("insert-%d chars" % self.tabWidth, "insert")

        # return "break" so that the default behavior doesn't happen
        return "break"

    # if we get to here, we'll just return. That allows the default
    # behavior to run

Putting it all together

Here is a complete working example that will insert four spaces when you press tab, and remove four spaces when you press backspace:

import tkinter as tk

def do_tab(event):
    text.insert("insert", " " * tabWidth)
    # return "break" so that the default behavior doesn't happen
    return "break"

def do_backspace(event):
    # get previous <tabWidth> characters; if they are all spaces, remove them
    previous = text.get("insert -%d chars" % tabWidth, "insert")
    if previous == " " * tabWidth:
        text.delete("insert-%d chars" % tabWidth, "insert")
        # return "break" so that the default behavior doesn't happen
        return "break"

    # if we get to here, we'll just return. That allows the default
    # behavior to run

root = tk.Tk()
tabWidth = 4

text = tk.Text(root)
text.pack(fill="both", expand=True)

text.bind("<Tab>", do_tab)
text.bind("<BackSpace>", do_backspace)

root.mainloop()
like image 115
Bryan Oakley Avatar answered Sep 28 '22 04:09

Bryan Oakley