-2

I've written a simple script with TKinter and SQLAlchemy, where an sqlite .db is created, saving Employee information such as name, age, address, etc, etc.

I realized that if a user puts a string in the age and/or zip field (both fields require int's), an error will be thrown. So I put a try-except clause in the add_data method. If a string is placed in the age field for instance, the except clause will be thrown correctly and the showinfo window will appear with the error, but when the user clicks 'ok' on the box, an UnboundLocalError: local variable 'age' referenced before assignment error will show in IDLE.

I have read "https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value" and the other similar questions to no avail.

The data (employee info) will be saved correctly when the user corrects the mistake (puts an int the field & clicks the Add button). However, I obviously don't want this UnboundLocalError being thrown. I just don't know how to correct it, or why it's being thrown to begin with. . .Any help is greatly appreciated as always!

from datetime import datetime
from Tkinter import *
from tkMessageBox import *
import sqlite3, sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, DateTime, Integer, String, MetaData
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# map classes to tables through Base Class
Base = declarative_base()


class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(250))
    age = Column(Integer)
    address = Column(String(250))
    city = Column(String(10))
    state = Column(String(2))
    zip = Column(Integer)
    ssn = Column(String(12))
    phone = Column(String(12))
    cell = Column(String(12))

# create engine for Session connection
engine = create_engine('sqlite:///employee.db',echo=True)

# create all tables in engine ('CREATE TABLE' in raw SQL)
Base.metadata.create_all(engine)

# create configured 'Session' class
Session = sessionmaker(bind=engine)

# create session
session = Session()


class GUI:
    def __init__(self, parent):
        self.parent = parent

    # create top frame
        frame = Frame(parent)
        frame.pack(expand=YES, fill=BOTH, padx=5, pady=5)

        # create bottom frame
        btm_frame = Frame(parent, relief=SUNKEN, borderwidth=1)
        btm_frame.pack(side=BOTTOM, expand=YES, fill=BOTH, padx=5, pady=5)

        # create datetime object
        d = datetime.today()
        date = d.strftime("%Y-%m-%d")


#------------------------Create Labels-------------------------------#


        # label to display date
        date_label = Label(btm_frame, text=date)
        date_label.pack(side=LEFT)



        # name label
        name_label = Label(frame, text="Enter Name:")
        name_label.grid(row=0, sticky=W)

        # age label
        age_label = Label(frame, text="Enter Age:")
        age_label.grid(row=1, sticky=W)

        # address label
        addr_label = Label(frame, text="Enter Address:")
        addr_label.grid(row=2, sticky=W)

        # city label
        city_label = Label(frame, text="Enter City:")
        city_label.grid(row=3, sticky=W)

        # state label
        state_label = Label(frame, text="Enter State:")
        state_label.grid(row=4, sticky=W)

        # zip code label
        zip_label = Label(frame, text="Enter Zip Code:")
        zip_label.grid(row=5, sticky=W)

        # ssn label
        ssn_label = Label(frame, text="Enter Social Security #:")
        ssn_label.grid(row=6, sticky=W)

        # phone label
        phone_label = Label(frame, text="Enter Phone #:")
        phone_label.grid(row=7, sticky=W)

        # cell label
        cell_label = Label(frame, text="Enter Cell #:")
        cell_label.grid(row=8, sticky=W)


#----------------------Create Vars and Entry-------------------------#


        # name variable and entry
        self.name_var = StringVar()
        self.e1 = Entry(frame, textvariable=self.name_var)
        self.e1.grid(row=0, column=1)

        # age variable and entry
        self.age_var =  IntVar()
        self.e2 = Entry(frame, textvariable=self.age_var)
        self.e2.grid(row=1, column=1)

        # address variable and entry
        self.address_var = StringVar()
        self.e3 = Entry(frame, textvariable=self.address_var)
        self.e3.grid(row=2, column=1)

        # city variable and entry
        self.city_var = StringVar()
        self.e4 = Entry(frame, textvariable=self.city_var)
        self.e4.insert(0, "Roanoke")        # insert default value
        self.e4.grid(row=3, column=1)

        # state variable and entry
        self.state_var = StringVar()
        self.e5 = Entry(frame, textvariable=self.state_var)
        self.e5.insert(0, "VA")             # insert default value
        self.e5.grid(row=4, column=1)

        # zip code variable and entry
        self.zip_var = IntVar()
        self.e6 = Entry(frame, textvariable=self.zip_var)
        self.e6.grid(row=5, column=1)

        # s.s.n variable and entry
        self.ssn_var = StringVar()
        self.e7 = Entry(frame, textvariable=self.ssn_var)
        self.e7.grid(row=6, column=1)

        # phone variable and entry
        self.phone_var = StringVar()
        self.e8 = Entry(frame, textvariable=self.phone_var)
        self.e8.grid(row=7, column=1)

        # cell variable and entry
        self.cell_var = StringVar()
        self.e9 = Entry(frame, textvariable=self.cell_var)
        self.e9.grid(row=8, column=1)

        # quit, search, clear, add, delete buttons
        quit_button = Button(btm_frame, text="Quit", relief=GROOVE, command=parent.destroy)
        quit_button.pack(side=RIGHT)        

        search_button = Button(btm_frame, text="Search", relief=GROOVE, command=self.search)
        search_button.pack(side=RIGHT, padx=1, pady=1)

        clear_button = Button(btm_frame, text="Clear", relief=GROOVE, command=self.clear_entries)
        clear_button.pack(side=RIGHT, padx=1, pady=1)        

        del_button = Button(btm_frame, text="Delete", relief=GROOVE, command=self.del_employee)
        del_button.pack(side=RIGHT, padx=1, pady=1)

        add_button = Button(btm_frame, text="Add", relief=GROOVE, command=self.add_data)
        add_button.pack(side=RIGHT, padx=1, pady=1)


    def add_data(self):        
        name = self.name_var.get()        
        try:
            age = self.age_var.get()
        except ValueError:
                showinfo("Error:", "Please Enter A Number In Age Field.")       
        addr = self.address_var.get()
        city = self.city_var.get()
        state = self.state_var.get()
        zip = self.zip_var.get()   
        ssn = self.ssn_var.get()
        phone = self.phone_var.get()
        cell = self.cell_var.get()
        # create new Employee in .db
        new_person = Employee(name=name, age=age, address=addr, city=city, state=state, zip=zip, ssn=ssn, phone=phone, cell=cell)
        session.add(new_person)
        session.commit()
        session.close()
        self.callback()
        return        

    def callback(self):        
        showinfo("New Employee", "Data Added")
        self.clear_entries()
        return        

    def clear_entries(self):
        entries = [self.e1, self.e2, self.e3, self.e4, self.e5, self.e6, self.e7, self.e8, self.e9]
        for entry in entries:
            entry.delete(0, END)
        return            

    def search(self):
        search_win = Toplevel()
        search_win.title("Employee Search")

        # create labels for each employee attribute
        attr = ["Id","Name","Age","Address","City","State","Zip","SSN","Cell","Phone"]
        column = 0
        for a in attr:
                Label(search_win, text=a).grid(row=0,column=column,padx=2, pady=2)
                column += 1

        # search all employees, put each emp. in Entry Widget with For Loop
        res = session.query(Employee).all()
        row = 1
        column = 0
        for employee in res:
            txt = [employee.id, employee.name, employee.age, employee.address, employee.city, employee.state, employee.zip, employee.ssn, employee.phone, employee.cell]
            for t in txt:
                ent = Entry(search_win, relief=RIDGE, width=19)
                ent.grid(row=row, column=column, sticky=W, padx=1, pady=1)
                ent.insert(0, t)
                column += 1
            row += 1
            column = 0
        return

        def del_employee(self):
        del_win = Toplevel()
        del_win.title("Delete Employee")

        id_label = Label(del_win, text="Enter Employee Id:")
        id_label.grid(row=0, column=0, padx=5, pady=5)

        self.employee_id = IntVar()
        self.e10 = Entry(del_win, textvariable=self.employee_id)
        self.e10.grid(row=0, column=1, padx=5, pady=5)

        del_button = Button(del_win, text="Delete Employee", relief=GROOVE, command=self.erase)
        del_button.grid(row=0, column=2, padx=5, pady=5)
        return

    def erase(self):
        emp_id = self.employee_id.get()
        res = session.query(Employee).filter(Employee.id==emp_id).first()
        session.delete(res)
        session.commit()
        showinfo("Employee", "Data Deleted")
        return



if __name__ == '__main__':
    root = Tk()
    root.geometry("310x270")
    root.title("Employee Info")
    mygui = GUI(root)
    root.mainloop()`
Chris Kavanagh
  • 451
  • 1
  • 5
  • 15

1 Answers1

-1
       age = "" 
        try:
            age = self.age_var.get()
        except ValueError:
                showinfo("Error:", "Please Enter A Number In Age Field.")

Have a look Here

Community
  • 1
  • 1
backtrack
  • 7,996
  • 5
  • 52
  • 99
  • Thanks, I tried this and it changed the behaviour completely. No more unboundlocalerror, but it saves the data, with the age variable set to 0. In other words, if I put a string in the age field, it throws the showinfo box, but when I click 'ok', it saves the data as is, without being able to change the age variable to the value I want. – Chris Kavanagh Mar 16 '15 at 05:24
  • Ok, but with the age variable inside the try: clause, I understand it's now a local variable, but where else am I accessing it in the add_data method? Is it in the new_person method where I'm assigning age=age? Again, thank you for the help. – Chris Kavanagh Mar 16 '15 at 05:36
  • In the add_data you have the following line...( new_person = Employee(name=name, age=age, address=addr, city=city, state=state, zip=zip, ssn=ssn, phone=phone, cell=cell)) In this line you are using age. As i mentioned in my post, in your provided code age's socpe is limited with in try block. So you got the error – backtrack Mar 16 '15 at 05:39
  • Ok, I see now why I got the error. I had thought this may be the problem so I changed the var name to `age1` in the try: clause to hopefully avoid this problem and it didn't work. Then in the new_person method I had age=age1. I;m a little confused why this didn't work instead. – Chris Kavanagh Mar 16 '15 at 05:44
  • As far as your suggestion `age=''`, this still won't work. It just sets the age in the .db to an empty string. . .In other words I want the user to still be able to set the age to whatever they want, which I was able to do before even when getting the UnboundLocalError. When the user clicked the 'ok' button in the showinfo box, it would allow them to enter a new value in the age field, then click 'Add'. Assigning the age var outside the try: clause prevents this. But thanks again for answering my original question. – Chris Kavanagh Mar 16 '15 at 05:49
  • Let me ask you a simple question, Why are you suing try for age alone. When you declare age ="" and assign value in the try block the value of the age will be updated to the user input value. :) Please tell me what was the error your getting now – backtrack Mar 16 '15 at 06:05
  • I did this (try/except) clause in the age field as a test run so to speak. I realized I hadn't designed a way for the field to fail if someone used a string instead of an int. I was going to put the rest in try/except clauses also. . .However, when I set age to a default value as you suggest, it won't work the way I want. If I put a string in the age field & click Add button, the showinfo box opens as it should. However, when I click 'ok' in the box, it then saves everything to the .db immediately with the default val of age. I want the user to be able to correct the age b4 saving to the .db. – Chris Kavanagh Mar 16 '15 at 10:21
  • I realize this is a different question/problem altogether and will probably ask it in another thread. . .Your help is greatly appreciated! Again, thanks. – Chris Kavanagh Mar 16 '15 at 10:23
  • 1
    The phrase "When you age is inside try it is considered as local variable and its scope is limited with in try block alone. " **is false**. There isn't a different scoping inside the try block. – Bryan Oakley Mar 18 '15 at 10:50