Calculator in Python

Prerequisites

  • Basic Python knowledge
  • Basic oops concept

In this article, I’m going to describe to you how to make a calculator in Python using tkinter package. To make any desktop application in python, we need to import any desktop package like QT, tkinter, etc. Here I have selected the tkinter package.

Calculator in Python using tkinter package

The tkinter package (“Tk interface”) is the standard Python interface to the Tk GUI toolkit 

  1. from tkinter import *
  2. import re

Now, we’ll have to assume some layout design for the calculator so that we can implement the same using tkinter package. Here I have chosen this design for that purpose:

Calculator in Python using tkinter package

If you are new to tkinter, I’ll recommend you to go through this page: https://tkdocs.com/tutorial/firstexample.html

I have extended Frame class of the tkinter in my custom class Calculator

  1. class Calculator(Frame):
  2. ....methods here
  3.  
  4. app = Calculator()
  5. app.mainloop()

mainloop is a method of Frame class which starts the desktop widget. So, after playing our all logic, we’ll have to call this method to render the GUI widgets.

constructor

I have started rendering of widgets right from the constructor of the class:

  1. def __init__(self, master=None):
  2. master = self.main_window()
  3. super().__init__(master)
  4. self.pack()
  5. self.create_widgets()
  6. self.render_widgets()

For any desktop application, there is always a main window which contains other widgets like button, label, input field, etc. Here I have created the main window master = self.main_window() in the above snippet

 

main_window()
  1. def main_window(self):
  2. root = Tk()
  3. root.title("Calculator developed by Ahmad Asjad using PYTHON")
  4. return root

For more detail on Tk class visit https://docs.python.org/3.5/library/tkinter.html#tkinter.Tk

create_widgets()

Now moving to our one of main functionality part – design layout for the calculator calling self.create_widgets()

  1. def create_widgets(self):
  2. self.create_controls()
  3. self.create_digits()
  4.  
  5. #4 width widgets
  6. four_width = ['clear_all', 'clear', 'div', 'mult', 'min', 'plus', 'equal', 'seven', 'eight', 'nine', 'four',
  7. 'five', 'six', 'one', 'two', 'three', 'dot']
  8. self.set_width(four_width, 4)
  9.  
  10. #30 width widgets
  11. thirty_width = ['result', 'input']
  12. self.set_width(thirty_width, 30)

In this method, I have called two methods of the same class to create the input widgets. I have categorized all buttons into two parts one for controls like plus, minus, division, etc. and another for digits like one, two, three, etc. characters. Then I have grouped those buttons width wise so that we can set the width of each widget accordingly.

 

Creating widgets

Let’s have a look on booth method creating the controls and digit widgets:

create_controls()
  1. def create_controls(self):
  2. self.result = Label(self, text='Result', bg='gray', height=2)
  3. self.input = Entry(self)
  4. self.clear_all = Button(self, bg='#393939', fg='white', text='C', height=2)
  5. self.clear = Button(self, bg='#393939', fg='white', text='X', height=2)
  6. self.div = Button(self, bg='#393939', fg='white', text='/', height=2, command=lambda : self.appent_input('/'))
  7. self.mult = Button(self, bg='#393939', fg='white', text='*', height=2, command=lambda : self.appent_input('*'))
  8. self.min = Button(self, bg='#393939', fg='white', text='-', height=2, command=lambda : self.appent_input('-'))
  9. self.plus = Button(self, bg='#393939', fg='white', text='+', height=2, command=lambda : self.appent_input('+'))
  10. self.equal = Button(self, bg='red', fg='white', text='=', height=5, command=self.calculate)

In this, I have set up all control required for a basic calculator and set its background and foreground color according to the design layout we decided earlier in the image. Don’t forget to see the command param of Button widgets. We are passing a method to perform the work once this button is clicked. Except one of them, all are passed appent_input() method and unique one is passed calculate method. You’ll see these methods later what exactly they do.

 

create_digits()
  1. def create_digits(self):
  2. self.seven = Button(self, bg='#1f1f1f', fg='white', text='7', height=2, command=lambda : self.appent_input('7'))
  3. self.eight = Button(self, bg='#1f1f1f', fg='white', text='8', height=2, command=lambda : self.appent_input('8'))
  4. self.nine = Button(self, bg='#1f1f1f', fg='white', text='9', height=2, command=lambda : self.appent_input('9'))
  5.  
  6. self.four = Button(self, bg='#1f1f1f', fg='white', text='4', height=2, command=lambda : self.appent_input('4'))
  7. self.five = Button(self, bg='#1f1f1f', fg='white', text='5', height=2, command=lambda : self.appent_input('5'))
  8. self.six = Button(self, bg='#1f1f1f', fg='white', text='6', height=2, command=lambda : self.appent_input('6'))
  9.  
  10. self.one = Button(self, bg='#1f1f1f', fg='white', text='1', height=2, command=lambda : self.appent_input('1'))
  11. self.two = Button(self, bg='#1f1f1f', fg='white', text='2', height=2, command=lambda : self.appent_input('2'))
  12. self.three = Button(self, bg='#1f1f1f', fg='white', text='3', height=2, command=lambda : self.appent_input('3'))
  13.  
  14. self.zero = Button(self, bg='#1f1f1f', fg='white', text='0', width=12, height=2, command=lambda : self.appent_input('0'))
  15. self.dot = Button(self, bg='#1f1f1f', fg='white', text='.', height=2, command=lambda : self.appent_input('.'))

Same as the above method except that this generates digit widgets whereas above method generates control widgets.

 

Command methods being used on click of any widget

appent_input()
  1. def appent_input(self, val):
  2. old_val = self.input.get()
  3. new_val = old_val + val
  4. self.input.delete(0, END)
  5. self.input.insert(0, self.validate_input(new_val))

What exactly it does is, after a minimal validation, it appends the particular character in the input field. You can add more validation according to the scenario, this is for descriptive purpose only.

calculate()
  1. def calculate(self):
  2. input_val = self.input.get()
  3. print(eval(input_val))
  4. self.result.config(text=input_val)
  5. self.input.delete(0, END)
  6. self.input.insert(0, eval(input_val))

This is the main method of the class which solves the calculation problem.

 

validate_input()
  1. def validate_input(self, input):
  2. first_char = input[0]
  3. wront_char = re.match('[^0-9\-\+\(\)]', first_char)
  4. if(wront_char == None):
  5. return input
  6. elif(len(input) == 1):
  7. return ""
  8. else:
  9. return self.validate_input(input[1:])

To validate the clicked button input. Like in the expression what character is allowed and what not.

 

render_widgets()

Till now, we have created the widgets, but we didn’t tell the system where these digits to appear. Following method solves this problem:

  1. def render_widgets(self):
  2. self.result.grid(row=0, column=0, columnspan=4)
  3. self.input.grid(row=1, column=0, columnspan=4)
  4.  
  5. self.clear_all.grid(row=2, column=0)
  6. self.clear.grid(row=2, column=1)
  7. self.div.grid(row=2, column=2)
  8. self.mult.grid(row=2, column=3)
  9. self.min.grid(row=3, column=3)
  10. self.plus.grid(row=4, column=3)
  11. self.equal.grid(row=5, column=3, rowspan=2)
  12.  
  13. self.seven.grid(row=3, column=0)
  14. self.eight.grid(row=3, column=1)
  15. self.nine.grid(row=3, column=2)
  16.  
  17. self.four.grid(row=4, column=0)
  18. self.five.grid(row=4, column=1)
  19. self.six.grid(row=4, column=2)
  20.  
  21. self.one.grid(row=5, column=0)
  22. self.two.grid(row=5, column=1)
  23. self.three.grid(row=5, column=2)
  24.  
  25. self.zero.grid(row=6, column=0, columnspan=2)
  26. self.dot.grid(row=6, column=2)

Here we have told the system how and where those widgets to appear. I have used grid to align those widgets, you can use according to your choice from the available Tk’s geometry-management mechanisms like pack(), etc.

 

For the complete class file, visit GitHub gist:

https://gist.github.com/ahmadasjad/2d7deb37a5867efd6d07c862ee2238d7

 

 

 

Leave a Reply