1

It's showing this error on calculating expressions with parenthesis like-> 86*4(3+5)88 :

TypeError: 'int' object is not callable

when I don't use the calculator buttons and type using my keyboard buttons it doesn't show any error but on pressing equal it gives wrong answer.

Please check my code and point out the error.

from tkinter import *

cal = Tk()
cal.title("Smart Calculator")

txtDisplay = Entry(cal, font=('arial', 20, 'bold'), bd=30, insertwidth=4, 
bg="powder blue", justify='right')

class Calculator:

    def __init__(self):
        self.result = 0
        self.current = 0
        self.operator = ""

    def btnClick(self, num):
            self.operator = self.operator + str(num)
            txtDisplay.delete(0, END)
            txtDisplay.insert(0, self.operator)
            self.expOutput(self.operator)

    def expOutput(self, operator):
        try:
            self.result = str(eval(operator))
            txtDisplay.delete(0, END)
            txtDisplay.insert(0, string=self.operator + "=" + self.result)
            self.current = 0
        except SyntaxError:
            txtDisplay.delete(0, END)
            txtDisplay.insert(0, self.operator)

    def oprtrClick(self, op):
        if self.current is 0:
            self.current = 1
            self.operator = self.operator + op
            txtDisplay.delete(0, END)
            txtDisplay.insert(0, string=self.operator)
        else:
            self.operator = self.operator + op
            self.expOutput(self.operator)

    def equals(self):
        self.operator = self.result
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, string=self.operator)

    def clear(self):
        self.__init__()
        txtDisplay.delete(0, END)

    def delete(self):
        self.operator = self.operator[: -1]
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, string=self.operator)

smartCal = Calculator()

btn0 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 
'bold'), text="0",bg="powder blue", command=lambda: smartCal.btnClick(0))


btn1 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 
'bold'), text="1",
          bg="powder blue", command=lambda: smartCal.btnClick(1))

btn2 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 
'bold'), text="2",bg="powder blue", command=lambda: smartCal.btnClick(2))


btn3 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 
'bold'), text="3",
          bg="powder blue", command=lambda: smartCal.btnClick(3))

btn4 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="4",
          bg="powder blue", command=lambda: smartCal.btnClick(4))

btn5 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="5",
          bg="powder blue", command=lambda: smartCal.btnClick(5))

btn6 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="6",
          bg="powder blue", command=lambda: smartCal.btnClick(6))

btn7 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="7",
          bg="powder blue", command=lambda: smartCal.btnClick(7))

btn8 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="8",
          bg="powder blue", command=lambda: smartCal.btnClick(8))

btn9 = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="9",
          bg="powder blue", command=lambda: smartCal.btnClick(9))

btnDecimal = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text=".",
               bg="powder blue", command=lambda: smartCal.btnClick("."))

btnLeftParen = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="(",
               bg="powder blue", command=lambda: smartCal.btnClick("("))

btnRightParen = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text=")",
               bg="powder blue", command=lambda: smartCal.btnClick(")"))

Add_btn = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="+",
             bg="powder blue", command=lambda: smartCal.oprtrClick("+"))

Sub_btn = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="-",
             bg="powder blue", command=lambda: smartCal.oprtrClick("-"))

Mul_btn = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="*",
             bg="powder blue", command=lambda: smartCal.oprtrClick("*"))

Div_btn = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="/",
             bg="powder blue", command=lambda: smartCal.oprtrClick("/"))

btnEquals = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="=",
               bg="powder blue", command=smartCal.equals)

btnClear = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="C",
              bg="powder blue", command=smartCal.clear)

btnBackspace = Button(cal, padx=16, pady=16, bd=8, fg="black", font=('arial', 20, 'bold'), text="⌫",
              bg="powder blue", command=smartCal.delete)

# ~*~*~Positioning~*~*~

txtDisplay.grid(columnspan=4)
# =========ROW1================== #
btn7.grid(row=1, column=0)
btn8.grid(row=1, column=1)
btn9.grid(row=1, column=2)
Add_btn.grid(row=1, column=3)
# =========ROW2================== #
btn4.grid(row=2, column=0)
btn5.grid(row=2, column=1)
btn6.grid(row=2, column=2)
Sub_btn.grid(row=2, column=3)
# =========ROW3================== #
btn1.grid(row=3, column=0)
btn2.grid(row=3, column=1)
btn3.grid(row=3, column=2)
Mul_btn.grid(row=3, column=3)
# =========ROW4================== #
btn0.grid(row=4, column=0)
btnClear.grid(row=4, column=1)
btnEquals.grid(row=4, column=2)
Div_btn.grid(row=4, column=3)
# =========ROW5================== #
btnDecimal.grid(row=5, column=0)
btnLeftParen.grid(row=5, column=1)
btnRightParen.grid(row=5, column=2)
btnBackspace.grid(row=5, column=3)

cal.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
preetika mondal
  • 323
  • 2
  • 12
  • 6
    I haven't looked closely at your code. But because you're using `eval`, you need to write expressions using proper Python syntax. `86*4(3+5)88` is a syntax error, you need `86*4*(3+5)*88`. Note that there are security issues with using `eval`. There are better ways to do this. – PM 2Ring May 01 '18 at 11:27
  • Can you tell me what is the alternative way to do this so that I don't get this error as well as avoid security issues. I am a beginner in programming so any help would be much appreciated. – preetika mondal May 01 '18 at 11:31
  • If this is homework, then maybe your teacher expects you to use `eval`. Doing it the proper way is a bit harder, since you need to write code that can parse arithmetic expressions, or use a 3rd-party library like [pyparsing](https://sourceforge.net/projects/pyparsing/) to do the parsing for you. – PM 2Ring May 01 '18 at 11:46
  • `eval` won't blow up your computer. :) But it does mean that someone using your calculator program can evaluate arbitrary expressions, and that means they can run any Python code, and execute any command on the system, including formatting the hard drive. If they're running the calculator on their own computer, that's not really an issue, since they can do that sort of thing anyway. – PM 2Ring May 01 '18 at 11:50
  • 1
    The error message tells a lot more. Please include it in full. – Jongware May 01 '18 at 12:28
  • 2
    This is way too much code. Please try to reduce it down to a [mcve]. – Bryan Oakley May 01 '18 at 13:22

2 Answers2

1

In your method expOutput, you should add the missing * sign to the operator string before and after the opening or closing brackets.

This needs to be done before calling eval(operator) by calling a parsing function, say add_missing_mult.

A naive implementation of the parsing function could look like:

def add_missing_mult(s):

    splitted = s.split('(')
    for i, sub in enumerate(splitted[:-1]):
        if sub[-1].isdigit() or sub[-1] == '.':
            splitted[i] += "*"
    s = '('.join(splitted)

    splitted = s.split(')')
    for i, sub in enumerate(splitted[1:]):
        if sub[0].isdigit():
            splitted[i+1] = "*" + splitted[i+1]
    return ')'.join(splitted)

add_missing_mult('86*4(3+5)88')

>>>'86*4*(3+5)*88'

and then in expOutput:

operator = add_missing_mult(operator)
try:
    self.result = str(eval(operator))
    txtDisplay.delete(0, END)
    ...

But on a real project, you would be looking at using a regular expression like in this question

Jacques Gaudin
  • 15,779
  • 10
  • 54
  • 75
1

I solved the problem without using a parsing function. I used two new variables - isButton and isRightParen and two new functions - leftParenthesesClick() and rightParenthesesClick(). isButton keeps track if the last pressed button was a number(0-9). when user presses left parentheses then first it checks if isRightParen is 1 then it includes the * sign before the left parentheses, then if isButton is 1 then also * is included , otherwise left parentheses is printed as it is.

Please check my code and tell me if this way is okay, is there some mistake or there is a better and shorter way to do this. @Jacques Gaudin @PM 2Ring I will later try this out with parsing function as well as suggested. :)

def __init__(self):
    self.var1 = ""
    self.var2 = ""
    self.output = 0
    self.current = 0
    self.operator = ""
    self.isButton = 0
    self.isRightParen = 0

def btnClick(self, num):
    if self.isRightParen is 1:
        self.operator = self.operator + "*" + str(num)
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, self.operator)
        self.expOutput(self.operator)
        self.isButton = 1
        self.isRightParen = 0

    elif self.isRightParen is 0:
        self.operator = self.operator + str(num)
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, self.operator)
        self.expOutput(self.operator)
        self.isButton = 1
        self.isRightParen = 0

def leftParenthesesClick(self):

    if self.isRightParen is 1:
        self.operator = self.operator + "*" + "("
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, self.operator)
        self.isRightParen = 0

    elif self.isButton is 0:
        self.operator = self.operator + "("
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, self.operator)
        self.isRightParen = 0
    elif self.isButton is 1:
        self.operator = self.operator + "*" + "("
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, self.operator)
        self.isRightParen = 0

def rightParenthesesClick(self):
    self.operator = self.operator + ")"
    txtDisplay.delete(0, END)
    txtDisplay.insert(0, self.operator)
    self.expOutput(self.operator)
    self.isRightParen = 1

def expOutput(self, operator):
    try:
        self.output = str(eval(operator))
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, string=self.operator + "=" + self.output)
        self.current = 0
    except SyntaxError:
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, self.operator)


def oprtrClick(self, op):
    if self.current is 0:
        self.current = 1
        self.operator = self.operator + op
        txtDisplay.delete(0, END)
        txtDisplay.insert(0, string=self.operator)
        self.isButton = 0
        self.isRightParen = 0
    else:
        self.operator = self.operator + op
        self.isButton = 0
        self.isRightParen = 0
        self.expOutput(self.operator)
Jacques Gaudin
  • 15,779
  • 10
  • 54
  • 75
preetika mondal
  • 323
  • 2
  • 12