How can I improve the code? Specifically, is there any way that I can move the code from App.handle into separate functions?
from tkinter import *
import operator
# tuple of buttons in the calculator
# string = command
# function = operation
# number = number input
BUTTONS = (
(('E', 'exit'), ('<-', 'backspace'), ('CE', 'clear'), ('/', operator.truediv)),
(('7', 7), ('8', 8), ('9', 9), ('*', operator.mul)),
(('4', 4), ('5', 5), ('6', 6), ('-', operator.sub)),
(('1', 1), ('2', 2), ('3', 3), ('+', operator.add)),
(('.', 'decimal'), ('0', 0), ('^', operator.pow), ('=', 'evaluate'))
)
# transpose the list
BUTTONS = zip(*BUTTONS)
def get_font(size):
"""
Return a font tuple
"""
return ('Verdana', size)
class App(Frame):
def __init__(self, parent):
super().__init__(parent)
self.pack()
self.top_bar = Frame(self)
self.top_bar.pack(fill=X, side=TOP, pady=20)
self.big_num = Label(self.top_bar, text='', font=get_font(32))
self.big_num.pack(fill=BOTH, expand=True)
self.reset_calc()
self.button_container = Frame()
self.button_container.pack(side=BOTTOM, expand=True, fill=BOTH)
for column in BUTTONS:
frame = Frame(self.button_container)
frame.pack(fill=BOTH, expand=True, side=LEFT)
for item in column:
button = Button(frame, text=item[0], font=get_font(11),
command=lambda x=item[1]: self.handle(x))
button.pack(fill=BOTH, expand=True, side=TOP)
def reset_calc(self):
"""
Reset everything.
"""
self.operator_function = None
self.first_number = None
self.set_text('')
self.displaying_solution = False
# 3 functions to modify calculator bar text
def get_text(self):
return self.big_num.cget('text')
def set_text(self, text):
self.big_num.config(text=text)
def append_text(self, text):
self.set_text(self.get_text() + text)
def handle(self, var):
"""
Handle button clicks.
var = string, number, or function that is bound to the button.
"""
if isinstance(var, str):
if var == 'exit':
root.quit()
elif var == 'clear':
self.reset_calc()
elif var == 'decimal':
if self.displaying_solution:
self.reset_calc()
self.append_text('.')
elif var == 'backspace':
self.set_text(self.get_text()[:-1])
elif var == 'evaluate':
if self.operator_function is not None and \
(not self.displaying_solution) and \
self.first_number is not None:
text = self.get_text()
try:
num = float(text)
except ValueError:
pass
else:
solution = self.operator_function(self.first_number,
num)
if int(solution) == solution:
solution = int(solution)
else:
solution = round(solution, 9)
self.set_text(str(solution))
self.displaying_solution = True
elif isinstance(var, int):
if self.displaying_solution:
self.reset_calc()
self.append_text(str(var))
elif hasattr(var, '__call__'):
# if var is a function
if not self.displaying_solution:
self.operator_function = var
text = self.get_text()
try:
num = float(text)
except ValueError:
pass
else:
self.first_number = num
self.set_text('')
def main():
root = Tk()
root.geometry('400x400')
root.title('Calculator')
app = App(root)
app.mainloop()
if __name__ == '__main__':
main()
E
. You'll most probably get aNameError
. I don't have time to write an answer, so I'll let somebody else to talk about these cases \$\endgroup\$root
is not global, that means that pressingE
button results in an error and the app doesn't quit. \$\endgroup\$