David Černý
David Černý

Reputation: 112

Tkinter Attribute Error when configuring tk Buttons

I'm building an SMTP mail sender and when I started building the login window, I stumbled uppon an Attribute Error when trying to set a button state to 'disabled'.

The first function in this example is the root and the second one is the one causing problems.

class smtp_gui(tk.Tk):
    is_logged_in = False
    user_email = ''
    user_passwd = ''

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.wm_title("SMTP Mail Sender")
        self.resizable(False, False)
        self.iconbitmap('at.ico')
        self.container = tk.Frame(self)
        self.btn_container = tk.Frame(self)
        self.bind('<Control-s>', self.send)
        self.bind('<Control-h>', self.get_help)

        self.container.pack(side="top", padx=1, pady=1)
        self.container.columnconfigure(1, weight=1)
        self.btn_container.pack(side="bottom", padx=1, pady=1, fill='x')

        tk.Label(self.container, text='From:').grid(row=0, column=0, sticky='e')
        tk.Label(self.container, text='To:').grid(row=1, column=0, sticky='e')
        tk.Label(self.container, text='Subject:').grid(row=2, column=0, sticky='e')

        self.refresh()

        self.To = tk.Entry(self.container)
        self.To.grid(row=1, column=1, sticky="we", pady=3, padx=2)

        self.Subject = tk.Entry(self.container)
        self.Subject.grid(row=2, column=1, sticky="we", pady=2, padx=2)

        self.Msg = tk.Text(self.container, width=40, height=5)
        self.Msg.grid(columnspan=2, padx=3, pady=3)

        send_btn = tk.Button(self.btn_container, text="Send", command=self.send)
        send_btn.pack(side='right', padx=2, pady=1)

        quit_btn = tk.Button(self.btn_container, text="Quit", command=self.quit)
        quit_btn.pack(side='right', padx=2, pady=1)

    def send(self, event=None):
        print(self.Msg.get("0.0", "end"))

    def refresh(self):
        if self.logged_in():
            try:
                self.login_btn.destroy()
            except AttributeError:
                pass
            self.mailabel = tk.Label(self.container, bd=1, text=smtp_gui.user_email, relief='sunken')
            self.mailabel.grid(row=0, column=1, pady=3, padx=2, sticky='we')
        else:
            try:
                self.mailabel.destroy()
            except AttributeError:
                pass
            self.login_btn = tk.Button(self.container, text="login before sending", command=self.get_login)
            self.login_btn.grid(row=0, column=1, sticky="we", pady=3, padx=2)

    def logged_in(self):
        return smtp_gui.is_logged_in

    def get_login(self):
        login = LoginWindow()
        self.refresh()

    def get_help(self, event=None):
        get_help = HelpWindow()


class LoginWindow(tk.Toplevel):

    def __init__(self, *args, **kwargs):
        # Window config
        tk.Toplevel.__init__(self, *args, **kwargs)
        self.grab_set()
        self.wm_title("Email login")
        self.resizable(False, False)
        self.iconbitmap('login.ico')
        # Containers
        self.container = tk.Frame(self)
        self.btn_container = tk.Frame(self)

        self.container.pack(padx=2, pady=2)
        self.btn_container.pack(side="bottom", padx=1, pady=1, fill='x')

        # Tracers

        self.tracetest = tk.StringVar()
        self.tracetest.trace('w', self.tracer)

        # Window elements
        tk.Label(self.container, text="Gmail address:").grid(row=0, column=0)
        tk.Label(self.container, text="Password:").grid(row=1, column=0)

        # Entries
        self.Address = tk.Entry(self.container, width=44, textvariable=self.tracetest)
        self.Address.grid(row=0, column=1, sticky="we", pady=3, padx=2)
        self.Address.insert(0, "your address")

        self.Passwd = tk.Entry(self.container, show="*", textvariable=None)
        self.Passwd.grid(row=1, column=1, sticky="we", pady=3, padx=2)

        # Buttons
        self.login_btn = tk.Button(self.btn_container, text="Login", command=self.get_credentials)
        self.login_btn.pack(side='right', padx=2, pady=3)

        storno_btn = tk.Button(self.btn_container, text="Storno", command=self.destroy)
        storno_btn.pack(side='right', padx=2, pady=3)

    def get_credentials(self):
        user_address = self.Address.get()
        try:
            assert(match(r'[a-zA-Z0-9][email protected]', user_address))
        except AssertionError:
        messagebox.showwarning("Invalid login", "This is not a valid gmail     address!")

    def tracer(self, *args):
        address = match(r'[a-zA-Z0-9][email protected]', self.Address.get())
        passwd = ''
        if passwd and address:
            self.login_btn.config(state='enabled') # Attribute error here, the program says these buttons don't exist in the functions even tough I defined them above
        else:
            self.login_btn.config(state='disabled')

The problematic function is at the very end (in the tracer function), you should be able to reproduce the problem with this code ( just import tkinter as tk and regex )

I'm guessing there is some kind of a reference problem.

EDIT - FULL TRACEBACK

Traceback (most recent call last):
  File "C:\Users\david\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1550, in __call__
    return self.func(*args)
  File "C:\Users\david\Documents\Git_repositories\smtp_client\main_menu.py", line 129, in tracer
    self.login_btn.config(state='disabled')
AttributeError: 'LoginWindow' object has no attribute 'login_btn'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\david\Documents\Git_repositories\smtp_client\main_menu.py", line 160, in <module>
    root.mainloop()
  File "C:\Users\david\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1131, in mainloop
    self.tk.mainloop(n)
  File "C:\Users\david\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1554, in __call__
    self.widget._report_exception()
AttributeError: 'StringVar' object has no attribute '_report_exception'

FOUND A SOLUTION

The traceback occured because the tracer was called before the creation of the button, when I moved the tracer after the initiation of the button, it worked perfectly, thanks guys!

Upvotes: 0

Views: 1189

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 386342

The trace fires when the value changes. The value changes when you do self.address.insert(0, "your address") because that widget is tied to the variable. All of this happens before you define self.login_btn.

You need to define the button before you enable the trace, or before you change the value of the traced variable.

Upvotes: 2

Related Questions