Graou13
Graou13

Reputation: 1375

AttributeError: 'tkapp' object has no attribute 'Menu1Button'

I'm new to python and I'm trying to change dynamically button's text and command to make sub-menus. here's my code:

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

import tkinter

class Display(tkinter.Tk):
    def __init__(self,parent):
        tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.GuiDisplay()

    def GuiDisplay(self):

        self.grid()
        self.geometry("1280x720")
        #self.overrideredirect(True) #uncomment for fullscreen

        """Build the GUI"""

        MessageDisplay = tkinter.Label(self, text = 'No messages from slaves', anchor = 'nw', justify = 'left', relief = 'ridge', font = ("Courier New", 15), bg = '#e4e7e8', fg = '#2980b9')
        MessageDisplay.grid(row = 0, column = 0, columnspan = 3, sticky = 'wens', ipadx = 2, ipady = 2)

        ClockDisplay = tkinter.Label(self, text = '00:00', relief = 'ridge', font = ("Courier New", 40), bg = '#e4e7e8', fg = '#2980b9')
        ClockDisplay.grid(row = 0, column = 3, sticky = 'wens', ipadx = 2, ipady = 2)

        Menu1Button = tkinter.Button(self, text = 'Ring', command = self.Action(Do = 'Rng'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        Menu1Button.grid(row = 1, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        Menu2Button = tkinter.Button(self, text = 'Toggle holliday mode', command = self.Action(Do = 'SwHolDay'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        Menu2Button.grid(row = 2, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        Menu3Button = tkinter.Button(self, text = 'Edit time', command = self.SetMenu(MenuIndex = 2), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        Menu3Button.grid(row = 3, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        Menu4Button = tkinter.Button(self, text = 'Add/Edit alarms', command = self.SetMenu(MenuIndex = 3), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        Menu4Button.grid(row = 4, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        Menu5Button = tkinter.Button(self, text = 'Shutdown', command = self.Action(Do = 'Shutdwn'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        Menu5Button.grid(row = 5, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        MenuDescription = tkinter.Label(self, text = 'No menu selected.', anchor = 'nw', justify = 'left', relief = 'ridge', font = ("Courier New", 15), bg = '#e4e7e8', fg = '#2980b9')
        MenuDescription.grid(column = 2, columnspan = 2, row = 1, rowspan = 5, sticky = 'wens', ipadx = 2, ipady = 2)

        self.grid_columnconfigure(0, weight = 3)
        self.grid_columnconfigure(1, weight = 3)
        self.grid_columnconfigure(2, weight = 3)
        self.grid_columnconfigure(3, weight = 1)
        self.grid_rowconfigure(0, weight = 10)
        self.grid_rowconfigure(1, weight = 18)
        self.grid_rowconfigure(2, weight = 18)
        self.grid_rowconfigure(3, weight = 18)
        self.grid_rowconfigure(4, weight = 18)
        self.grid_rowconfigure(5, weight = 18)

    def SetMenu(self, MenuIndex):

        if MenuIndex==1:
            self.Menu1Button.configure(text = 'Ring', command = self.Action(Do = 'Rng'))
            self.Menu2Button.configure(text = 'Toggle holliday mode', command = self.Action(Do = 'SwHolDay'))
            self.Menu3Button.configure(text = 'Edit time', command = self.SetMenu(MenuIndex = 2))
            self.Menu4Button.configure(text = 'Add/Edit alarms', command = self.SetMenu(MenuIndex = 3))
            self.Menu5Button.configure(text = 'Shutdown', command = self.Action(Do = 'Shutdwn'))
        elif MenuIndex==2:
            self.Menu1Button.configure(text = 'Edit hours', command = self.Action(Do = 'ModH'))
            self.Menu2Button.configure(text = 'Edit minutes', command = self.Action(Do = 'ModM'))
            self.Menu3Button.configure(text = 'Edit seconds', command = self.Action(Do = 'ModS'))
            self.Menu4Button.configure(text = 'Internet synchronization', command = self.Action(Do = 'Synch'))
            self.Menu5Button.configure(text = 'Return to menu', command = self.SetMenu(MenuIndex = 1))
        elif MenuIndex==3:
            self.Menu1Button.configure(text = 'Edit hours', command = self.Action(Do = 'ModHA'))
            self.Menu2Button.configure(text = 'Edit minutes', command = self.Action(Do = 'ModMA'))
            self.Menu3Button.configure(text = 'Edit seconds', command = self.Action(Do = 'ModSA'))
            self.Menu4Button.configure(text = 'Switch mode', command = self.Action(Do = 'ChMod'))
            self.Menu5Button.configure(text = 'Return to menu', command = self.SetMenu(MenuIndex = 1))

    def  Action(self, Do):
        pass

if __name__ == "__main__":
        app = Display(None)
        app.title('Web Bell')
        app.mainloop()

and the traceback for the error is

  File "\\uranus\faure\Bureau\project\webBell.py", line 80, in <module>
    app = Display(None)
  File "\\uranus\faure\Bureau\project\webBell.py", line 10, in __init__
    self.GuiDisplay()
  File "\\uranus\faure\Bureau\project\webBell.py", line 32, in GuiDisplay
    Menu3Button = tkinter.Button(self, text = 'Edit time', command = self.SetMenu(MenuIndex = 2), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
  File "\\uranus\faure\Bureau\project\webBell.py", line 64, in SetMenu
    self.Menu1Button.configure(text = 'Edit hours', command = self.Action(Do = 'ModH'))
  File "C:\Python33\lib\tkinter\__init__.py", line 1867, in __getattr__
    return getattr(self.tk, attr)
AttributeError: 'tkapp' object has no attribute 'Menu1Button'

I guess it come from the "self" in the SetMenu function but I tried without and it still didn't works.

EDIT: updated code

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

import tkinter

class Display(tkinter.Tk):
    def __init__(self,parent):
        tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.GuiDisplay()

    def GuiDisplay(self):

        self.grid()
        self.geometry("1280x720")
        #self.overrideredirect(True) #uncomment for fullscreen

        """Build the GUI"""

        self.MessageDisplay = tkinter.Label(self, text = 'No messages from slaves', anchor = 'nw', justify = 'left', relief = 'ridge', font = ("Courier New", 15), bg = '#e4e7e8', fg = '#2980b9')
        self.MessageDisplay.grid(row = 0, column = 0, columnspan = 3, sticky = 'wens', ipadx = 2, ipady = 2)

        self.ClockDisplay = tkinter.Label(self, text = '00:00', relief = 'ridge', font = ("Courier New", 40), bg = '#e4e7e8', fg = '#2980b9')
        self.ClockDisplay.grid(row = 0, column = 3, sticky = 'wens', ipadx = 2, ipady = 2)

        self.Menu1Button = tkinter.Button(self, text = 'Ring', command = self.Action(Do = 'Rng'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        self.Menu1Button.grid(row = 1, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        self.Menu2Button = tkinter.Button(self, text = 'Toggle holliday mode', command = self.Action(Do = 'SwHolDay'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        self.Menu2Button.grid(row = 2, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        self.Menu3Button = tkinter.Button(self, text = 'Edit time', command = self.SetMenu(MenuIndex = 2), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        self.Menu3Button.grid(row = 3, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        self.Menu4Button = tkinter.Button(self, text = 'Add/Edit alarms', command = self.SetMenu(MenuIndex = 3), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        self.Menu4Button.grid(row = 4, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        self.Menu5Button = tkinter.Button(self, text = 'Shutdown', command = self.Action(Do = 'Shutdwn'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
        self.Menu5Button.grid(row = 5, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

        self.MenuDescription = tkinter.Label(self, text = 'No menu selected.', anchor = 'nw', justify = 'left', relief = 'ridge', font = ("Courier New", 15), bg = '#e4e7e8', fg = '#2980b9')
        self.MenuDescription.grid(column = 2, columnspan = 2, row = 1, rowspan = 5, sticky = 'wens', ipadx = 2, ipady = 2)

        self.grid_columnconfigure(0, weight = 3)
        self.grid_columnconfigure(1, weight = 3)
        self.grid_columnconfigure(2, weight = 3)
        self.grid_columnconfigure(3, weight = 1)
        self.grid_rowconfigure(0, weight = 10)
        self.grid_rowconfigure(1, weight = 18)
        self.grid_rowconfigure(2, weight = 18)
        self.grid_rowconfigure(3, weight = 18)
        self.grid_rowconfigure(4, weight = 18)
        self.grid_rowconfigure(5, weight = 18)

    def SetMenu(self, MenuIndex):

        if MenuIndex==1:
            self.Menu1Button.configure(text = 'Ring', command = self.Action(Do = 'Rng'))
            self.Menu2Button.configure(text = 'Toggle holliday mode', command = self.Action(Do = 'SwHolDay'))
            self.Menu3Button.configure(text = 'Edit time', command = self.SetMenu(MenuIndex = 2))
            self.Menu4Button.configure(text = 'Add/Edit alarms', command = self.SetMenu(MenuIndex = 3))
            self.Menu5Button.configure(text = 'Shutdown', command = self.Action(Do = 'Shutdwn'))
        elif MenuIndex==2:
            self.Menu1Button.configure(text = 'Edit hours', command = self.Action(Do = 'ModH'))
            self.Menu2Button.configure(text = 'Edit minutes', command = self.Action(Do = 'ModM'))
            self.Menu3Button.configure(text = 'Edit seconds', command = self.Action(Do = 'ModS'))
            self.Menu4Button.configure(text = 'Internet synchronization', command = self.Action(Do = 'Synch'))
            self.Menu5Button.configure(text = 'Return to menu', command = self.SetMenu(MenuIndex = 1))
        elif MenuIndex==3:
            self.Menu1Button.configure(text = 'Edit hours', command = self.Action(Do = 'ModHA'))
            self.Menu2Button.configure(text = 'Edit minutes', command = self.Action(Do = 'ModMA'))
            self.Menu3Button.configure(text = 'Edit seconds', command = self.Action(Do = 'ModSA'))
            self.Menu4Button.configure(text = 'Switch mode', command = self.Action(Do = 'ChMod'))
            self.Menu5Button.configure(text = 'Return to menu', command = self.SetMenu(MenuIndex = 1))

    def  Action(self, Do):
        pass

if __name__ == "__main__":
        app = Display(None)
        app.title('Web Bell')
        app.mainloop()

Upvotes: 0

Views: 5363

Answers (1)

ebarr
ebarr

Reputation: 7842

You have not defined Menu1Button as a class attribute, so it is only scoped inside your __init__. It should be defined as:

self.Menu1Button = tkinter.Button(self, text = 'Ring', command = self.Action(Do = 'Rng'), font = ("Courier New", 20), bg = '#3498db', fg = '#e4e7e8', activebackground = '#2980b9', activeforeground = '#e4e7e8')
self.Menu1Button.grid(row = 1, column = 0, columnspan = 2, sticky = 'wens', padx = 2, ipadx = 4)

The same goes for all the other widgets defined inside your constructor.

EDIT:

So now that that first problem is fixed the issue becomes much clearer. You are defining the command attribute of your widgets incorrectly.

For example, for self.Menu3Button you define the command as self.SetMenu(MenuIndex = 2). In python this will try and execute the function self.SetMenu with the argument 2 (sidebar: you don't need to do MenuIndex=2, just 2 is fine) when it first gets to it. This causes the function SetMenu to execute before Menu3Button has been defined as a class attribute. Hence python cannot find the attribute and you get an AttributeError.

The command passed to the widget should be a function instance. For example:

# an example function
def printit(val):
    print val

# This is NOT valid
command = printit("hello!")

# This is valid
command = lambda:printit("hello!")

In the valid case we are passing a partial function (i.e. a half formed function where the arguments are already attached):

In [29]: printit("direct call")
direct call

In [30]: partial_printit = lambda:printit("partial call")

In [31]: partial_printit()
partial call

So in your case:

command = self.SetMenu(MenuIndex = 2)

becomes

command = lambda:self.SetMenu(MenuIndex = 2)

This is the same for all the command keywords for all your widgets.

For more info on lambda functions, see here: https://docs.python.org/2/tutorial/controlflow.html#lambda-expressions

Upvotes: 2

Related Questions