I want to create a class for a Tk.Button in a Tk.Frame so that the button can have a border color independent of its background color.
I wrote this code which creates a button in a frame just as I want but I cannot later get or set the text, bcolor and bg of a previously created FrameButton.
I thought I should be able to do this @profile and @bgsetter constructions but these give me an error.
import tkinter as tk
class FramedButton(tk.Frame):
def __init__(self, parent, text="HELLO", bcolor="red", *args, **kwargs):
tk.Frame.__init__(self, parent, bg="green", *args, **kwargs)
self.parent = parent
self.button=tk.Button(self, text=text, bg=bcolor, borderwidth=1)
self.button.grid(row=0, column=0, padx=5, pady=5)
self.pack()
root = tk.Tk()
root.title="FramedButton"
fb=FramedButton(root)
fb.pack(padx=10,pady=10)
# but these do not work... what do I need to add/change?
fb.text="WORLD"
fb.bg="blue"
fb.bcolor="yellow"
root.mainloop()
2 Answers 2
You need to call fb.button.config() to set the options for the internal button and fb.config() to set the options for the outer frame:
fb.config(bg='blue') # set the "outer border" to blue
fb.button.config(text='WORLD', bg='yellow') # the the button text and background color
Comments
The tkinter module is an interface to the Tcl/Tk GUI toolkit. To make actual changes to the widgets, we need to call Tk commands.
Your code doesn't work because you're just creating new data attributes for your instance, not accessing the widget options themselves.
The actual value of a Tk widget option is not related to the attributes of the Python class instance.
The tkinter module defines special methods for getting or setting widget options.
__getitem__using cget__setitem__using configure
As we can see in the source code, these methods use self.tk.call(args) to call Tk commands to control the Tk GUI.
import tkinter as tk
# inherited from class Misc
print(tk.Frame.configure)
print(tk.Frame.config)
print(tk.Tk.configure)
print(tk.Tk.config)
# inherited from class Wm
# class Wm provides functions for the communication with the window manager.
# for tk.Tk and tk.Toplevel
print(tk.Tk.title)
print(tk.Tk.wm_title)
class FramedButton(tk.Frame):
def __init__(self, parent, text="HELLO", bcolor="red", *args, **kwargs):
tk.Frame.__init__(self, parent, bg="green", *args, **kwargs)
self.parent = parent
self.button=tk.Button(self, text=text, bg=bcolor, borderwidth=1)
self.button.grid(row=0, column=0, padx=5, pady=5)
self.pack()
# get button text: self.tk.call(self.button, "cget", "-text")
root = tk.Tk()
# set title
root.title("FramedButton")
# same as root.wm_title("FramedButton") or root.tk.call("wm", "title", root, "FramedButton")
# get title
print(root.title())
fb=FramedButton(root)
fb.pack(padx=10,pady=10)
# available config options
print(root.keys()) # print(root.config())
print(fb.keys()) # print(fb.config())
print(fb.button.keys()) # print(fb.button.config())
# set options
# widget.config(option=value) or widget["option"] = value
fb.button["text"]="WORLD" # set new text to self.button, __setitem__
fb.config(bg="blue") # set new bg to frame
fb.button.config(bg="yellow") # set new bg to self.button
# get button text
print(fb.button["text"]) # __getitem__
print(fb.button.cget("text"))
print(fb.button.config("text"))
root.mainloop()
If you want to customize the attribute lookup, you can use descriptors such as property.
import tkinter as tk
class FramedButton(tk.Frame):
def __init__(self, parent, text="HELLO", bcolor="red", *args, **kwargs):
tk.Frame.__init__(self, parent, bg="green", *args, **kwargs)
self.parent = parent # same as self.master
self.button=tk.Button(self, text=text, bg=bcolor, borderwidth=1)
self.button.grid(row=0, column=0, padx=5, pady=5)
self.pack()
self.text = text
@property
def text(self):
return self.button["text"]
@text.setter
def text(self, value):
self.button["text"] = value
root = tk.Tk()
root.title("FramedButton")
fb=FramedButton(root)
fb.pack(padx=10,pady=10)
print(dir(fb))
# [..., 'button', ..., 'parent', ..., 'text', ...]
print(fb.text) # HELLO
fb.text="WORLD"
print(fb.text) # WORLD
print(FramedButton.text)
# <property object at 0x0000012D7E957CE0>
root.mainloop()