I have been learning to write custom tkinter widgets for some time – and want to get it right – but the instructional materials/videos I’ve found on the subject are inconsistent. The following code demonstrates some of the inconsistencies I’ve found. It creates two custom widgets using classes. If you read the four comments (2 in each class) the issue I’m having will become obvious. They are written in very different ways, and yet both produce exactly the same result in the root window.
import tkinter as tk
class first_frame(tk.Frame):
def __init__(self,parent,btn_text):
super().__init__(parent)
# initialized using self
self.btn_text = btn_text
# widget created with name self.my_btn
self.my_btn = tk.Button(self,text=self.btn_text)
self.my_btn.pack()
class second_frame(tk.Frame):
def __init__(self,parent,btn_text):
super().__init__(parent)
# btn_text not initialized here at all
# widget created as simply my_btn (not self.my_btn)
my_btn = tk.Button(self,text=btn_text)
my_btn.pack()
root = tk.Tk()
root.geometry("400x400+400+150")
test1 = first_frame(root,btn_text="first button")
test1.pack()
test2 = second_frame(root,btn_text="second button")
test2.pack()
root.mainloop()
Again, in the interest of creating classes correctly, I need to know if there is some reason variables passed in to the class should be initialized in the form self.btn_text = btn_text, since it doesn’t seem to matter in these examples. And likewise, when creating a new widget within a class whether the correct form is, in this case, self.my_btn = … or simply my_btn = … since, again, they both seem to work. Any advice appreciated.
>Solution :
Assigning a value to an attribute of self creates an instance variable. That variable persists for as long as the parent object does, and it can be referenced from other methods, or from outside code (if that code has a reference to the object). In your example code, for instance, you could print out test1.btn_text.
An assignemnt without self, in contrast, creates a local variable. In a function, local variables only last as long as the function is running, and other code can’t access them. There’s no easy way to get the btn_text from test2, for example (it’s probably not impossible to get at it, but it’s much less convenient than an instance variable).
TKinter doesn’t require you to keep a reference to sub-widgets when you’re creating your own widget class, because it’s already keeping track of the widget hierarchy as you specify each widget’s parent, and then call pack() on it to specify its layout.
If your code doesn’t ever need to change a widget after you set it up, you can use a local variable like my_btn to set it up. You’d want to use self.my_btn if you were going to modify the button later on (e.g. to disable it after it’s been pushed perhaps, or to change the button’s text).