コード例 #1
0
ファイル: cli.py プロジェクト: etownsend/CIS422Team1Proj1
class CommandLineInterface(Cmd):
	"""
	VALID COMMANDS:
	
	| add
	| edit
	| delete
	| display
	| display_mail
	| sort
	| open
	| save
	| save_as
	| import
	| export
	| options
	| help
	| quit

	VALID FLAGS (used with keywords edit, delete, display, sort):
	NOTE all flags must be followed by a single argument in quotes (eg: -a "123 Easy St"; -fn "Kevin").
	-fn (first name)
	-ln (last name)
	-a (address)
	-a2 (address line 2)
	-c (city)
	-s (state)
	-z (ZIP Code)
	-p (phone number)
	-e (email)
	"""

	intro = WELCOME_MESSAGE
	prompt = "> "

	def __init__(self, *args, **kwargs):
		self.addressbook = AddressBook()
		self.current_book_filename = ""
		Cmd.__init__(self, *args, **kwargs)

	def do_add(self, line):
		"""
		Add a new contact to the Address Book.
		"""
		new_contact = Contact()

		print "* denotes a required field.\nPress ctrl-c to cancel."
		for field in CONTACT_FIELDS:
			valid = FIELD_VALIDATORS[field[0]]
			try:
				data = None
				if field[0] in REQUIRED_FIELDS:
					while getattr(new_contact, field[0], '')=='' or not valid(data)[0]:
						data = raw_input("{0}: ".format(field[1]))
						setattr(new_contact, field[0], data)
						if not valid(data)[0]:
							print valid(data)[1]
				else:
					while not valid(data)[0]:
						data = raw_input("{0}: ".format(field[1]))
						setattr(new_contact, field[0], data)
						if not valid(data)[0]:
							print valid(data)[1]
			
			except KeyboardInterrupt:
				print "\nCancelling New Contact\n"
				return

		self.addressbook.add(new_contact)
		print "Your entry was successfully added."

	def do_edit(self, line):
		"""
		Edit an existing contact in the Address Book.
		It actually deletes the "edited " contact and adds a new contact with the changed (or not) fields
		"""
		if line == "":
			print EDIT_AND_DELETE_NEED_ARGS
			return

		# Get list of tuples of (field, arg)
		fields_args = get_field_and_args_from_input(line)
		if not fields_args: return

		# Search address book for specified contact
		lists = [] # a list of lists returned by search, each list the result of a search on one field
		for field_arg in fields_args:
			lists.append(self.addressbook.search(field_arg[0], field_arg[1]))

		# Intersect all resulting lists from field queries
		# This is a list of tuples (index, contact)
		search_results = list(set(lists[0]).intersection(*lists[1:]))

		# if more than one such contact, display list of said contacts
		# if exacly one such contact, that is the one we want
		# if no such contact, inform user
		if len(search_results) > 1:
			print NARROW_SEARCH_MESSAGE
			for result in search_results:
				print "-------------------------"
				print result[1]

		elif len(search_results) == 1:
			print "\nPress ctrl-c to cancel."

			contact = search_results[0] #tuple (index, contact)

			# TODO, create temp contact to hold edits, replace old contact with new
			temp = Contact()

			for field in CONTACT_FIELDS:
				valid = FIELD_VALIDATORS[field[0]]
				try:
					new_data = None
					old_data = getattr(contact[1], field[0], '')
					while not valid(new_data)[0]:
						user_input = raw_input("{0}: {1}".format(field[1], old_data) + chr(8)*len(old_data))
						# NOTE: 8 is the ASCII value of backspace
						if not user_input:
							user_input = old_data

						# My hacky way of having the newly added chars replace those of the old_data
						# for cases where user replaces some, but not all, of the old data's chars
						user_input = list(user_input)
						new_data = list(old_data)
						for i in range(len(user_input)):
							try:
								new_data[i] = user_input[i]
							except IndexError:
								new_data.extend(user_input[i:])
								break
						new_data = ''.join(new_data)
						if not valid(new_data)[0]:
							print valid(new_data)[1]

					# Update the temp Contact Info
					setattr(temp, field[0], new_data)
			
				except KeyboardInterrupt:
					print "\nCancelling Contact Edit, reverting to original.\n"
					return

			confirm = raw_input("Are you sure you want to make these changes to this entry? (y/n): ")
			if confirm in ('yes', 'y'):
				self.addressbook.delete(contact[0])
				self.addressbook.add(temp)
				print "Edit complete"
				return

			else:
				print "\nCancelling Contact Edit, reverting to original.\n"


		else:
			print "There were no contacts that met your specification. Please generalize your request."

	def do_delete(self, line):
		"""
		Deletes a contact from the Address Book.
		Or if more than one contact meets the user's specification, presents a list of said contacts.
		If no contacts meet the user's specification, then does nothing.
		**User can only delete one contact at a time.**
		"""
		if line == "":
			print EDIT_AND_DELETE_NEED_ARGS
			return

		# Get list of tuples of (field, arg)
		fields_args = get_field_and_args_from_input(line)
		if not fields_args: return

		# Search address book for specified contact
		lists = [] # a list of lists returned by search, each list the result of a search on one field
		for field_arg in fields_args:
			lists.append(self.addressbook.search(field_arg[0], field_arg[1]))

		# Intersect all resulting lists from field queries
		# This is a list of tuples (index, contact)
		search_results = list(set(lists[0]).intersection(*lists[1:]))

		# if more than one such contact, display list of said contacts
		# if exacly one such contact, that is the one we want
		# if no such contact, inform user
		if len(search_results) > 1:
			print NARROW_SEARCH_MESSAGE
			for result in search_results:
				print "-------------------------"
				print result[1]

		elif len(search_results) == 1:
			contact = search_results[0] #tuple (index, contact)
			print contact[1]
			yes_delete = raw_input("Is this the entry you want to delete? (y/n): ")
			if yes_delete in ('yes', 'y'):
				yes_delete = raw_input("*** Are you sure? (y/n): ")
				if yes_delete in ('yes', 'y'):
					try:
						self.addressbook.delete(contact[0])
						print "Contact deleted."
					except:
						print "*** Encountered an error while trying to delete, please make sure your input is correct."
					return
			print "No deletions."
		else:
			print "There were no contacts that met your specification. Please generalize your request."

	def do_display(self, line):
		"""
		If no flags are given, displays all contacts in the Address Book.
		If flags are present, then displays only contacts that meet all of the specifications given by flags.
		"""

		if self.addressbook.total == 0:
			print "The addressbook is empty."
			return

		# if no flags, arguments: 
		# Print entire address book
		if line == '':
			print "\n{0}".format(self.addressbook)

		# else find subset of addressbook
		else:
			# Get list of tuples (field, arg)
			fields_args = get_field_and_args_from_input(line)
			if not fields_args: return

			# Search address book for specified contacts
			lists = [] # a list of lists returned by search, each list the result of a search on one field
			for field_arg in fields_args:
				lists.append(self.addressbook.search(field_arg[0], field_arg[1]))

			# Intersect all resulting lists from field queries
			# This is a list of tuples (index, contact)
			search_results = list(set(lists[0]).intersection(*lists[1:]))

			if len(search_results) > 0:
				for result in search_results:
					print "\n{0}".format(result[1])
				print "\n"
			else:
				print "There were no contacts that met your specification. Please generalize/check your request."

	def do_display_mail(self, line):
		"""
		Same as 'display', but displays contact in mailing label format, as opposed to full contact.
		If no flags are given, displays all contacts in the Address Book.
		If flags are present, then displays only contacts that meet all of the specifications given by flags.
		"""

		if self.addressbook.total == 0:
			print "The addressbook is empty."
			return

		# if no flags, arguments: 
		# Print entire address book
		if line == '':
			print "\n{0}".format(self.addressbook.print_all_mailing())

		# else find subset of addressbook
		else:
			# Get list of tuples (field, arg)
			fields_args = get_field_and_args_from_input(line)
			if not fields_args: return

			# Search address book for specified contacts
			lists = [] # a list of lists returned by search, each list the result of a search on one field
			for field_arg in fields_args:
				lists.append(self.addressbook.search(field_arg[0], field_arg[1]))

			# Intersect all resulting lists from field queries
			# This is a list of tuples (index, contact)
			search_results = list(set(lists[0]).intersection(*lists[1:]))

			if len(search_results) > 0:
				for result in search_results:
					print "\n{0}".format(result[1].print_mailing())
				print "\n"
			else:
				print "There were no contacts that met your specification. Please generalize/check your request."
	
	def do_sort(self, line):
		"""
        Sort the address book by the given attributes (flags). The first
        attribute is used and ties are broken using the following attributes in
        the list.
        Defaults to sorting by last name.  Ties are broken by last name.
		"""

		# convert flags to list of attributes.
		flags = line.split()
		attrs = []

		for flag in flags:
			if flag not in VALID_FLAGS:
				print "'{0}' is not a valid flag. Enter \"options\" to view available flag options.\n".format(flag)
				return

			for field in CONTACT_FIELDS:
				if flag == field[2]:
					attrs.append(field[0])

		# Ties are broken by name
		attrs.extend(['lname', 'fname'])

		# Apply sort function
		try:
			self.addressbook.sort(attributes=attrs)
			print "Address book is now sorted.\n"
		except:
			print "*** Encountered an error while trying to sort, please make sure your input is correct."

	def do_open(self, line):
		"""
		Open an address book from file.
		"""

		confirm = raw_input("Are you sure you want to open a new address book? Changes since your last save will be lost. ('open' / 'cancel'): ")
		if confirm != 'open': return		

		file_name = raw_input("Please give the name/path of the file containing the address book you want to open (or leave blank to cancel): ")

		if file_name:
			try:
				self.addressbook = utils.open_addressbook(file_name)
				self.current_book_filename = file_name
				print "Now using address book from file '{0}'".format(file_name)
			except:
				print "*** Encountered an error while trying to open that file. Please make sure you provided a good file name."

	def do_save(self, line):
		"""
		Save an address book as an object to a file, to the file name inferred from current address book.
		"""

		if self.current_book_filename == '': 
			print "This seems to be a new address book."
			return self.do_save_as(line)


		file_name = self.current_book_filename

		confirm = raw_input("Are you sure you want to overwrite the existing file '{0}'? (y/n): ".format(file_name))
		if confirm in ['yes', 'y']:
			try:
				utils.save_addressbook(self.addressbook, file_name)
				print "Successfully saved your address book to the file '{0}'".format(file_name)
			except:
				print "in save"
				print "*** Encountered an error while trying to save. Sorry..."

		else:
			print "Cancelling save."
			return

	def do_save_as(self, line):
		"""
		Save an address book as an object to a file.
		"""

		file_name = raw_input("What do you want to call your file? (or leave blank to cancel): ")
			# "WARNING, I can't tell if you're overwritting a file that already exists, so choose the name carefully: ")

		if not file_name: return

		try:
			with open(file_name):
				overwrite = raw_input("There already seems to be a file with that name. Do you want to overwrite that file? (y/n): ")
		except IOError:
			overwrite = 'yes'

		if overwrite in ['yes', 'y']:
			try:
				utils.save_addressbook(self.addressbook, file_name)
				self.current_book_filename = file_name
				print "Successfully saved your address book to the file '{0}'".format(file_name)
			except:
				print "in save as"
				print "*** Encountered an error while trying to save. Sorry..."
	
	def do_import(self, line):
		"""
		Import a contacts list from a tsv file and add the contacts to the current book.
		The format of the file is as follows:
		
		| Last<tab>Delivery<tab>Second<tab>Recipient<tab>Phone<NL>
		| followed by a list of contacts with the same format.
		"""
		file_name = raw_input("Please give the name/path of the file from which you want to import contacts (or leave blank to cancel): ")

		if file_name:
			confirm = raw_input("Are you sure you want to try to merge the contacts from this file into your current book? (y/n): ")
			if confirm not in ['yes', 'y']: print "Cancelling import."; return
			try:
				self.addressbook.import_contacts(file_name)
				print "Successfully imported contacts from '{0}'".format(file_name)
			except:
				print "*** Encountered an error while trying to import from that file. " + \
					"Please make sure you provided a good file name and that the file matches the format described in 'help import'."
		else:
			print "Cancelling import."

	def do_export(self, line):	
		"""
		Export the contacts of the current AddressBook to a tsv file.
		The format of the file is as follows:
		
		| Last<tab>Delivery<tab>Second<tab>Recipient<tab>Phone<NL>
		| followed by a list of contacts with the same format.
		"""	
		file_name = raw_input("Please give the name of the file to which you want to export contacts (or leave blank to cancel): ")
			
		if file_name:
			try:
				self.addressbook.export_contacts(file_name)
				print "Successfully exported your address book to the file '{0}'".format(file_name)
			except:
				print "*** Encountered an error while trying to export. Sorry..."
		else:
			print "Cancelling export."


	def do_options(self, line):
		"""
		Displays the available keyword commands and flags used by the applet.
		"""
		print OPTIONS_MESSAGE

	def do_quit(self, line):
		"""
		quit the applet
		"""
		confirm = raw_input("Are you sure you want to quit? Changes since your last save will be lost. ('quit' to quit; anything else will cancel): ")
		if confirm != 'quit': 
			return
		else:
			sys.exit()

	def default(self, line):
		"""
		Method called on an input line when the command prefix is not recognized.
		"""
		if line != "":
			print "*** Invalid command.  Type \"options\" to view available command options."

	def emptyline(self):
		"""
		Method called when an empty line is entered in response to the prompt.
		If this method is not overridden, it repeats the last nonempty command entered.
		"""
		pass
コード例 #2
0
ファイル: gui.py プロジェクト: etownsend/CIS422Team1Proj1
class GUI(Frame):
	"""Main class which represents our parent window for the GUI."""
	
	def __init__(self, parent):
		"""Initiates the window, with the appropriate grid layout, buttons,
			Canvas and scroll bar. All class-wide variables defined here."""
		Frame.__init__(self, parent, background="#ffffff")
		self.parent = parent
		
		#Temporary Variables - remember to reset!
		self.searchState = None #Currently displaying search results?
		self.tempvars = []	#Temporary values for add and editing fields
		self.tempID = -1	#TemporaryID number for contact
		self.tempFile = None	#Temporary File for import/export
		self.mL = 0		#Mailing label flag
		
		# Some formatting for the grid layout
		self.grid_rowconfigure(1, weight=1)
		self.grid_columnconfigure(0, weight=2)
		self.grid_columnconfigure(1, weight=0)
		self.grid_columnconfigure(2, weight=0)
		self.grid_columnconfigure(3, weight=0)
		self.grid_columnconfigure(4, weight=7)

		#Logo Column
		logo = PhotoImage(file="logo.gif")
		imgCan = Label(self, image=logo)
		imgCan.image = logo
		imgCan.grid(sticky = N+W, row = 0, column = 0, padx=0)	
		
		# Add Button
		img1 = PhotoImage(file="add.gif")
		addBtn = Button(self, image= img1, command = self.clickAdd)
		addBtn.image = img1
		addBtn.grid(sticky = N+W, row = 0, column = 1, ipadx=5, padx=3, ipady=5, pady=8)
		
		# Edit Button
		img2 = PhotoImage(file="edit.gif")
		editBtn = Button(self, image=img2, command = self.clickEdit)
		editBtn.image = img2
		editBtn.grid(sticky = N+W, row = 0, column = 2, ipadx=5, padx=3, ipady=5, pady=8)

		#Delete Button
		img3 = PhotoImage(file="delete.gif")
		delBtn = Button(self, image=img3, command = self.clickDelete)
		delBtn.image = img3
		delBtn.grid(sticky = N+W, row = 0, column = 3, ipadx=5, padx=3, ipady=5, pady=8)
		
		#Search Box Input
		self.searchBox = Entry(self, relief=SUNKEN, border=2, width=20)
		self.searchBox.grid(sticky = N+E, row = 0, column = 4, padx=2, pady=20)

		#Search Button
		searchBtn = Button(self, text="Search", command = self.clickSearch)
		searchBtn.grid(sticky = N+E, row = 0, column = 5, padx=0, pady=20)
		
		#Return to view the full book Button
		search2Btn = Button(self, text="View Full Book", command = self.update)
		search2Btn.grid(sticky = N+E, row = 0, column = 6, padx=0, pady=20)
		
		#Main addressbook!
		self.addressbook = AddressBook()

		#Used for initial testing
		"""for i in range (0, 5):
			new_contact = Contact()
			setattr(new_contact, 'lname', "Yablok" + str(i))
			self.addressbook.add(new_contact)"""
		
		#Canvas and scrollbar configuration
		self.canvas=Canvas(self, relief=SUNKEN, border=2, bg='#c6c6c6')
		self.vbar=Scrollbar(self, command=self.canvas.yview)
		self.vbar.config(command=self.canvas.yview)
		self.vbar.grid(sticky=NS, columnspan=8, row = 1, column = 8)
		self.canvas.config(width=980,height=700)
		self.canvas.config(yscrollcommand=self.vbar.set)
		self.canvas.grid(sticky=N+E+S+W, columnspan=7, row = 1, column = 0)

		self.fill_book(self.addressbook.contacts)
		self.initUI()


	def initUI(self):
		"""Setup the window and display it.""" 
		self.parent.title("Blue Book")
		self.pack(fill=BOTH, expand=1)


	def get_tagged_box(self, tag):
		"""Search through Canvas objects by tags, return the rectangle with the given tag."""
		id, = self.canvas.find_withtag(tag)
		tags = self.canvas.gettags(tag)
		if 'text' in tags: #if the current item is text
			id = self.canvas.find_withtag(self.canvas.itemcget(id,'text'))
		return id;
 

	def boxClicked(self, event):
		"""Give the rectangle the 'select' tag when clicked, and the highlight properties
			(outline and white background."""
		id = self.get_tagged_box('current')
		tags = self.canvas.gettags(id)
		for t in tags:
			if (t == "select"): #The box is already selected, clicking should deselect
				self.canvas.dtag(id, 'select')
				self.canvas.itemconfigure(id, width=0, fill=BOX_COLOR)
				return
		for en in self.canvas.find_withtag("select"):
			if (en != id): #Cycle through and make sure only one is selected at a time.
				self.canvas.dtag(en, 'select')
				self.canvas.itemconfigure(en, width=0, fill=BOX_COLOR)
		#Select the current box.
		self.canvas.addtag('select', 'withtag', id)
		self.canvas.itemconfigure(id, width=2, outline="#58a4cd", fill=BOX_HIGHLIGHT)


	def hover(self, event):
		"""Binding for when the user hovers over a rectangle. Changes the cursor."""
		id = self.get_tagged_box('current')
		self.config(cursor="pointinghand")


	def no_hover(self,event):
		"""Binding for when the user stops hovering over a rectangle. Changes the cursor."""
		id = self.get_tagged_box('current')
		self.config(cursor="")


	def fill_book(self, subset):
		"""Fill the Canvas with the info from adresses from the subset provided.
			for each entry add a rectangle to the canvas, and the associated address text within
			the rectangle. Bind these boxes to the boxClicked() function."""
		SCROLLAREA_HEIGHT = (BOX_HEIGHT + ROW_PAD)*int(math.ceil(len(subset)/3.0)) + ROW_PAD
		self.canvas.config(scrollregion=(0,0,SCROLLAREA_WIDTH, SCROLLAREA_HEIGHT))
		
		if len(subset) == 0 and self.searchState is None:
			self.canvas.create_text(980/2, 700/2, anchor='c', tags='text', text="The addressbook is currently empty.", )
		elif len(subset) == 0:
			self.canvas.create_text(980/2, 700/2, anchor='c', tags='text', text="No existing entries match your search.", )
		else:
			idn = 0 #ID Number
			r = 0 #row count
			y = COLUMN_PAD
			
			for c in range(0, len(subset)):
				if (idn%3 == 0 and idn != 0): #If in a new row
					#Update row count, x, and y.
					r += 1
					y = COLUMN_PAD+r*(BOX_HEIGHT+COLUMN_PAD)
					x = ROW_PAD
				else :
					x = ROW_PAD+((c%3)*(BOX_WIDTH+ROW_PAD))
					y = COLUMN_PAD+r*(BOX_HEIGHT+COLUMN_PAD)
				
				#If the index number exists in the addressbook
				if (idn < self.addressbook.total):
					#Create rectangle
					id = self.canvas.create_rectangle(x, y, x+BOX_WIDTH, y+BOX_HEIGHT, fill=BOX_COLOR, width=0, tags=('box', idn)) 
					con = ""
					#Generate Text
					for field in CONTACT_FIELDS:
						#Check for subset
						if (self.searchState is None): #default
							entry = self.addressbook.contacts[idn]
						else: #Data has been searched, get from tuple
							entry = subset[idn][1]
							
						if (getattr(entry, field[0], '') != ""):
							con += field[1] + ": " + str(getattr(entry, field[0], '') + "\n")
					#Check for Mailing Label Display
					if (self.mL):
						self.canvas.create_text(x+20, y+20, anchor='nw', text=entry.print_mailing(), tags='text', width=TEXT_WIDTH)
					else:
						self.canvas.create_text(x+20, y+20, anchor='nw', text=con, tags='text', width=TEXT_WIDTH)
				idn+= 1 #Increment ID Number
        	self.canvas.tag_bind('all', '<1>', self.boxClicked) #Check when box is clicked
        	self.canvas.tag_bind('all', '<Any-Enter>', self.hover) #Check when box is hovered over
        	self.canvas.tag_bind('all', '<Any-Leave>', self.no_hover) #Check when box is not hovered over


	def update(self):
		"""Update the canvas once a change has been made to the addressbook,
			to display changes in the addressbook we delete all entries and and
			re-fill the canvas via the fill_book() function."""
		self.searchState = None #reset searchState
		self.searchBox.delete(0, 10000)
		self.canvas.delete("all")
		self.fill_book(self.addressbook.contacts)


	def updateSearchState(self):
		"""Special update function which uses a subset of the full address
			book - the self.searchState which is the current search results."""
		self.canvas.delete("all")
		self.fill_book(self.searchState)
	

	def sort(self, att):
		"""Sort the addresses by last name. This search function only works
			on the full addressbook, not a subset, sort before searching."""
		#If we are not viewing search results, sort entire book
		if (self.addressbook.total == 0):
			return
		attrs = []
		attrs.append(att)
		attrs.extend(['lname', 'fname'])
		self.addressbook.sort(attributes=attrs)
		self.update()


	def clickSearch(self):
		"""General search for all of the fields and all of the contacts in the
			address book. It will look for exact matches in fields. For example search
			will accurately find '123 Easy St' but not just 'Easy St'. (Needs full search
			field."""
		results = []
		val = self.searchBox.get()
		if val == "":
			return
		for field in (CONTACT_FIELDS):
			results += self.addressbook.search(field[0], val)
		self.searchState = results
		self.updateSearchState()


	def clickAdd(self):
		"""Add an entry to the address book. Creates a new top level window with inputs, and refers
			to the addEntry function to add the new contact object."""
		self.tempvars = []
		self.searchState = None
		toplevel = Toplevel()
		toplevel.title("Add an Entry")
		count = 1
		tex = Label(toplevel, text="Please enter your new contact information in the inputs below.")
		tex.grid(row=0, column=0, columnspan=4, sticky=W+E+N+S, padx=10, pady=10)
		for label in (CONTACT_FIELDS):
			va = StringVar()
			l = Label(toplevel, text=label[1])
			l.grid(row=count,column=0, columnspan=1, sticky=W, padx=10, pady=3)
			e = Entry(toplevel, relief=SUNKEN, border=2, width=40, textvariable=va)
			e.grid(row=count,column=1, columnspan=3, sticky=W, padx=10, pady=3)
			self.tempvars.append(e)
			count += 1
		ok = Button(toplevel, text="Submit", command= lambda: self.addEntry(toplevel))
		can = Button(toplevel, text="Cancel", command=toplevel.destroy)
		can.grid(row=count,column=2, sticky=W, padx=0, pady=10)
		ok.grid(row=count,column=1, sticky=W, padx=0, pady=10)
		

	def addEntry(self, top):
		"""Get all of the fields entered by the user in the toplevel window
			Add the new contact object to the address book and update the canvas."""
		new_contact = Contact()
		count = 0
		go = 1
		for field in CONTACT_FIELDS:
			valid = FIELD_VALIDATORS[field[0]]
			if field[0] in REQUIRED_FIELDS:
				data = self.tempvars[count].get()
				if data == "":
					tkMessageBox.showinfo("Required Field Error", field[1] + " is a required field, please enter this info into the input box.")
					go = 0
					break
				if(go): setattr(new_contact, field[0], data)
			else:
				data = self.tempvars[count].get()
				if data != "" and not valid(data)[0]:
					tkMessageBox.showinfo("Input Error", valid(data)[1])
					go = 0
					break
				if(go): setattr(new_contact, field[0], data)
			count += 1
		if(go):
			self.addressbook.add(new_contact)
			self.update()
			top.destroy()		


	def clickEdit(self):
		"""Edit an existing entry in the address book. Creates a new top level window with
			inputs which automatically gets the existing fields from the contact selected by the user.
			It refers to the editEntry() function to make the edits in to the actual contact in the addressbook."""
		self.tempvars = [] #Temp values to get from the entry boxes
		self.tempID = -1 #Temp ID of the Contact being edited
		
		#ERROR: no contact selected by the user
		if (len(self.canvas.find_withtag('select')) == 0 or self.addressbook.total == 0):
			tkMessageBox.showinfo("Error: No Selection", "Please select an address to edit by clicking on its surrounding box. Selected addresses will have a blue outline.")
			return

		toplevel = Toplevel()
		toplevel.title("Edit an Entry")
		
		#Get the global ID of the contact based on
		#based on what is selected.
		id = self.get_tagged_box('select')
		tags=self.canvas.gettags(id)
		for t in tags:
			if t not in ('current','box', 'select'):
				box_name=t
		try:
			i = int(box_name)
		except ValueError:
			tkMessageBox.showinfo("Input error", "The ID of the contact is invalid, please delete and re-add this contact.")
			return
		self.tempID = i; #global ID
		
		#Layout configuration for the new toplevel window
		count = 1
		tex = Label(toplevel, text="Please edit the contact information in the inputs below.")
		tex.grid(row=0, column=0, columnspan=4, sticky=W+E+N+S, padx=10, pady=10)
		for label in (CONTACT_FIELDS): #Get all of the fields
			va = StringVar()
			l = Label(toplevel, text=label[1])
			l.grid(row=count,column=0, columnspan=1, sticky=W, padx=10, pady=3)
			
			#Special case for if searchState is activated.
			if (self.searchState is None):
				s = str(getattr(self.addressbook.contacts[i], label[0], ''))
			else:
				s = str(getattr(self.searchState[i][1], label[0], ''))
			
			va.set(s)
			e = Entry(toplevel, relief=SUNKEN, border=2, width=40, text=s, textvariable=va)
			e.grid(row=count,column=1, columnspan=3, sticky=W, padx=10, pady=3)
			self.tempvars.append(e) # add the values so we can .get() them later
			count += 1
		ok = Button(toplevel, text="Submit", command=lambda: self.editEntry(toplevel))
		can = Button(toplevel, text="Cancel", command=toplevel.destroy)
		can.grid(row=count,column=2, sticky=W, padx=0, pady=10)
		ok.grid(row=count,column=1, sticky=W, padx=0, pady=10)	


	def editEntry(self, top):
		"""Edit the entry id in self.tempID. Update all of the fields, and update the
			Canvas to reflect these changes."""
		if (self.tempID >= 0):
		
			#special case for searchState subset 
			if (self.searchState is None):
				en = self.addressbook.contacts[self.tempID]
			else:
				en = self.searchState[self.tempID][1]
			
			count = 0
			go = 1
			for field in CONTACT_FIELDS:
				valid = FIELD_VALIDATORS[field[0]]
				if field[0] in REQUIRED_FIELDS:
					data = self.tempvars[count].get()
					if data == "":
						tkMessageBox.showinfo("Required Field Error", field[1] + " is a required field, please enter this info into the input box.")
						go = 0
						break
					if(go): setattr(en, field[0], data)
				else:
					data = self.tempvars[count].get()
					if data != "" and not valid(data)[0]:
						tkMessageBox.showinfo("Input Error", valid(data)[1])
						go = 0
						break
					if(go): setattr(en, field[0], data)
				count += 1
			if (go):
				if (self.searchState is None):
					self.update()
				else:
					self.updateSearchState()
				top.destroy()
			
		else: 
			tkMessageBox.showinfo("Input error", "The ID of the contact is invalid, please delete and re-add this contact")
			return
	

	def clickDelete(self):
		"""Delete a contact from the address book, prompt a confirmation box which
			reviews the contact information, and proceeds to delete the entry from
			the addressbook, and update the Canvas"""
		self.tempvars = [] #Temp values to get from the entry boxes
		self.tempID = -1 #Temp ID of the Contact being edited

		#ERROR: no contact selected by the user
		if (len(self.canvas.find_withtag('select')) == 0 or self.addressbook.total == 0):
			tkMessageBox.showinfo("Error: No Selection", "Please select an address to delete by clicking on its surrounding box. Selected addresses will have a blue outline.")
			return
		
		#Get the global id of the selected contacts
		#through the tags options
		id = self.get_tagged_box('select')
		tags = self.canvas.gettags(id)
		for t in tags:
			if t not in ('current','box', 'select'):
				box_name=t
		try:
			i = int(box_name)
		except ValueError:
			tkMessageBox.showinfo("Input error", "Something was invalid")
			return
		self.tempID = i; #Gloabl ID

		con = ""
		for field in CONTACT_FIELDS:
			#Special case for  searchState subset
			if (self.searchState is None):
				con += field[1] + ": " + getattr(self.addressbook.contacts[i], field[0], '') + "\n"
			else:
				con += field[1] + ": " + str(getattr(self.searchState[i][1], field[0], '')) + "\n"
		#Confirmation box
		if tkMessageBox.askyesno("Delete an Entry", "Are you sure you want to delete this entry?\n" + con):
			if (self.searchState is None):
				self.addressbook.delete(self.tempID)
			else:
				self.addressbook.delete(self.searchState[self.tempID][0])
				self.searchState.pop(self.tempID)
			
			#Update the Canvas
			if (self.searchState is None):
				self.update()
			else:
				self.updateSearchState()


	def mailLabels(self):
		"""View the contact information in a mailing label format.
			Update the canvas to display new format"""
		self.mL = 1
		if (self.searchState is None):
			self.update()
		else:
			self.updateSearchState()


	def noMailLabels(self):
		"""View the contact information in the regular format.
			Update the canvas to display new format"""
		self.mL = 0
		if (self.searchState is None):
			self.update()
		else:
			self.updateSearchState()


	def new(self):
		"""Create a new blank address book, ask to save before creating new book.
			Update the Canvas with the new book."""
		self.tempFile = None
		if tkMessageBox.askyesno("New Address Book", "Do you want to save your current file first?"):
			self.save()
		self.addressbook.contacts = []
		self.addressbook.total = 0
		self.update()
	

	def open(self):
		"""Open a new address book and prompt the user to save changes."""
		if self.addressbook.contacts != []: 
			if tkMessageBox.askyesno("Open Address Book", "Do you want to save your current file first?"):
				self.save()
		dlg = tkFileDialog.Open(self)
		fl = dlg.show()

		if fl != '':
			self.addressbook = utils.open_addressbook(fl)
			self.tempFile = fl
			self.update()


	def close(self):
		"""Close an address book and prompt the user to save changes. If no address book is open,
		quit the program"""
		if self.addressbook.contacts == []: 
			root.quit()
			return
		if tkMessageBox.askyesno("Close Address Book", "Do you want to save your current file first?"):
			self.save()
		self.tempFile = None
		self.addressbook.contacts = []
		self.addressbook.total = 0
		self.update()


	def imp(self):
		"""Import a new address from a .tsv file. Prompt the user to select a file,
			update the Canvas with the imported addressbook"""	
		ftypes = [('TSV files', '*.tsv'), ('All files', '*')]
		dlg = tkFileDialog.Open(self, filetypes = ftypes)
		fl = dlg.show()
		if fl != '':
			self.addressbook.import_contacts(fl)
			if tkMessageBox.askyesno("Import Addresses", 
				"Do you want to merge addresses with the same first and last name?"):
				self.addressbook.merge_addressbook()
			self.update()
	

	def export(self):
		"""Export the current address book to a .tsv file. Prompt the user to select a file,
		update the Canvas with the imported addressbook"""
		self.tempFile = None
		f = tkFileDialog.asksaveasfile(mode='w', defaultextension=".tsv")
		if f is None:
			return
		self.tempFile = f.name
		self.addressbook.export_contacts(f.name)


	def save(self):
		"""Save an address book to the disk."""
		if (self.tempFile is None):
		    f = tkFileDialog.asksaveasfile()
		    if f != None and self.addressbook != None:
		    	utils.save_addressbook(self.addressbook, f.name)
		    	self.tempFile = f.name
		else:
			utils.save_addressbook(self.addressbook, self.tempFile)


	def save_as(self):
		"""Save an address book to the disk prompting for a file name."""
		f = tkFileDialog.asksaveasfile()
		if f != None and self.addressbook != None:
		    utils.save_addressbook(self.addressbook, f.name)


	def quit(self):
		"""Quit the program but make sure to save changes first."""
		self.close()
		root.quit()