Tkinter. How can one use the bind_class method for multiple entry widgets

96 Views Asked by At

I wonder if anyone can help please. I have a frame with multiple entry widgets. They fill with data when a file is read. Now I want to click into one of them and clear the text by pressing the F8 key. I could do this long hand and bind the F8 key to each widget but could I use the bind_class or the bind_all method to do this?

My feeble attempt below:

import tkinter
import tkinter as ttk
# from ttkthemes import ThemedTk

class Frame1(ttk.Frame):
    def __init__(self, master):
        super().__init__(master)

        self.bind_class('Entry', '<F8>', self.clearfield)

        self.label = ttk.Label(self, text="Name")
        self.label2 = ttk.Label(self, text="Contact")

        self.name = ttk.Entry(self, width = 30, text="")     
        self.contact = ttk.Entry(self, width = 20, text="")

        self.label.grid(row=1, column=0, padx=10, pady=5, sticky="w")
        self.label2.grid(row=2, column=0, padx=10, pady=5, sticky="w")

        self.name.grid(row=1, column=1, padx=10, pady=5, sticky="w")
        self.contact.grid(row=2, column=1, padx=10, pady=5, sticky="w")

    def clearfield(self, event):
        insert = self.ttk.Entry.index("insert")
        self.ttk.Entry.delete(0, insert)

   
class App(tkinter.Tk):
    def __init__(self):
        super().__init__()

        # style = ttk.Style(self)
        # style.theme_use("alt")
        self.title("Test")
        self.geometry("250x300")
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.bind_class('Entry', '<F8>', self.clearfield)

        self.acc_no = ttk.Entry(self, width = 60, text="Acc. no.")
        self.acc_no.grid(row=0, column=0, padx=10, pady=10, sticky="w")
        self.acc_no.focus_set()

        self.frame1 = Frame1(self)
        self.frame1.grid(row=1, column=0, padx=10, pady=(10, 10), sticky="nsw")

    def clearfield(self, event):
        insert = self.acc_no.index("insert")
        self.acc_no.delete(0, insert)

app=App()
app.mainloop()

There Is nothing on the internet that explains this and I tried various options but did not succeed.

2

There are 2 best solutions below

1
Bryan Oakley On BEST ANSWER

Your only mistake is trying to use self in the callback. When the bound callback is called, an event object is passed. That object has an attribute named widget which tells you which widget received the event.

The following example illustrates the point. It also removes the unnecessary step of computing the index, using the symbolic index "insert" instead.

def clearfield(self, event):
    event.widget.delete(0, "insert")

If you want to delete everything rather than only everything up to the insertion point, use "end" instead of "insert".

def clearfield(self, event):
    event.widget.delete(0, "end")
2
acw1668 On

Since you have called bind_class("Entry", ...) twice (one inside App.__init__() and one inside Frame1.__init__()), so the later will override the former, i.e Frame1.clearfield() will be executed even you pressed <F8> inside self.acc_no.

Also the code inside Frame1.clearfield() does not work as said in the comment.

I would suggest to create a custom ttk.Entry widget with the binding implemented and use this custom widget instead of ttk.Entry when you want to have such feature:

import tkinter
import tkinter as ttk

# custom class to have the required event binding
class MyEntry(ttk.Entry):
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)
        self.bind("<F8>", lambda e: e.widget.delete(0, "insert"))


class Frame1(ttk.Frame):
    def __init__(self, master):
        super().__init__(master)

        self.label = ttk.Label(self, text="Name")
        self.label2 = ttk.Label(self, text="Contact")

        # use the custom Entry class
        self.name = MyEntry(self, width = 30, text="")
        self.contact = MyEntry(self, width = 20, text="")

        self.label.grid(row=1, column=0, padx=10, pady=5, sticky="w")
        self.label2.grid(row=2, column=0, padx=10, pady=5, sticky="w")

        self.name.grid(row=1, column=1, padx=10, pady=5, sticky="w")
        self.contact.grid(row=2, column=1, padx=10, pady=5, sticky="w")


class App(tkinter.Tk):
    def __init__(self):
        super().__init__()

        self.title("Test")
        self.geometry("250x300")
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)

        # use the custom Entry class
        self.acc_no = MyEntry(self, width = 60, text="Acc. no.")
        self.acc_no.grid(row=0, column=0, padx=10, pady=10, sticky="w")
        self.acc_no.focus_set()

        self.frame1 = Frame1(self)
        self.frame1.grid(row=1, column=0, padx=10, pady=(10, 10), sticky="nsw")


app=App()
app.mainloop()