Tkinter – Example 2: a calculator

calculator tkinter example

A calculator with tkinter

We are going to make another example with tkinter, building an app to create a calculator. Let’s start analysing each widget in the window.

The display (Entry widget)

To make the display we are going to use the following code:

   ########################
   ####  the display  #####
   ########################

display = tk.StringVar()
# relief can be FLAT or RIDGE or RAISED or SUNKEN GROOVE
entry_display = tk.Entry(self)
entry_display['relief'] = tk.FLAT
entry_display['textvariable'] = display
entry_display['justify'] = 'right'
entry_display['bd'] = 30
entry_display['bg'] = 'orange'
entry_display.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH)

Let’s comment the code

# THE VARIABLE TO GET THE VALUE IN THE DISPLAY
display = tk.StringVar()

# THE ENTRY OBJECT = DISPLAY
entry_display = tk.Entry(self)

# THE BORDER = FLAT
entry_display['relief'] = tk.FLAT

# IT CAN BE FLAT or RIDGE or RAISED or SUNKEN GROOVE

# THE VARIABLE SEEN BEFOR
entry_display['textvariable'] = display
# JUSTIFICATED TO THE RIGHT
entry_display['justify'] = 'right'
# THE THIKNESS
entry_display['bd'] = 30
# THE BACKGROUND COLOR
entry_display['bg'] = 'orange'
# TO SEE IT, AT THE TOP, EXTENDED AS LONG AS THE WINDOW
entry_display.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH)

 

The whole code

from tkinter import *

#This function returns a Frame
def iCalc(source, side):
    "Returns a Frame object yet packed and expanded, to shorten the code"
    # the bd is the border of the frame
    storeObj = Frame(source, borderwidth=10, bd=1, bg="gray")
    # the pack methos is needed to diplay the object
    storeObj.pack(side=side, expand=YES, fill=BOTH)
    return storeObj


def button(source, side, text, command=None):
    "Return a Button object that is packed yet"
    storeObj = Button(source, text=text, command=command)
    storeObj.pack(side=side, expand=YES, fill=BOTH)
    return storeObj

# This class inherit from Frame
class App(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.option_add("*Font", "arial 20 bold")
        self.pack(expand=YES, fill=BOTH)
        self.master.title("Calculator")


        #  THE DISPLAY
        display = StringVar()
        # relief can be FLAT or RIDGE or RAISED or SUNKEN GROOVE
        entry = Entry(self, relief=FLAT, textvariable=display, justify='right', bd=15, bg='orange')
        entry.pack(side=TOP)
        # I added an action to calculate the operation when Return (Enter) is hit
        entry.focus()
        # You can hit enter to get the result instead of clicking =
        self.master.bind("<Return>", lambda e, s=self, storeObj=display: s.calc(storeObj))
        # YOu can click del button on the keyboard to cancel
        self.master.bind("<Delete>", lambda e, s=self, storeObj=display: storeObj.set(""))
        self.master.bind("<BackSpace>", lambda e, s=self, storeObj=display: storeObj.set(""))

        # Thi is the frame for the [C] button
        erase = iCalc(self, TOP)
        clearBut = "C"
        button(erase, LEFT, clearBut, lambda storeObj=display, q=clearBut: storeObj.set(""))

        for numBut in ("789/", "456*", "123-", "0.+"):
            functionNum = iCalc(self, TOP)
            for char in numBut:
                button(functionNum, LEFT, char, lambda storeObj=display, q=char: storeObj.set(storeObj.get() + q))
        equalButton = iCalc(self, TOP)

        for iEqual in "=":
            if iEqual == "=":
                btniEqual = button(equalButton, LEFT, iEqual)
                btniEqual.bind("<ButtonRelease-1>", lambda e, s=self, storeObj=display: s.calc(storeObj), '+')
            else:
                btniEqual = button(equalButton, LEFT, iEqual, lambda storeObj=display, s='%s' % iEqual: storeObj.set(storeObj.get() + s))

    def calc(self, display):
        try:
        	# Sets the display to the evaluation of the string in the display itself, i.e. calculate the result
            display.set(eval(display.get()))
        except:
        	# if something goes wrong with the result
            display.set("ERROR")


if __name__ == '__main__':
    App().mainloop()

 

Video to show the app running

Version 2

In this version we have a different look, with a very big delete button.

from tkinter import *

#This function returns a Frame
def iCalc(source, side):
    "Returns a Frame object yet packed and expanded, to shorten the code"
    # the bd is the border of the frame
    storeObj = Frame(source, borderwidth=10, bd=1, bg="gray")
    # the pack methos is needed to diplay the object
    storeObj.pack(side=side, expand=YES, fill=BOTH)
    return storeObj


def button(source, side, text, command=None):
    "Return a Button object that is packed yet"
    storeObj = Button(source, text=text, command=command)
    storeObj.pack(side=side, expand=YES, fill=BOTH)
    return storeObj

# This class inherit from Frame
class App(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.option_add("*Font", "arial 20 bold")
        self.pack(expand=YES, fill=BOTH)
        self.master.title("Calculator")


        #  THE DISPLAY
        display = StringVar()
        # relief can be FLAT or RIDGE or RAISED or SUNKEN GROOVE
        entry = Entry(self, relief=FLAT, textvariable=display, justify='right', bd=15, bg='orange')
        entry.pack(side=TOP)
        # I added an action to calculate the operation when Return (Enter) is hit
        entry.focus()
        # You can hit enter to get the result instead of clicking =
        self.master.bind("<Return>", lambda e, s=self, storeObj=display: s.calc(storeObj))
        # YOu can click del button on the keyboard to cancel
        self.master.bind("<Delete>", lambda e, s=self, storeObj=display: storeObj.set(""))
        self.master.bind("<BackSpace>", lambda e, s=self, storeObj=display: storeObj.set(""))

        # Thi is the frame for the [C] button
        erase = iCalc(self, LEFT)
        clearBut = "Delete"
        button(erase, LEFT, clearBut, lambda storeObj=display, q=clearBut: storeObj.set(""))

        for numBut in ("789/", "456*", "123-", "0.+"):
            functionNum = iCalc(self, TOP)
            for char in numBut:
                button(functionNum, LEFT, char, lambda storeObj=display, q=char: storeObj.set(storeObj.get() + q))
        equalButton = iCalc(self, TOP)

        for iEqual in "=":
            if iEqual == "=":
                btniEqual = button(equalButton, LEFT, iEqual)
                btniEqual.bind("<ButtonRelease-1>", lambda e, s=self, storeObj=display: s.calc(storeObj), '+')
            else:
                btniEqual = button(equalButton, LEFT, iEqual, lambda storeObj=display, s='%s' % iEqual: storeObj.set(storeObj.get() + s))

    def calc(self, display):
        try:
        	# Sets the display to the evaluation of the string in the display itself, i.e. calculate the result
            display.set(eval(display.get()))
        except:
        	# if something goes wrong with the result
            display.set("ERROR")


if __name__ == '__main__':
    App().mainloop()

Minimal layout: Mini-Calculator

The article with the mini-calculator code (another version, minimal).

Tkinter test for students

Tkinter articles

Advertisement