I wrote a small program that generates a GUI for a grocery list (any list honestly). The GUI has the capacity to open a file, save a file, and save then exit the GUI from a file menu. The grocery items are added one at a time to a listbox using a popup window. The added items can be deleted as well. The functionality is pretty minimal, but I was hoping for some feedback with regards to structure and common practice. Any advice would be appreciated.
import tkinter as tk
from tkinter.filedialog import asksaveasfilename,askopenfilename
class NewForm(tk.Toplevel):
keys = ['Name','Price']
def __init__(self):
tk.Toplevel.__init__(self)
self.resizable(False,False)
self.title('Add item')
self.grab_set()
self.labels={}
self.variables={}
for key in NewForm.keys:
self.labels[key] = ''
self.variables[key] = tk.StringVar()
for label in self.labels.keys():
frm = tk.Frame(self,relief=tk.RAISED,bd = 2)
frm.pack(side=tk.TOP,expand=tk.YES,fill=tk.X)
tk.Label(frm,text=label).pack(side=tk.LEFT)
ent = tk.Entry(frm,textvar = self.variables[label])
ent.pack(side=tk.RIGHT)
tk.Button(self,text='Submit',command=self.Submit).pack(side=tk.TOP)
self.wait_window()
def Submit(self):
for variable_value in self.variables.values():
if len(variable_value.get())==0:
tk.messagebox.showinfo('Warning','The fields are incomplete!')
return
self.destroy()
class Main(tk.Frame):
def __init__(self,parent = None):
tk.Frame.__init__(self,parent,relief = tk.RIDGE,bd=4,
width = 400,height = 300)
self.master.title('Grocery List')
self.pack(expand=tk.YES,fill=tk.BOTH)
parent.minsize(width = 300,height=200)
parent.maxsize(width = 500,height =400)
self.add_menu()
self.add_info()
self.add_buttons()
def add_menu(self):
self.menu = tk.Menu(self.master)
self.master.config(menu=self.menu)
file = tk.Menu(self.menu,tearoff=False)
file.add_command(label='Open',command = self.file_open)
file.add_command(label='Save',command = self.file_save)
file.add_command(label='Save and Exit',command = self.file_save_exit)
self.menu.add_cascade(label='File',menu = file)
def add_info(self):
info_frame = tk.Frame(self,relief=tk.SUNKEN,bd=2)
info_frame.pack(side=tk.TOP,expand=tk.YES,fill=tk.BOTH)
self.sbar = tk.Scrollbar(info_frame)
self.lbox = tk.Listbox(info_frame,relief = tk.SUNKEN,selectmode='multiple')
self.sbar.config(command = self.lbox.yview)
self.lbox.config(yscrollcommand = self.sbar.set)
self.sbar.pack(side=tk.RIGHT,fill=tk.Y)
self.lbox.pack(side=tk.TOP,expand = tk.YES,fill=tk.BOTH)
def add_buttons(self):
frame = tk.Frame(self,relief = tk.RAISED,bd=2)
frame.pack(side=tk.TOP,expand=tk.YES,fill=tk.BOTH)
tk.Button(frame,text='Add grocery item',
command = self.add_item).pack(side=tk.LEFT,
expand=tk.YES)
tk.Button(frame,text='Delete grocery item',
command = self.delete_item).pack(side=tk.LEFT,
expand=tk.YES)
def add_item(self):
self.popup = NewForm()
self.update_list()
def delete_item(self):
idx_to_delete = self.lbox.curselection()
for idx in idx_to_delete[::-1]:
self.lbox.delete(idx)
def update_list(self):
self.lbox.insert(0,self.popup.variables['Name'].get()+', '+self.popup.variables['Price'].get()+'\n')
def file_open(self):
file = askopenfilename()
with open(file,'r') as f:
contents = f.readlines()
for line in contents:
self.lbox.insert(0,line)
def file_save(self):
filename = asksaveasfilename()
if filename:
with open(filename,'w') as f:
for line in self.lbox.get(0,tk.END):
f.write(line)
def file_save_exit(self):
self.file_save()
self.master.destroy()
Main(tk.Tk()).mainloop()
1 Answer 1
Simpler
These 2 lines:
contents = f.readlines()
for line in contents:
can be combined into 1:
for line in f.readlines():
This eliminates the intermediate contents
variable.
Documentation
The PEP 8 style guide recommends adding docstrings for classes and functions.
It is also helpful to add a docstring to summarize the code at the top:
"""
Generate a grocery list and store it in a file.
"""
Layout
There is inconsistent whitespace around operators and inconsistent usage of blank lines. The black program can be used to automatically reformat the code for consistency.
Comments
It would be worth adding a comment for this line:
for idx in idx_to_delete[::-1]:
It is not obvious what type of variable idx_to_delete
is
or what [::-1]
is doing.
Explore related questions
See similar questions with these tags.