[Tutor] Tkinter Q's

Michael Lange klappnase at freenet.de
Thu Jul 14 00:02:30 CEST 2005

On Wed, 13 Jul 2005 08:13:42 +0000
Joseph Quigley <cpu.crazy at gmail.com> wrote:

> Hi, 
> 	what's the **kw stand for, used for? What does it mean?

**kw means that there is an optional list of keyword arguments that you can pass to __init__
(for example: main = Main(background="black") ). Keyword, because there is defined set of keywords that may be used.

> > Here's what I would do:
> >
> > class Main(Frame):
> >    def __init__(self, master=None, **kw):
> >        Frame.__init__(self, master, **kw)
> >
> >        showquote = Label(self, text=random.choice(quotes.quote))
> >        showquote.pack()
> >
> >        # etc
> >
> > if __name__ == '__main__':
> >    root = Tk()
> >    main = Main(root)
> >    main.pack(fill=BOTH, expand=True)
> >    root.mainloop()
> > ###
> >
> > Do you see what I am doing there, and why it is different from your approach?
> Uh, not really, no. I'm very new to GUI. So you're saying that If I make the class actually do something (I edited and example in:
> An into to Tkinter, Fredrik Lundh).

O.k., let's try it in more detail

    class Main(Frame):
        def __init__(self, master=None, **kw):
            Frame.__init__(self, master, **kw)

This defines a new class "Main"; the construct "Main(Frame)" means that "Main" is a subclass of Frame
and so inherits all methods and other attributes of a Frame (like pack(), configure() and so on).
The second line defines the classes __init__() method, which is called everytime you create a new instance of
the Main class with e.g. " main = Main() ". There are three arguments passed to __init__() : "self" is the first,
because everytime you call a class method, the first argument that is passed to the call is the class instance
that does the call, e.g if you do " root.mainloop() ", the mainloop() method gets called with your root window
as first argument. You actually don't need to call this variable "self", you could as well call it "joe", but
everyone uses "self", and this makes sense, because it points to what is meant.
The third line now is the first thing __init__() does: it calls the Frame's __init__()
and passes "self" as first argument to it, so now actually when you do:

main = Main()

your variable "main" is set to what your __init__() method returns and this is at this point nothing else
as that what Frame.__init__() returns - a newly created Frame instance. You see, all arguments
that are passed to __init__() are passed to Frame.__init__(), so after these three lines
The "Main" class is nothing more (or less) than a Frame.

I hope this made sense so far.
Now you are ready to add some useful features to your class, to make it "more" than a standard Frame;
e.g. start with adding some widgets and one new class method:

    class Main(Frame):
        def __init__(self, master=None, **kw):
            Frame.__init__(self, master, **kw)
            self.label = Label(self, text="Hello")
            self.button = Button(self, text="Change quote", command=self.change_quote)

        def change_quote(self):
            if self.label['text'] == "Hello":

You see, we added a Button and a Label to the Frame. As first argument we passed "self" to the widgets,
which, you remember, is a Frame instance; by calling the variable "self.label" instead of just "label" however you did some more:
you added a new attribute to the Frame instance. the advantage is that you now can access the label from the outside:

  root = Tk()
  main = Main(root)

changes the Label's text.

The last thing is that you defined a new class method for the "Main" class: change_quote()
You see, change_quote() is defined *outside* of __init__() (note the indentation level), 
so it becomes another class attribute, just as __init__() itself and all the methods inherited from Frame.
You can try:

  root = Tk()
  main = Main(root)
  root.after(3000, main.change_quote)

Here when you call "main.change_quote" the class instance is passed as first argument to the method,
that's why you have to pass "self" as argument in the class method definition, basically the same as with __init__(),
and that's why you have to use "self.change_quote" as command for the button.

I hope this still made sense.

> >    def changeQuote():
> >        currQuote = showquote.cget('config')  # Get the current quote
> >        newQuote = random.choice(quotes.quote)
> >        while newQuote == currQuote:          # Make sure the new quote differs
> >            newQuote = random.choice(quotes.quote)
> >        showquote.config(text=newQuote)
> >    Button(self, text='Show another quote', command=changeQuote).pack()
> Aaag. I'm confused... I just tried you changeQuote example with out the above stuff... didn't work. My error message:
> 	AttributeError: 'NoneType' object has no attribute 'config'

Just a guess: a common mistake among Tkinter beginners is:

    mylabel = Label(parent, text="Hello").pack()

The problem here is, because pack() returns None you don't have a reference to the Label,
but set the "mylabel" variable to None. If this is what happened, you need to split the above into two lines:

    mylabel = Label(parent, text="hello")

> so I edited it a little, still didn't work (same error). I tried the  class Main(Frame) and didn't get it to work either. Do you think you could make it a little bit simpler? If you can't that's ok, I'll try to study it more.

The changeQuote() method in the example contains a typo:

    currQuote = showquote.cget('config')  # Get the current quote
this must be:

    currQuote = showquote.cget('text')

I hope this helps


More information about the Tutor mailing list