tkinter: NameError: name 'root' is not defined

Question:

I’ve split my tkinter app in more file, and right now I’ve two file:

main.py

import tkinter as tk

from login_info import LoginInfo 


class Main(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)        

        self.login_page = LoginInfo(self)
        self.login_page.pack(expand='True')     

if __name__ == "__main__":
    root = tk.Tk()
    Main(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

login_page.py

import tkinter as tk
class LoginInfo(tk.Frame):
   def __init__(self, parent, *args, **kwargs):
       tk.Frame.__init__(self, parent, *args, **kwargs)

       self.login_frame = tk.Frame(self)
       self.login_frame.pack()

       self.username_label = tk.Label(self.login_frame, text='Username:')
       self.username_label.grid(row=0, column=0, padx=(10,0), pady=(10,0))

       self.username_entry = tk.Entry(self.login_frame)
       self.username_entry.grid(row=0, column=1, padx=(10,0), pady=(10,0))

       self.password_label = tk.Label(self.login_frame, text='Password:')
       self.password_label.grid(row=1, column=0, padx=(10,0), pady=(10,0))

       self.password_entry = tk.Entry(self.login_frame)
       self.password_entry.grid(row=1, column=1, padx=(10,0), pady=(10,0))

       self.login_button = tk.Button(self.login_frame, text='Login', command=self.login)

       self.login_button.grid(row=2, column=0, columnspan=2, pady=20)
       root.bind('<Return>', self.login)

   def login(self,event):
       print('Logged In')

if __name__ == "__main__":
   root = tk.Tk()
   LoginInfo(root).pack(side="top", fill="both", expand=True)
   root.mainloop()

In the login page I’m trying to bind the return button to a function but I’m gettin this error:

NameError: name 'root' is not defined

This happen only if I launch the code from main.py, if I launch it from login_page.py it works

Asked By: Kome Gognome

||

Answers:

Remove the duplicated …

if __name__ == "__main__":
   root = tk.Tk()
   LoginInfo(root).pack(side="top", fill="both", expand=True)
   root.mainloop()

… from the login_page.py and launch the code from main.py.

Use if __name__ == '__main__':... only once and only in the module which starts the program. Because only in this module the special variable __main__ will have the value '__main__'.

Also, make sure that you import from login_page, not from login_info in the main.py, as you named that module login_page.py and not login_info.py.

Concrete code

I was able to get your example running by doing this:

# main.py

import tkinter as tk

from login_page import LoginPage

class Main(tk.Frame):
  def __init__(self, parent, *args, **kwargs):
    tk.Frame.__init__(self, parent, *args, **kwargs)
    self.login_page = LoginPage(self)
    self.login_page.pack(expand='True')

if __name__ == "__main__":
  root = tk.Tk()
  main = Main(root)
  main.pack(side="top", fill="both", expand=True)
  root.mainloop()
# login_page.py

import tkinter as tk

class LoginPage(tk.Frame):
  def __init__(self, parent, *args, **kwargs):
    tk.Frame.__init__(self, parent, *args, **kwargs)
    
    self.login_frame = tk.Frame(self)
    self.login_frame.pack()
    
    self.username_label = tk.Label(self.login_frame, text='Username:')
    self.username_label.grid(row=0, column=0, padx=(10,0), pady=(10,0))
    
    self.username_entry = tk.Entry(self.login_frame)
    self.username_entry.grid(row=0, column=1, padx=(10,0), pady=(10,0))
    
    self.password_label = tk.Label(self.login_frame, text='Password:')
    self.password_label.grid(row=1, column=0, padx=(10,0), pady=(10,0))
    
    self.password_entry = tk.Entry(self.login_frame)
    self.password_entry.grid(row=1, column=1, padx=(10,0), pady=(10,0))
    
    self.login_button = tk.Button(self.login_frame, text='Login', command=self.login)
    
    self.login_button.grid(row=2, column=0, columnspan=2, pady=20)
    
    self.bind_all('<Return>', self.login)

  def login(self, event=None):
    print('Logged In')

The trick is to use self.bind_all instead of root.bind, near the bottom of login_page.py, which binds the key to the whole application. This function seems not to be documented in the official Python tkinter docs, which may indicate that there is a more pythonic solution.

I personally would prefer to put the declaration of this binding in the main.py. We have a sub unit here, which silently modifies a parent unit. The bigger the project gets, the more this leads to problems.

Answered By: Nils Lindemann
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.