1

I am new to relatively Python and tkinter. I am trying the make a button for each item that I put in the list.

from tkinter import *

x = ['d', 'f']

class GUI:

    def __init__(self, master):

        self.master = master
        master.title("window")

        self.label = Label(master, text="Window")
        self.label.grid(columnspan=5, sticky=W+E)

        self.close_button = Button(master, text="Close", command=master.destroy)
        self.close_button.grid(columnspan=5, sticky=W+E)

        for s in x:
            self.s_button = Button(master, text=s, command=self.s)
            self.s_button.grid(columnspan=5, sticky=W+E)

    for a in x:
        def a(self):
            print (a)     

root = Tk()
gui = GUI(root)
root.mainloop()

Whenever I run the code I receive a error AttributeError: 'GUI' object has no attribute 's'.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
Dave
  • 21
  • 2
  • You can't create dynamic functions like that. I also don't see the need to do so. – Steven Summers Mar 04 '17 at 00:16
  • You have `command=self.s` but you never assigned `self.s` – Barmar Mar 04 '17 at 00:16
  • 1
    The error message has nothing to do with the function you're defining in the loop, it's from `self.s`. – Barmar Mar 04 '17 at 00:16
  • It looks like you're trying to create and access dynamic properties. See http://stackoverflow.com/questions/1325673/how-to-add-property-to-a-class-dynamically – Barmar Mar 04 '17 at 00:24

1 Answers1

1

First mistake: command=self.s looks for an attribute called "s". If you want to look for an attribute whose name is the content of the s variable, you should write command=getattr(self, s).

Second mistake: def a(self) defines a class method called "a". If you want to programmatically generate named methods, you may do so after creating the class, with something like:

for a in x:
    def fun(self):
        print (a)
    setattr(GUI, a, fun)

Edit: this is still wrong, because the same variable a is used for both functions.

Anyway, it looks quite strange to generate methods that way, I’d rather have a single method that takes the content to be printed as an argument, something like:

from tkinter import *

x = ['d', 'f']

class GUI:
    def __init__(self, master):
        self.master = master
        master.title("window")

        self.label = Label(master, text="Window")
        self.label.grid(columnspan=5, sticky=W+E)

        self.close_button = Button(master, text="Close", command=master.destroy)
        self.close_button.grid(columnspan=5, sticky=W+E)

        for s in x:
            self.s_button = Button(master, text=s, command=(lambda: self.print_(s)))
            self.s_button.grid(columnspan=5, sticky=W+E)

    def print_(self, a):
        print (a)

root = Tk()
gui = GUI(root)
root.mainloop()

Edit: this is still wrong because of the s variable that is shared since the for loop does not create a new scope. Only functions create new scopes. Here is some working code:

from tkinter import *

x = ['d', 'f']

class GUI:
    def __init__(self, master):
        self.master = master
        master.title("window")

        self.label = Label(master, text="Window")
        self.label.grid(columnspan=5, sticky=W+E)

        self.close_button = Button(master, text="Close", command=master.destroy)
        self.close_button.grid(columnspan=5, sticky=W+E)

        for s in x:
            self.s_button = Button(master, text=s, command=self.make_printer(s))
            self.s_button.grid(columnspan=5, sticky=W+E)

    def make_printer(self, a):
        def fun():
            print (a)
        return fun

root = Tk()
gui = GUI(root)
root.mainloop()
Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
user2233709
  • 173
  • 1
  • 7