Search code examples
pythonsqltkinterpypyodbc

Getting NameError: free variable referenced before assignment, but cannot see where I've done so


I am trying to create a basic log in frame for my application (not worrying about it being complicated at all, it's for an A Level project). In the checklogin function, I am trying to retrieve the username input by the user, and seeing if it is in the database already, and will add a bit to check that the passwords match later. However I keep getting the error 'NameError: free variable 'userentry' referenced before assignment in enclosing scope'. Looking this error, it says the usual reason is because 'userentry' is being reassigned to later in the same function. However, I am struggling to see where this is the case. Any help will be appreciated.

class Login(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background="lightgreen")

    def check_login():
        sql = "SELECT Password FROM Player WHERE Username IN (?)"
        parameters = (userentry.get())
        cursor.execute(sql, parameters)
        print(cursor.fetchall())

    for col in range(5):
        self.grid_columnconfigure(col, minsize=50)

    for row in range(7):
        self.grid_rowconfigure(row, minsize=60)

    titlelbl = tk.Label(self,
                        text="Please enter your username and password",
                        font = "Verdana 20 bold",
                        fg="black",
                        bg="lightgreen")

    titlelbl.grid(column=1,
                  row=0,
                  columnspan=3)

    usernamelbl = tk.Label(self,
                           text="Username:",
                           font="Verdana 14",
                           bg="lightgreen")

    usernamelbl.grid(column=1,
                     row=2)

    passwordlbl = tk.Label(self,
                           text="Password:",
                           font="Verdana 14",
                           bg="lightgreen")

    passwordlbl.grid(column=1,
                     row=4)

    signupbtn = tk.Button(self,
                          text="Sign Up",
                          fg="lightgreen",
                          bg="darkgreen",
                          height="3",
                          width="12",
                          command = lambda: controller.show_frame("SignUp"))

    signupbtn.grid(column=3,
                   row=6)

    loginbtn = tk.Button(self,
                         text="Log In",
                         fg="lightgreen",
                         bg="darkgreen",
                         height="3",
                         width="12",
                         command = check_login())

    loginbtn.grid(column=1,
                  row=6)

    userentry = tk.Entry(self)

    userentry.grid(column=3,
                       row=2)

    passentry = tk.Entry(self,
                         show="*")

    passentry.grid(column=3,
                       row=4)                       

Solution

  • In this code

    loginbtn = tk.Button(self,
                         text="Log In",
                         fg="lightgreen",
                         bg="darkgreen",
                         height="3",
                         width="12",
                         command = check_login())
    

    change command = check_login() to command = check_login (without the parentheses).

    check_login is the function object. check_login() calls the function object and evaluates to the return value of the function.

    The parentheses tell Python to call the function immediately. Here, when the tk.Button is defined, the userentry variable is not yet defined. This is why you are getting a NameError when check_login is called.

    So if instead you use command = check_login then the tk.Button receives the function object, check_login, which it can then call later, when the button is pressed.