简体   繁体   中英

Create a class for tkinter widgets to call default attributes

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.

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 

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.

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 — ie 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()

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.

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