I'm writing a scientific calculator as part of a project, but when I click the "=" sign, 6 functions (coth, acot, sec, sech, asec and acsc) display an error message in the calculation field instead of the result. The rest displays the correct result in radians. I can only use the math module, so I tried to create equivalents of the given functions using inversions of other running ones. Unfortunately, this solution only worked in my case 1/3 of the time. I am attaching a code snippet containing trigonometry, numbers and basic mathematical operations.
import tkinter as tk
import math
class ScientificCalculator:
def __init__(self, root):
self.root = root
root.title("Kalkulator Naukowy")
self.expression = ""
self.max_history_lines = 10
self.memory = 0
self.display = tk.Entry(root, width=40, font=('Arial', 20), borderwidth=5)
self.display.grid(row=0, column=0, columnspan=7, pady=(20, 10))
buttons = [
"7", "8", "9", "+", "sin(", "sinh(", "asin(",
"4", "5", "6", "-", "cos(", "cosh(", "acos(",
"1", "2", "3", "*", "tan(", "tanh(", "atan(",
"0", ".", "(", ")", "=", "AC", "C", "cot(",
"coth(", "acot(", "sec(", "sech(", "asec(", "csc(", "csch(",
"acsc("
]
row_val = 1
col_val = 0
for button in buttons:
if button == "=":
tk.Button(root, text=button, padx=30, pady=20, command=lambda b=button: self.button_click(b), font=('Arial', 14, 'bold'), bg='#082e79', fg='white').grid(row=row_val, column=col_val, columnspan=2, sticky='nsew')
col_val += 2
elif button in ["C", "AC"]:
tk.Button(root, text=button, padx=30, pady=20, command=lambda b=button: self.button_click(b), font=('Arial', 14, 'bold'), bg='#082e79', fg='white').grid(row=row_val, column=col_val, columnspan=2, sticky='nsew')
col_val += 2
else:
tk.Button(root, text=button, padx=20, pady=20, command=lambda b=button: self.button_click(b), font=('Arial', 14, 'bold'), bg='#666666', fg='white').grid(row=row_val, column=col_val, padx=1, pady=1, sticky='nsew')
col_val += 1
if col_val > 6:
col_val = 0
row_val += 1
self.history_text = tk.Text(root, height=10, width=40)
self.history_text.grid(row=10, column=0, columnspan=7, padx=2, pady=2, sticky='nsew')
self.history = []
def button_click(self, button):
if button == "=":
try:
self.expression = self.expression.replace("cot(", "1/math.tan(")
self.expression = self.expression.replace("coth(", "1/math.tanh(")
self.expression = self.expression.replace("acot(", "1/math.atan(")
self.expression = self.expression.replace("sec(", "1/math.cos(")
self.expression = self.expression.replace("sech(", "1/math.cosh(")
self.expression = self.expression.replace("asec(", "1/math.acos(")
self.expression = self.expression.replace("csc(", "1/math.sin(")
self.expression = self.expression.replace("csch(", "1/math.sinh(")
self.expression = self.expression.replace("acsc(", "1/math.asin(")
result = str(eval(self.expression))
history_expression = self.expression
self.history.insert(0, history_expression + " = " + result)
self.display.delete(0, tk.END)
self.display.insert(0, result)
self.update_history()
self.expression = result
except Exception:
self.display.delete(0, tk.END)
self.display.insert(0, "Błąd")
elif button == "C":
self.expression = ""
self.display.delete(0, tk.END)
elif button == "AC":
self.memory = 0
self.expression = ""
self.display.delete(0, tk.END)
self.history = []
self.update_history()
else:
if button in ["sin(", "cos(", "tan(", "sinh(", "cosh(", "tanh(", "asin(", "acos(", "atan("]:
self.expression += "math." + button
else:
self.expression += button
self.display.insert(tk.END, button)
def update_history(self):
if len(self.history) > self.max_history_lines:
self.history = self.history[:self.max_history_lines]
self.history_text.delete(1.0, tk.END)
for item in self.history:
self.history_text.insert(tk.END, item + "\n")
if __name__ == "__main__":
root = tk.Tk()
calc = ScientificCalculator(root)
root.geometry("750x730")
root.configure(bg="#bfbfbf")
root.mainloop()
The first steps to solving this issue are printing a more detailed error message.
Catching the Exception
tells us there is a problem, not what problem is happening.
We can know that exactly by making a modification like so:
# ...
self.expression = result
except Exception as e:
self.display.delete(0, tk.END)
self.display.insert(0, "Błąd")
print("Exception:", e)
# ...
Adding this, and trying to evaluate acsc(-0.5), I get an error message:
Exception: name 'a1' is not defined
That name, a1
is not present anywhere in the given snippet, so let's dig deeper:
I add a line inside button_click
that prints the exact expression that's being evaluated:
# ...
self.expression = self.expression.replace("acsc(", "1/math.asin(")
print("Expression before eval:",self.expression)
result = str(eval(self.expression))
# ...
Running the code again with the same input, the problem becomes clear immediately:
Expression before eval: a1/math.sin(-0.5)
Exception: name 'a1' is not defined
acsc(-0.5)
should have become 1/math.asin(-0.5)
. Instead, it became a1/math.sin(-0.5)
. This is the hint we need to finally solve the problem. Let's look at the part of the code that is supposed to do the replacement:
# ...
self.expression = self.expression.replace("csc(", "1/math.sin(")
self.expression = self.expression.replace("csch(", "1/math.sinh(")
self.expression = self.expression.replace("acsc(", "1/math.asin(")
Python executes things from top to bottom. The first replacement happens before the last one.
Thus, the csc(-0.5)
part from acsc(-0.5)
gets substituted, and the rest of the replacements don't do anything.
We can finally solve the problem by changing the order.
self.expression = self.expression.replace("acot(", "1/math.atan(")
self.expression = self.expression.replace("cot(", "1/math.tan(")
self.expression = self.expression.replace("coth(", "1/math.tanh(")
self.expression = self.expression.replace("asec(", "1/math.acos(")
self.expression = self.expression.replace("sec(", "1/math.cos(")
self.expression = self.expression.replace("sech(", "1/math.cosh(")
self.expression = self.expression.replace("acsc(", "1/math.asin(")
self.expression = self.expression.replace("csc(", "1/math.sin(")
self.expression = self.expression.replace("csch(", "1/math.sinh(")
Making this change, when I enter acsc(-0.5), into the new program, I get the value as -1.9098593171027438
which is the correct and expected output.
I hope this was helpful for you to understand about the issue you faced, and how you can approach issues in the future.