Jose Dzireh Chong
Jose Dzireh Chong

Reputation: 143

This is some code I received from someone else, why is **kwargs needed?

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

class CheckboxRow(tk.Frame):
    def __init__(self, master, name, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.name = name

        checkbox = tk.Checkbutton(self, text=name)
        checkbox.pack(side=tk.LEFT)

        deleteItem = tk.Button(self, text="x", bg="red", fg="white",
                                activebackground="white", activeforeground="red",
                                command=self.destroyCheckbox)
        deleteItem.pack(side=tk.RIGHT)

    def destroyCheckbox(self):
        self.master.master.checkboxList.remove(self.name)
        self.destroy()

class CheckboxArea(tk.Frame):
    def add(self, name):
        row = CheckboxRow(self, name)
        row.pack(fill=tk.X)

class InputStuff(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

        prompt = tk.Label(self, text="What do you want your checkbox to be for?")
        prompt.pack()

        bottomInput = tk.Frame(self)
        self.entry = tk.Entry(bottomInput, bd=3)
        self.entry.pack(side=tk.LEFT)
        buttonConfirm = tk.Button(bottomInput, text="Confirm", command=self.drawCheckbox)
        buttonConfirm.pack(side=tk.LEFT)
        bottomInput.pack()

        buttonDone = tk.Button(self, text = "Close Input", command=master.hide_input_stuff)
        buttonDone.pack()

    def drawCheckbox(self, event=None):
        self.master.add(self.entry.get())
        self.entry.delete(0, tk.END)

class MainWindow(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        master.protocol("WM_DELETE_WINDOW", self.on_closing)

        self.checkboxList = []

        self.checkbox_area = CheckboxArea(self)
        self.checkbox_area.pack(fill=tk.X)

        self.input_stuff = InputStuff(self)
        self.add_button = tk.Button(self, text="Add Item", command=self.show_input_stuff)

        self.hide_input_stuff() # start with "add" button active

        self.load()

    def add(self, name):
        self.checkbox_area.add(name)
        self.checkboxList.append(name)

    def show_input_stuff(self):
        self.add_button.pack_forget()
        self.input_stuff.pack()
        self.input_stuff.entry.focus()
        self.master.bind('<Return>', self.input_stuff.drawCheckbox)

    def hide_input_stuff(self):
        self.input_stuff.pack_forget()
        self.add_button.pack()
        self.master.unbind('<Return>')

    def load(self):
        try:
            with open('toDoListSaveFile.json') as infile:
                checkboxList = json.load(infile)
            for savedCheckbox in checkboxList:
                self.add(savedCheckbox)
        except (IOError, ValueError):
            #an error occured while loading ... so no saved settings loaded
            pass

    def on_closing(self):
        with open("toDoListSaveFile.json", 'w') as outfile:
            json.dump(self.checkboxList, outfile)
        self.quit()

def main():
    master = tk.Tk()
    master.title("To-Do List (with saving!)")
    master.geometry("300x300")
    win = MainWindow(master)
    win.pack(fill=tk.X)
    master.mainloop()

if __name__ == '__main__':
    main()

The **kwargs is in class CheckboxRow. Why is it needed? To my knowledge, you only need **kwargs when you're accepting a variable amount of arguments, however __init__() will only ever take three arguments, self,masterandname`.

Same with tk.Frame.__init__. Why is kwargs needed here? I'm fairly sure I'm wrong about how kwargs is supposed to be used, could someone explain?

Upvotes: 0

Views: 135

Answers (3)

Eric Duminil
Eric Duminil

Reputation: 54253

class CheckboxRow(tk.Frame):
    def __init__(self, master, name, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

This way, any keyword that is used for the initialization of a CheckboxRow instance will :

  • be accepted
  • be passed to the initialization of a tk.Frame

Looking at your code, it's not possible to know which keyword arguments will be used by tk.Frame without looking at the documentation.

Here's a simpler example without kwargs:

def f(x):
    print x

f(2, width=800, height=600)
#=> TypeError: f() got an unexpected keyword argument 'width'

With kwargs :

def f(x, **kwargs):
    print x
    g(**kwargs)

def g(**kwargs):
    print kwargs

f(2, width=800, height=600)
# 2
# {'width': 800, 'height': 600}

Upvotes: 0

DrevanTonder
DrevanTonder

Reputation: 717

kwargs allows you to pass optional keyword arguments. So if you only want to change only some arguments and not others:

For example:

f = Frame(height=2, bd=1, relief=SUNKEN)
f = Frame(relief=SUNKEN) #This also works
f = Frame() # Also works

Upvotes: 0

Bryan Oakley
Bryan Oakley

Reputation: 386105

Strictly speaking, it isn't needed. However, by including it you can pass standard frame options to the subclasses.

For example, with **kwargs you can set the borderwidth of the frame like this:

x = CheckboxRow(root, "A row", borderwidth=1, relief="sunken")

The reason being, borderwidth=1, relief="sunken" is what is contained in kwargs, and it is being passed to the inherited Frame class which will accept these arguments.

You can omit **kwargs from the parameter list if you also remove it from the call to Frame.__init__, with the downside being that you can no longer pass in valid Frame options:

class CheckboxRow(tk.Frame):
    def __init__(self, master, name):
        tk.Frame.__init__(self, master)

Upvotes: 3

Related Questions