Welcome to my Blog

Python Calculator

February 15, 2020 | 19 Minute Read

A simple way to implement a calculator, using Python!

Repository link

To start with, we should set up a structure to organize our code, although a simple calculator can be a lot.

calculator

  • app #folder
    • src #folder
      • app_calculator.py #file
    • main.py #file
  • calculator.py #file

In calculator.py the code will look like this


from app.main import Main

if __name__ == "__main__":
    Main()

Now in our main.py file

import tkinter
from app.src.app_calculator import Calculator

class Main:
    def __init__(self):
        root = tkinter.Tk()
        Calculator(root)
        root.mainloop()

Now in our app_calculator.py file


import tkinter 

class Calculator:
    def __init__(self, root):
        self.root = root
        self.root.title("Calculator")
        x = (self.root.winfo_screenwidth() // 2) - (550 // 2)
        y = (self.root.winfo_screenheight() // 2) - (600 // 2)
        self.root.geometry("550x600+{}+{}".format( x, y))

Then we have our structure for the code, which in this case will be in app_calculator.py We start root in main.py and assign it to Calculator, as soon as we call calculator we already start root.mainloop () of our app. We give the screen a name with

    self.root.title ("Calculator") 

in app_calculator.py

And with

    self.root.geometry() 

we determine the size of the canvas, which in this case we are beyond determining the size. Also determining that it opens in the middle of the monitor! This way as soon as we execute calculator.py file, we will open the empty window that we will use to mount our app_calculator on it. Next step is to assemble the components for our app, which in Python is very simple. We will follow an order in the assembly that in this case from top to bottom, and apply ok. But it could be done in many other ways.

now in app_calculator.py file we declare this function!

    def frame_calc(self, root, side, bw, bd, bg):
        frame = tkinter.Frame(root, borderwidth= bw, bd= bw, bg= bg)
        frame.pack(side= side, expand=tkinter.YES, fill= tkinter.BOTH)
        return frame

And now we call the function we created to start our frame.

import tkinter

class Calculator:
    def __init__(self):
        self.root = root
        self.root.title("Calculator")
        x = (self.root.winfo_screenwidth() // 2) - (550 // 2)
        y = (self.root.winfo_screenheight() // 2) - (600 // 2)
        self.root.geometry("550x600+{}+{}".format( x, y))

        self.frame = frame_calc(self.root, tkinter.TOP, 1, 4, "black")


    def frame_calc(self, root, side, bw, bd, bg):
        frame = tkinter.Frame(root, borderwidth= bw, bd= bw, bg= bg)
        frame.pack(side= side, expand=tkinter.YES, fill= tkinter.BOTH)
        return frame

Creating the frame in a function now leaves us with a line of code every time we create a new frame in this project, which in this case will be just this one. Now let’s create the display for our calculator.

    def display(self, frame, font):
        display = tkinter.Entry(frame)
        display.configure(textvariable=self.value_display)
        display.configure(font=font)
        display.configure(justify='right')
        display.pack(side=tkinter.TOP, expand= tkinter.YES, fill= tkinter.BOTH)
        return display

It will also be a function similar to the one we placed above. And the code for our Calculator class will now have to look like this.

import tkinter

class Calculator:
    def __init__(self):
        self.root = root
        self.root.title("Calculator")
        x = (self.root.winfo_screenwidth() // 2) - (550 // 2)
        y = (self.root.winfo_screenheight() // 2) - (600 // 2)
        self.root.geometry("550x600+{}+{}".format( x, y))
        
        self.value_display = tkinter.StringVar()
        
        self.frame = frame_calc(self.root, tkinter.TOP, 1, 4, "black")

        self.display(self.frame, ("Ubunto", 17))


    def frame_calc(self, root, side, bw, bd, bg):
        frame = tkinter.Frame(root, borderwidth= bw, bd= bw, bg= bg)
        frame.pack(side= side, expand=tkinter.YES, fill= tkinter.BOTH)
        return frame


    def display(self, frame, font):
        display = tkinter.Entry(frame)
        display.configure(textvariable=self.value_display)
        display.configure(font=font)
        display.configure(justify='right')
        display.pack(side=tkinter.TOP, expand= tkinter.YES, fill= tkinter.BOTH)
        return display

Note that we now have a self.value_display variable starting with tkinter.StringVar () and set to textvariable on our display. In it will be the values of our display. With that we run the calculator.py file we will have a screen with a display already! Now we need to create a function with the calculator buttons and we will divide it into 3, two just to create the buttons on the screen and another one by extending the Buttons object of tkinter. Let’s start with the button.

    def button(self, frame, side, text, command=None):
        button = tkinter.Button(frame)
        button.configure(text= text)
        button.configure(font= ("Ubunto", 14))
        button.configure(command=command)
        button.pack( side=side, expand=tkinter.YES, fill=tkinter.BOTH)
        return button 

Now create the buttons to clear the display on the screen!

    def clear(self, root):
        for clear in (["CE"],["C"]):
            erase = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
            for row in clear:
                self.button( erase, tkinter.LEFT, row,
                            lambda storeObj=self.value_display, q= row: 
                                        storeObj.set(''))

Now we will create the buttons with the values for the calculator!

    def button_calculator(self, root):
        for num_btn in ("()%","/789", "*456", "-123", "+.0"):
            btn_calc = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
            for row in num_btn:
                self.button( btn_calc, tkinter.LEFT, row,
                            lambda display= self.value_display, q=row: 
                                    display.set(display.get() + q))

Now our code should look like this!

import tkinter

class Calculator:
    def __init__(self):
        self.root = root
        self.root.title("Calculator")
        x = (self.root.winfo_screenwidth() // 2) - (550 // 2)
        y = (self.root.winfo_screenheight() // 2) - (600 // 2)
        self.root.geometry("550x600+{}+{}".format( x, y))
        
        self.value_display = tkinter.StringVar()
        
        self.frame = frame_calc(self.root, tkinter.TOP, 1, 4, "black")

        self.display(self.frame, ("Ubunto", 17))

        self.clear(self.root)

        self.button_calculator(self.root)


    def frame_calc(self, root, side, bw, bd, bg):
        frame = tkinter.Frame(root, borderwidth= bw, bd= bw, bg= bg)
        frame.pack(side= side, expand=tkinter.YES, fill= tkinter.BOTH)
        return frame


    def display(self, frame, font):
        display = tkinter.Entry(frame)
        display.configure(textvariable=self.value_display)
        display.configure(font=font)
        display.configure(justify='right')
        display.pack(side=tkinter.TOP, expand= tkinter.YES, fill= tkinter.BOTH)
        return display
    
    
    def button(self, frame, side, text, command=None):
        button = tkinter.Button(frame)
        button.configure(text= text)
        button.configure(font= ("Ubunto", 14))
        button.configure(command=command)
        button.pack( side=side, expand=tkinter.YES, fill=tkinter.BOTH)
        return button 

    
    def clear(self, root):
        for clear in (["CE"],["C"]):
            erase = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
            for row in clear:
                self.button( erase, tkinter.LEFT, row,
                            lambda storeObj=self.value_display, q= row: 
                                        storeObj.set(''))


    def button_calculator(self, root):
        for num_btn in ("()%","/789", "*456", "-123", "+.0"):
            btn_calc = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
            for row in num_btn:
                self.button( btn_calc, tkinter.LEFT, row,
                            lambda display= self.value_display, q=row: 
                                    display.set(display.get() + q))

Now when executing the calculator.py file we already have our calculator with form, or almost and without functionality yet. We must now put the = button and the logic for our app, we go to our button!

    def equals(self, root):
        equals_button = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
        for value in "=":
            if value == '=':
                btn_equals = self.button( equals_button, tkinter.LEFT, value)
                btn_equals.bind('<ButtonRelease-1>',
                        lambda e, s=self, display=self.value_display: s.calc(display), '+')
            else:
                btn_equals = self.button( equals_button, tkinter.LEFT, value,
                        lambda display=self.value_display, s=' %s '%value: 
                                display.set(display.get()+s))

We have already left our calc () function, because we are already pressing buttons without it.

    def calc(self, display):
        try: 
            display.set(eval(display.get()))
        except:
            display.set("ERROR")

With that we have our code ready for operation that will look like this.

import tkinter

class Calculator:
    def __init__(self,root):
        self.root = root
        self.root.title("Calculator")
        x = (self.root.winfo_screenwidth() // 2) - (550 // 2)
        y = (self.root.winfo_screenheight() // 2) - (600 // 2)
        self.root.geometry("550x600+{}+{}".format( x, y))

        self.value_display = tkinter.StringVar()

        self.frame = self.frame_calc(self.root, tkinter.TOP, 1, 4, "black")

        self.display(self.frame, ("Ubunto", 17))

        self.clear(self.root)

        self.button_calculator(self.root)

        self.equals(self.root)


    def frame_calc(self, root, side, bw, bd, bg):
        frame = tkinter.Frame(root, borderwidth= bw, bd= bw, bg= bg)
        frame.pack(side= side, expand=tkinter.YES, fill= tkinter.BOTH)
        return frame


    def display(self, frame, font):
        display = tkinter.Entry(frame)
        display.configure(textvariable=self.value_display)
        display.configure(font=font)
        display.configure(justify='right')
        display.pack(side=tkinter.TOP, expand= tkinter.YES, fill= tkinter.BOTH)
        return display


    def button(self, frame, side, text, command=None):
        button = tkinter.Button(frame)
        button.configure(text= text)
        button.configure(font= ("Ubunto", 14))
        button.configure(command=command)
        button.pack( side=side, expand=tkinter.YES, fill=tkinter.BOTH)
        return button 


    def clear(self, root):
        for clear in (["CE"],["C"]):
            erase = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
            for row in clear:
                self.button( erase, tkinter.LEFT, row,
                            lambda storeObj=self.value_display, q= row: 
                                        storeObj.set(''))

    def button_calculator(self, root):
        for num_btn in ("()%","/789", "*456", "-123", "+.0"):
            btn_calc = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
            for row in num_btn:
                self.button( btn_calc, tkinter.LEFT, row,
                            lambda display= self.value_display, q=row: 
                                    display.set(display.get() + q))



    def equals(self, root):
        equals_button = self.frame_calc( root, tkinter.TOP, 1, 4, "white")
        for value in "=":
            if value == '=':
                btn_equals = self.button( equals_button, tkinter.LEFT, value)
                btn_equals.bind('<ButtonRelease-1>',
                        lambda e, s=self, display=self.value_display: s.calc(display), '+')
            else:
                btn_equals = self.button( equals_button, tkinter.LEFT, value,
                        lambda display=self.value_display, s=' %s '%value: 
                                display.set(display.get()+s))

    def calc(self, display):
        try: 
            display.set(eval(display.get()))
        except:
            display.set("ERROR")

Repository link

With that we reached the end of our calculator, in a very simple way. Thank you and see you later

jacksonsr45