简体   繁体   中英

Bind event runs before character typed is put in entry

I am making a special Text widget with tkinter that is supposed to have working indentation. To get the indentation to work I binded the function that puts down the tabs to the Enter button. This binding worked fine before with the bind going after the actual typed character but it won't with this function. Can someone help me out?

Working bind:

def double_parentheses(self, event):
        main_text_box_anchor = str(self.text.index("insert")).split(".")
        self.text.insert(INSERT, ")")
        self.text.mark_set(INSERT, str(main_text_box_anchor[0]) + "." + 
        str(int(main_text_box_anchor[1])))
#later in the code
scroll.text.bind("(", scroll.double_parentheses)

This puts the parentheses in order with the insert in the middle of them

Not working:

def new_line_indent(self, event):
        line = self.text.get("insert linestart", "insert lineend")
        editable_line = str(line)
        if editable_line != "":
            if editable_line[-1] == ":":
                if "\t" in editable_line:
                    for i in range(editable_line.count("\t")):
                        self.text.insert("insert", "\t")
                else:    
                    self.text.insert("insert", "\t")
            elif "\t" in editable_line:
                for i in range(editable_line.count("\t")):
                    self.text.insert("insert", "\t")
#Later in the code
scroll.text.bind("<Return>", scroll.new_line_indent)

This puts the tabs in but it does it BEFORE the new line is created and I can't figure out why. What am I doing wrong?

This puts the tabs in but it does it BEFORE the new line is created and I can't figure out why.

The short answer is that your binding happens before the built-in key bindings. Thus, your function is called before the newline is actually inserted by tkinter.

You should have your code insert the newline, perform your other actions, then return "break" to prevent the default behavior from happening.

For a more thorough explanation of how bindings are processed, see this answer

Here's a working example:

def new_line_indent(self, event):
    # get the text of the current line
    line = self.text.get("insert linestart", "insert lineend")

    # insert the newline
    self.text.insert("insert", "\n")

    # insert indentation if line ends with ":"
    if line and line[-1] == ":":

        # get current indentation, then insert it
        leading_whitespace = self.get_leading_whitespace(line)
        self.text.insert("insert", leading_whitespace)

        # add an additional level of indentation
        self.text.insert("insert", "\t")

    # since we already inserted the newline, make sure that
    # the default bindings do not
    return "break"

def get_leading_whitespace(self, line):
    n = len(line) - len(line.lstrip())
    return line[:n]

Other than the solution suggested by Bryan, you can bind <KeyRelease-Return> instead of <Return> . Then the callback will be executed after the newline is added:

    def new_line_indent(self, event):
        # get previous line
        line = self.text.get("insert-1c linestart", "insert-1c lineend")
        editable_line = str(line)
        if editable_line != "":
            for i in range(editable_line.count("\t")):
                self.text.insert("insert", "\t")
            if editable_line[-1] == ":":
                self.text.insert("insert", "\t")

...
#Later in the code
scroll.text.bind("<KeyRelease-Return>", scroll.new_line_indent)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM