AttributeError:'tkapp'对象没有属性'Menu1Button

0 投票
1 回答
5357 浏览
提问于 2025-04-18 02:09

我刚开始学习Python,想要动态地改变按钮的文本和命令,以便制作子菜单。以下是我的代码:

#!/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()

然后错误的追踪信息是

  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'

我觉得问题出在SetMenu函数里的"self",但是我试过不使用"self",结果还是不行。

编辑:更新后的代码

#!/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()

1 个回答

2

你没有把 Menu1Button 定义为类的属性,所以它只能在你的 __init__ 方法里面使用。应该这样定义:

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.Menu3Button,你把命令定义成 self.SetMenu(MenuIndex = 2)。在 Python 里,这会在第一次执行到这行时就尝试运行 self.SetMenu 函数,并传入参数 2(顺便说一下,你不需要写成 MenuIndex=2,直接写 2 就可以了)。这导致 SetMenu 函数在 Menu3Button 还没被定义为类属性之前就被执行了。因此,Python 找不到这个属性,就会报 AttributeError 错误。

传给小部件的 command 应该是一个函数实例。例如:

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

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

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

在有效的情况下,我们传递的是一个部分函数(也就是一个已经附上参数的半成品函数):

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

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

In [31]: partial_printit()
partial call

所以在你的情况下:

command = self.SetMenu(MenuIndex = 2)

变成

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

这对于你所有小部件的 command 关键字都是一样的。

想了解更多关于 lambda 函数的信息,可以查看这里: https://docs.python.org/2/tutorial/controlflow.html#lambda-expressions

撰写回答