Reputation: 49
I'm trying to create a GUI using tkinter in Python3 which will have several buttons and I dont want to type the same attributes for all of them each time like this:
tkinter.Button(topFrame, font=("Ariel", 16), width=10, height=10,
fg="#ffffff", bg="#000000", text="Cake")
For example, fg
, bg
colour and size
will all be the same on each button. The only things changing on each button will be the text and where on the screen to put them.
I'm quite new to programming and Python and am trying to re-use code when I want to create a new button. I think I'm missing some understanding of classes which I'm not getting when I read up on it.
I want to pass in different text for each button and a different frame in order to place it in a different location on the GUI and have everything else the same.
My code so far:
import tkinter
import tkinter.messagebox
window = tkinter.Tk()
#create default values for buttons
#frame and buttonText are the values passed to the class when making a new
#button
class myButtons:
def buttonLayout(self, frame, buttonText):
self.newButton=tkinter.Button(frame, font=("Ariel", 16),
width=10, height=10, fg=#ffffff,
bg=#000000, text=buttonText)
topFrame = tkinter.Frame(window)
topFrame.pack()
#create new button here and place in the frame called topFrame with the text
#"Cake" on it
buttonCake = myButtons.buttonLayout(topFrame, "Cake")
#position the new button in a certain cell using grid in topFrame
buttonCake.grid(row=1, column=0)
window.mainloop()
The error I get when I try to run it is:
TypeError: buttonLayout() missing 1 required positional argument: 'buttonText'
I'm confused because I'm passing in "Cake"
and the error says it's missing.
Thank you for pointing out init I was not aware of how to use init for my problem, but that and the answers given here have helped. Thank you.
Upvotes: 1
Views: 822
Reputation: 123501
You're not defining your class and using it correctly.
Here's a version with the corrections needed to make it work:
import tkinter
class MyButton:
""" Create Button with some default values. """
def __init__(self, frame, buttonText):
self.newButton = tkinter.Button(frame, font=("Ariel", 16),
width=10, height=10, fg='#ffffff',
bg='#000000', text=buttonText)
window = tkinter.Tk()
topFrame = tkinter.Frame(window)
topFrame.pack()
# Create new button here and place in the frame called topFrame with the text
# "Cake" on it.
buttonCake = MyButton(topFrame, "Cake")
# Position the new button in a certain cell in topFrame using grid().
buttonCake.newButton.grid(row=1, column=0)
window.mainloop()
Update
A more object-oriented approach would be be to derive your own tkinter.Button
subclass which would allow instances of it to be utilized exactly like those of the base class due to inheritance — i.e. so there would be no need to remember to reference its newButton
attribute in the grid()
call instead of the button itself, as would normally be required.
The implementation shown below is also very flexible in the sense that you can easily override any of the defaults when creating one simply by supplying a different value for it via the usual associated keyword argument(s).
import tkinter
class MyButton(tkinter.Button):
""" Create Button with some default values. """
# Default Button options (unless overridden).
defaults = dict(font=("Ariel", 16), width=10, height=10,
fg='#ffffff', bg='#000000')
def __init__(self, *args, **kwargs):
kwargs = dict(self.defaults, **kwargs) # Allow defaults to be overridden.
super().__init__(*args, **kwargs)
window = tkinter.Tk()
topFrame = tkinter.Frame(window)
topFrame.pack()
# Create new button here and place in the frame called topFrame with the text
# "Cake" on it.
buttonCake = MyButton(topFrame, text="Cake")
# Position the new button in a certain cell in topFrame using grid().
buttonCake.grid(row=1, column=0)
window.mainloop()
Upvotes: 0
Reputation: 1308
I'm use in my projects have a generic class, named Tools that have code like this
def get_button(self, container, text, row=None, col=None):
w = ttk.Button(container, text=text, underline=0)
if row is not None:
w.grid(row=row, column=col, sticky=tk.W+tk.E, padx=5, pady=5)
else:
w.pack(fill =tk.X, padx=5, pady=5)
calling as
self.tools = Tools()
f = ttk.Frame()
bts = [('Reset', self.on_reset),
('New', self.on_add),
('Edit', self.on_edit),
('Close', self.on_cancel)]
for btn in bts:
self.tools.get_button(f, btn[0] ).bind("<Button-1>", btn[1])
you can easy expand this add style property.
Upvotes: 0
Reputation: 11
so I commented the code so you can learn a few things
from tkinter import * #in order not to have to writer "tkinter." each time
class app: #we usually put the whole app in a class
def __init__(self,window): # so here you "attach" things to your instance represented by self
self.window=window
self.topFrame = Frame(window)
self.topFrame.pack()
self.ButtonList=[] #because you wouldn't want to make 100 button with the same name
def buttonLayout(self, frame, buttonText): # here we create a method wich will be also "attached" to the instance
self.ButtonList.append(Button(frame, font=("Ariel", 16),width=10, height=10, fg="#ffffff", bg="#000000", text=buttonText)) #adding a button to your list of buttons
self.lastButton=self.ButtonList[(len(self.ButtonList)-1)] #storing the last button to call grid outside the class
window=Tk()
anInstance=app(window)
anInstance.buttonLayout(anInstance.topFrame, "Cake")
anInstance.lastButton.grid(row=1,column=0)
window.mainloop()
also if you do buttons you usually create them in __init__
but you have there a nice buttonbuilder for your app, you can even make a Frame builder out of this.
Upvotes: 0
Reputation: 4964
You get your error because of the self
parameter.
There's also the problem of your code not creating an instance of the MyButtons
class.
Here is an example that inherits from Button
and customizes __init__
to setup some of your default values.
import tkinter
import tkinter.messagebox
window = tkinter.Tk()
#create default values for buttons
#frame and buttonText are the values passed to the class when making a new button
class MyButton(tkinter.Button):
def __init__(self, *args, **kwargs):
if not kwargs:
kwargs = dict()
kwargs['font'] = ("Arial", 16)
kwargs['width'] = 10,
kwargs['height'] = 10,
kwargs['fg'] = '#ffffff',
kwargs['bg'] = '#000000',
super().__init__(*args, **kwargs)
topFrame = tkinter.Frame(window)
topFrame.pack()
#create new button here and place in the frame called topFrame with the text "Cake" on it
buttonCake = MyButton(topFrame, text="Cake")
#position the new button in a certain cell using grid in topFrame
buttonCake.grid(row=1, column=0)
window.mainloop()
This forces your default values into the Button. You can add if
statements to define them only if you don't pass them in the call by doing like this:
if not 'width' in kwargs:
kwargs['width'] = 10
Upvotes: 1