This is my first project using the tkinter library. It's a basic calculator that supports the four basic arithmetic operations. It also supports inputting digits and symbols via the keyboard.
I'm looking for any sort of feedback. Are my comments sufficient for understanding the algorithm? Are there redundant comments or code? Is the program written well overall?
import tkinter as tk
from functools import partial
def insert_value(value):
"""Insert value into input entry widget
if value is an operator, add spacing on each side of it
value: value to add to widget"""
global acceptable_inputs, interchangeable_operators
value = convert_to_int(value)
cur_text = str(result_lbl["text"])
if cur_text == "Error":
result_lbl["text"] = f"{value}"
elif isinstance(value, int) or len(cur_text) == 0 or value in ("(", ")", "."): #don't add additional spacing
result_lbl["text"] = f"{cur_text}{value}"
else: #add spacing in between provided value (only if value is an arithmetic operator)
for s in interchangeable_operators:
if s[1] in value:
value = replace_operators(value, s[1], s[0]) #change typed "/" to "÷" and/or "*" to "×"
result_lbl["text"] = f"{cur_text} {value} "
def convert_to_int(d):
"""Convert data to integer; returns provided data as integer if the data can be converted; else returns unmodified data
d: data to convert"""
try:
return int(d)
except ValueError:
return d
def insert_in_string(s, index, value):
"""Insert value into string at index
s: string
index: index of insertion
value: value to insert into string"""
return s[:index] + value + s[index:]
def adjust_adjacent_parentheses_int(s):
"""Check if provided string contains adjacent integer and parenthesis, if it does, insert an astrik in between them; returns modified string
s: string"""
#find indices where astrik should be inserted
indices = []
for i in range(len(s)):
if isinstance(convert_to_int(s[i]), int):
if i+1 < len(s) and s[i+1] == "(": #if ( is immediately after an integer, plan to insert astrik
indices.append(i+1)
elif i-1 >= 0 and s[i-1] == ")": #if ) is immediately before an integer, plan to insert astrik
indices.append(i)
#insert astriks in their respective indices
if indices:
for i in range(len(indices)):
s = insert_in_string(s, indices[i]+i, "*")
return s
def replace_operators(s, symbol, operator):
"""Change symbol in provided string to its corresponding operator; returns modified string
s: string
symbol: symbol to change
operator: operator to replace with symbol"""
return s.replace(symbol, operator)
def calculate_result():
"""Print calculated result into entry widget"""
global parentheses, interchangeable_operators
result = str(result_lbl["text"])
if not result:
return
else:
result_lbl["text"] = ""
#modify string so it can be appropriately evaluated by the eval() function
for s in interchangeable_operators:
if s[0] in result:
result = replace_operators(result, s[0], s[1])
#insert astriks in between parentheses and integers
if any(c in parentheses for c in result):
result = adjust_adjacent_parentheses_int(result)
try:
result = eval(result)
except (SyntaxError, NameError) as e:
result_lbl["text"] = "Error"
return
#convert evaluation to integer form if the float only has trailing 0s
if isinstance(result, float) and result-int(result) == 0:
result = convert_to_int(result)
result_lbl["text"] = result
def key_bindings(key):
"""Controller for executing corresponding functions related to key input. This function is binded to <Key> from tkinter
key: key object from tkinter binding"""
global acceptable_inputs
if key.char == "\r": #enter key was pressed, calculate expression currently in input
calculate_result()
elif key.keysym == "BackSpace": #delete latest character
cur_text = str(result_lbl["text"])
result_lbl["text"] = cur_text[:len(cur_text)-1]
elif key.char in acceptable_inputs: #insert character from key press into input bar if character is eligible
insert_value(key.char)
def clear_result():
"""Clear all characters contain within the result entry widget"""
result_lbl["text"] = ""
#initialize variables
interchangeable_operators = (("÷", "/"), ("×", "*"))
acceptable_inputs = ("+", "-", "/", "*", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "(", ")")
parentheses = ("(", ")")
#initialize window
window = tk.Tk()
window.title("Basic Calculator")
window.geometry("320x180")
#result frame/entry setup
result_frm = tk.Frame(master=window)
result_frm.pack(fill=tk.BOTH, expand=True)
result_lbl = tk.Label(text="", master=result_frm, fg="black", relief=tk.FLAT)
result_lbl.pack(expand=True)
#setup primary button grid/frame
main_btn_frm = tk.Frame(master=window, bg="#CAC8AE")
main_btn_frm.pack(fill=tk.BOTH, expand=True)
main_btn_frm.rowconfigure([0, 1, 2, 3], weight=1)
main_btn_frm.rowconfigure(4, weight=2)
main_btn_frm.columnconfigure([0, 1, 2, 3], weight=1)
#initialize all buttons inside of grid
btn_padding = 1
border_width = 3
tk.Button(master=main_btn_frm, text="1", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "1")).grid(row=0, column=0, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="2", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "2")).grid(row=0, column=1, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="3", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "3")).grid(row=0, column=2, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="+", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "+")).grid(row=0, column=3, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="4", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "4")).grid(row=1, column=0, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="5", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "5")).grid(row=1, column=1, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="6", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "6")).grid(row=1, column=2, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="-", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "-")).grid(row=1, column=3, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="7", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "7")).grid(row=2, column=0, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="8", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "8")).grid(row=2, column=1, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="9", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "9")).grid(row=2, column=2, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="×", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "×")).grid(row=2, column=3, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="0", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "0")).grid(row=3, column=0, padx=btn_padding, pady=btn_padding, sticky="nsew", columnspan=2)
tk.Button(master=main_btn_frm, text=".", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, ".")).grid(row=3, column=2, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="÷", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "÷")).grid(row=3, column=3, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="(", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, "(")).grid(row=4, column=0, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="C", fg="black", bg="red", relief=tk.RAISED, bd=border_width, command=clear_result).grid(row=4, column=1, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text="=", fg="black", bg="green", relief=tk.RAISED, bd=border_width, command=calculate_result).grid(row=4, column=2, padx=btn_padding, pady=btn_padding, sticky="nsew")
tk.Button(master=main_btn_frm, text=")", fg="black", bg="orange", relief=tk.RAISED, bd=border_width, command=partial(insert_value, ")")).grid(row=4, column=3, padx=btn_padding, pady=btn_padding, sticky="nsew")
#binding(s)
window.bind("<Key>", key_bindings)
#main loop
window.mainloop()