Scientific Calculator GUI in Python using Tkinter
In this tutirial, we are going to build our own Scientific Calculator using Tkinter library of Python.
This tutorial is originally written by Rahul Roy
To download the full project in Zip format kindly ref this: Scientific Calculator GUI in Python using Tkinter | Rahul0921 (coderspacket.com)
Having a scientific calculator is an absolute requirement when it comes to the daily needs of a Science student. Especially in these days of online learning, having a Scientific Calculator on your screen helps a lot in online assignments.
This article is here to help you build one using Python’s standard GUI toolkit – Tkinter.
Logic behind the working of this Scientific Calculator
Our Calculator will look like this
1. We will create a class Calc where the instance variables shall be
i. ‘op’(Stores the current operator clicked by user),
ii. ‘M’(stores memory),
iii. ‘isTrue_2nd’(stores True if ‘2nd’ is clicked and activates the second set of scientific functions like sin-1, 10x. ex, y √x in place of sin, log, ln, xy ),
iv. ‘isdegree’(True if ‘deg’ is on, takes radians for trigonometric operations when ‘deg’ is clicked to ‘rad’ and isdegree = False) and
v. ‘decimalclicked’(turns True when user clicks ‘.’)
2. We then create all the functions to be invoked on clicking any operational button.
3. Then we come to the structural part of the Calculator, we will build the display box (with a textvariable for better modifications) and the buttons with their respective paddings,styles(necessary fonts and colors) and commands as the function to be executed after being clicked.
4. At last, we will arrange all these buttons in a grid geometry.
5. We can add our desired fonts using Tkinter.font.Font() and an Icon image to our GUI using root.iconphoto(false, PhotoImage(filename)) where root is our main GUI window.
Implementation of the Required Functions
We can divide these functions into 5 types :-
1. Functions that show immediate result on the display value. Eg: AC, CE, numClick(0-9), signClick(+/-), decimalClick(.), epiranClick(e, pi or #Ran), rec_fact_Click(x-1 or x!).
2. Functions that stores the memory and sets up the display to receive a second operand. The ultimate result will be displayed after we press equals(=). Eg: DMAS_mod_Click(/,*,+,- or MOD).
3. Functions that are like type 1, but the buttons involved with them share 2 complementary operations. Eg: trig_log_root_click(sin,cos,tan,√x,log,ln).
4. Functions that are like type 2, but the buttons involved with them share 2 complementary operations. Eg: pow_pc_clicked(xy, ncr).
5. Other Functions like equalpress(operates for Functions type 2 and 4), deg_2nd_clicked(‘deg’ when clicked turns to rad, that means the display value will be taken as radian for trigonometric operations, ‘2nd’ turns on the complementary functions).
So here is the code of our GUI Calculator in Python.
class Calc(): def __init__(self): self.op, self.M = None, None, self.isTrue_2nd, self.isdegree, = False, True self.decimalclicked = False def AC(self): self.op, self.M, self.decimalclicked = None, None, False equation.set("0") def CE(self): self.decimalclicked = False equation.set("0") def error(self, type=""): self.CE() equation.set(type + "ERROR") def numclick(self,num): curdisp = equation.get() if not self.op and curdisp == '0': equation.set(num) elif self.op and curdisp == self.M: equation.set(num) else: equation.set(curdisp + num) def signClick(self): try: curdisp = equation.get() if curdisp != '0': equation.set(str(float(equation.get())*-1)) except: self.error() def decimalClick(self): curdisp = equation.get() if self.op and curdisp == self.M: equation.set("0.") elif not self.decimalclicked: equation.set(curdisp+".") self.decimalclicked = True def epiRanClick(self,clicked): self.CE() val = {'e':str(math.e),'pi':str(math.pi),'Ran':str(random.random())} equation.set(val[clicked]) def DMAS_mod_Click(self,clicked): self.M = equation.get() self.op = clicked def rec_fact_click(self,clicked): cur = equation.get() try: if clicked == 'r': cur = str(float(cur)**-1) if clicked == '!': cur = str(math.factorial(float(cur))) equation.set(cur) except: self.error(type="MATH ") def trig_log_root_click(self,clicked): cur = equation.get() try: if clicked == 'g': cur = str((math.log10(float(cur)), math.pow(10, float(cur))) [self.isTrue_2nd]) elif clicked == 'n': cur = str((math.log(float(cur)), math.pow(math.e, float(cur))) [self.isTrue_2nd]) elif clicked == 'r': cur = str((math.sqrt(float(cur)), math.pow(float(cur), 2)) [self.isTrue_2nd]) if clicked in "gnr": equation.set(cur) return if self.isdegree and not self.isTrue_2nd: cur = float(cur)*math.pi/180 else: cur = float(cur) if clicked == 's': cur = str(round((math.sin(cur), math.asin(cur)) [self.isTrue_2nd], 10)) elif clicked == 'c': cur = str(round((math.cos(cur), math.acos(cur)) [self.isTrue_2nd], 10)) elif clicked == 't': cur = str(round((math.tan(cur), math.atan(cur)) [self.isTrue_2nd], 10)) equation.set(cur) except: self.error(type="MATH ") def pow_pc_clicked(self,clicked): self.M = equation.get() inv_func = {'pow': 'yroot', 'c': 'p'} self.op = {True:inv_func[clicked], False:clicked} [self.isTrue_2nd] def equalpress(self): # = pressed Just after op clicked if self.op and equation.get() == self.M: self.error() return cur = equation.get() try: if self.op == '+': cur = str(float(self.M) + float(cur)) elif self.op == '-': cur = str(float(self.M) - float(cur)) elif self.op == '*': cur = str(float(cur) * float(self.M)) elif self.op == '/': cur = str(float(self.M)/float(cur)) elif self.op == 'mod': cur = str(float(self.M) % float(cur)) elif self.op == 'pow': cur = str(math.pow(float(self.M),float(cur))) elif self.op == 'yroot': cur = str(math.pow(float(self.M),1/float(cur))) elif self.op == 'c': cur = str(math.comb(int(float(self.M)),int(float(cur)))) elif self.op == 'p': cur = str(math.perm(int(float(self.M)),int(float(cur)))) self.AC() if '.' in cur: self.decimalclicked = True equation.set(cur) except: self.error(type="MATH ") def deg_2nd_click(self, clicked): if clicked == 'deg': self.isdegree = not self.isdegree btn_deg["text"] = {True: "rad", False: "deg"} [btn_deg["text"] == "deg"] btn_deg["bg"] = {True: "yellow", False: "#eff1f4"} [btn_deg["bg"] =="#eff1f4"] elif clicked == '2nd': self.isTrue_2nd = not self.isTrue_2nd btn_permucombo["text"] = {True: "nPr", False: "nCr"} [btn_permucombo["text"] == "nCr"] btn_log10["text"] = {True: "10"+get_super('x'), False: "log"} [btn_log10["text"] == "log"] btn_loge["text"] = {True: "e"+get_super('x'), False: "ln"} [btn_loge["text"] == "ln"] btn_root["text"] = {True: "x"+get_super('2'), False: "√"} [btn_root["text"] == "√"] btn_power["text"] = {True: get_super('y')+"√x", False: "x"+get_super('y')} [btn_power["text"] == "x"+get_super('y')] btn_sin["text"] = {True: "sin"+get_super('-1'), False: "sin"} [btn_sin["text"] == "sin"] btn_cos["text"] = {True: "cos"+get_super('-1'), False: "cos"} [btn_cos["text"] == "cos"] btn_tan["text"] = {True: "tan"+get_super('-1'), False: "tan"} [btn_tan["text"] == "tan"] btn_log10["padx"] = {True: 23, False: 20} [btn_log10["padx"] == 20] btn_root["padx"] = {True: 24, False: 29} [btn_root["padx"] == 29] btn_power["padx"] = {True: 22, False: 27} [btn_power["padx"] == 27] btn_sin["padx"] = {True: 11, False: 20} [btn_sin["padx"] == 20] btn_cos["padx"] = {True: 11, False: 20} [btn_cos["padx"] == 20] btn_tan["padx"] = {True: 11, False: 20} [btn_tan["padx"] == 20] btn_2nd["bg"] = {True: "yellow", False: "#eff1f4"} [btn_2nd["bg"] == "#eff1f4"] c= Calc() equation = StringVar(value='0') input = Entry(root, textvariable=equation, bg='#c1ecf4',bd=10, width=35, font=("Lucida Console", 16), justify=RIGHT) input.grid(row=0, column=0, columnspan=5, pady=10) def get_super(x): normal = "xy12-()" superscript = "ˣʸ¹²⁻⁽⁾" res = x.maketrans(''.join(normal), ''.join(superscript)) return x.translate(res) btn_1 = Button(root, text="1", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('1')) btn_2 = Button(root, text="2", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('2')) btn_3 = Button(root, text="3", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('3')) btn_4 = Button(root, text="4", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('4')) btn_5 = Button(root, text="5", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('5')) btn_6 = Button(root, text="6", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('6')) btn_7 = Button(root, text="7", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('7')) btn_8 = Button(root, text="8", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('8')) btn_9 = Button(root, text="9", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('9')) btn_0 = Button(root, text="0", padx=30, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.numclick('0')) btn_point = Button(root, text=".", padx=31, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.decimalClick()) btn_add = Button(root, text="+", padx=30, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.DMAS_mod_Click('+')) btn_sub = Button(root, text="-", padx=30, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.DMAS_mod_Click('-')) btn_mult = Button(root, text="x", padx=30, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.DMAS_mod_Click('*')) btn_div = Button(root, text="/", padx=30, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.DMAS_mod_Click('/')) btn_sign = Button(root, text="+/-", padx=21, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.signClick()) btn_equals = Button(root, text="=", padx=30, pady=15,font=myfont,bd=4,bg='orange' ,command=lambda: c.equalpress()) btn_reciprocal = Button(root, text='x'+get_super('-1'), padx=21, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.rec_fact_click('r')) btn_power = Button(root, text='x'+get_super('y'), padx=27, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.pow_pc_clicked('pow')) btn_root = Button(root, text="√", padx=29, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.trig_log_root_click('r')) btn_fact = Button(root, text="x!", padx=24, pady=15,bd=4, font=myfont,bg="#eff1f4", command=lambda: c.rec_fact_click('!')) btn_permucombo = Button(root, text="nCr", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.pow_pc_clicked('c')) btn_random = Button(root, text="Ran#", padx=16, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.epiRanClick('Ran')) btn_mod = Button(root, text="MOD", padx=21, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.DMAS_mod_Click('mod')) btn_e = Button(root, text="e", padx=29, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.epiRanClick('e')) btn_pi = Button(root, text="π", padx=29, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.epiRanClick('pi')) btn_sin = Button(root, text="sin", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.trig_log_root_click('s')) btn_cos = Button(root, text="cos", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.trig_log_root_click('c')) btn_tan = Button(root, text="tan", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.trig_log_root_click('t')) btn_log10 = Button(root, text="log", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.trig_log_root_click('g')) btn_loge = Button(root, text="ln", padx=26, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.trig_log_root_click('n')) btn_2nd = Button(root, text="2nd", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.deg_2nd_click('2nd')) btn_deg = Button(root, text="deg", padx=20, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.deg_2nd_click('deg')) btn_ce = Button(root, text="CE", padx=26, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.CE()) btn_ac = Button(root, text="AC", padx=25, pady=15,bd=4,bg="#eff1f4", font=myfont, command=lambda: c.AC()) btn_2nd.grid(row=1, column=0) btn_deg.grid(row=1, column=1) btn_mod.grid(row=1, column=2) btn_ce.grid(row=1, column=3) btn_ac.grid(row=1, column=4) btn_fact.grid(row=2, column=0) btn_permucombo.grid(row=2, column=1) btn_random.grid(row=2, column=2) btn_log10.grid(row=2, column=3) btn_loge.grid(row=2, column=4) btn_root.grid(row=3, column=0) btn_power.grid(row=3, column=1) btn_reciprocal.grid(row=3, column=2) btn_div.grid(row=3, column=3) btn_sin.grid(row=3, column=4) btn_7.grid(row=4, column=0) btn_8.grid(row=4, column=1) btn_9.grid(row=4, column=2) btn_mult.grid(row=4, column=3) btn_cos.grid(row=4, column=4) btn_4.grid(row=5, column=0) btn_5.grid(row=5, column=1) btn_6.grid(row=5, column=2) btn_sub.grid(row=5, column=3) btn_tan.grid(row=5, column=4) btn_1.grid(row=6, column=0) btn_2.grid(row=6, column=1) btn_3.grid(row=6, column=2) btn_add.grid(row=6, column=3) btn_pi.grid(row=6, column=4) btn_sign.grid(row=7, column=0) btn_0.grid(row=7, column=1) btn_point.grid(row=7, column=2) btn_e.grid(row=7, column=3) btn_equals.grid(row=7, column=4) root.mainloop()
Hello,
I found this wonderful project in Python, and I would like to know how the variable superscript = “ˣʸ¹²⁻⁽⁾” was created to be used in x.maketrans of the get_super method:
set get_super(x):
normal = “xy12-()”
superscript = “ˣʸ¹²⁻⁽⁾”
print(superscript)
res = x.maketrans(”.join(normal), ”.join(superscript))
You wrote the code without comments, it’s difficult to understand. Moreover, there’s an error in the function “epiRanClick(self, clicked)”. The method random has an unsolved reference.