Python 3 calculator with tkinter











up vote
4
down vote

favorite
1












I have been programming for a long time. Only recently have I decided to take a stab at Python (I should be working with C# as I am in school for it, but I don't care for Windows, long story).



I was on this site and it showed a source for a calculator. I took it, and put it in PyCharm and started to study. By the time I was done I had changed the source significantly. I had added keyboard binding and reduced a lot of the redundant code in it.



My question is simple: is this code that I wrote efficient from a python standard viewpoint?



# -*-coding: utf-8-*-
# !/usr/bin/python3.5

from tkinter import Tk, Button, Entry, END
import math


class Calc:
def getandreplace(self): # replace x, + and % to symbols that can be used in calculations
# we wont re write this to the text box until we are done with calculations

self.txt = self.e.get() # Get value from text box and assign it to the global txt var
self.txt = self.txt.replace('÷', '/')
self.txt = self.txt.replace('x', '*')
self.txt = self.txt.replace('%', '/100')

def evaluation(self, specfunc): # Evaluate the items in the text box for calculation specfunc = eq, sqroot or power
self.getandreplace()
try:
self.txt = eval(str(self.txt)) # evaluate the expression using the eval function
except SyntaxError:
self.displayinvalid()
else:
if any([specfunc == 'sqroot', specfunc == 'power']): # Square Root and Power are special
self.txt = self.evalspecialfunctions(specfunc)

self.refreshtext()

def displayinvalid(self):
self.e.delete(0, END)
self.e.insert(0, 'Invalid Input!')

def refreshtext(self): # Delete current contents of textbox and replace with our completed evaluatioin
self.e.delete(0, END)
self.e.insert(0, self.txt)

def evalspecialfunctions(self, specfunc): # Calculate square root and power if specfunc is sqroot or power
if specfunc == 'sqroot':
return math.sqrt(float(self.txt))
elif specfunc == 'power':
return math.pow(float(self.txt), 2)

def clearall(self): # AC button pressed on form or 'esc" pressed on keyboard
self.e.delete(0, END)
self.e.insert(0, '0')

def clear1(self, event=None):
# C button press on form or backspace press on keyboard event defined on keyboard press

if event is None:
self.txt = self.e.get()[:-1] # Form backspace done by hand
else:
self.txt = self.getvalue() # No need to manually delete when done from keyboard

self.refreshtext()

def action(self, argi: object): # Number or operator button pressed on form and passed in as argi
self.txt = self.getvalue()

self.stripfirstchar()

self.e.insert(END, argi)

def keyaction(self, event=None): # Key pressed on keyboard which defines event
self.txt = self.getvalue()

if any([event.char.isdigit(), event.char in '/*-+%().']):
self.stripfirstchar()
elif event.char == 'x08':
self.clear1(event)
elif event.char == 'x1b':
self.clearall()
elif event.char == 'r':
self.evaluation('eq')
else:
self.displayinvalid()
return 'break'

def stripfirstchar(self): # Strips leading 0 from text box with first key or button is pressed
if self.txt[0] == '0':
self.e.delete(0, 1)

def getvalue(self): # Returns value of the text box
return self.e.get()

def __init__(self, master): # Constructor method
self.txt = 'o' # Global var to work with text box contents
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0, column=0, columnspan=6, pady=3)
self.e.insert(0, '0')
self.e.focus_set() # Sets focus on the text box text area

# Generating Buttons
Button(master, text="=", width=10, command=lambda: self.evaluation('eq')).grid(row=4, column=4, columnspan=2)
Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)
Button(master, text='C', width=3, command=lambda: self.clear1()).grid(row=1, column=5)
Button(master, text="+", width=3, command=lambda: self.action('+')).grid(row=4, column=3)
Button(master, text="x", width=3, command=lambda: self.action('x')).grid(row=2, column=3)
Button(master, text="-", width=3, command=lambda: self.action('-')).grid(row=3, column=3)
Button(master, text="÷", width=3, command=lambda: self.action('÷')).grid(row=1, column=3)
Button(master, text="%", width=3, command=lambda: self.action('%')).grid(row=4, column=2)
Button(master, text="7", width=3, command=lambda: self.action('7')).grid(row=1, column=0)
Button(master, text="8", width=3, command=lambda: self.action('8')).grid(row=1, column=1)
Button(master, text="9", width=3, command=lambda: self.action('9')).grid(row=1, column=2)
Button(master, text="4", width=3, command=lambda: self.action('4')).grid(row=2, column=0)
Button(master, text="5", width=3, command=lambda: self.action('5')).grid(row=2, column=1)
Button(master, text="6", width=3, command=lambda: self.action('6')).grid(row=2, column=2)
Button(master, text="1", width=3, command=lambda: self.action('1')).grid(row=3, column=0)
Button(master, text="2", width=3, command=lambda: self.action('2')).grid(row=3, column=1)
Button(master, text="3", width=3, command=lambda: self.action('3')).grid(row=3, column=2)
Button(master, text="0", width=3, command=lambda: self.action('0')).grid(row=4, column=0)
Button(master, text=".", width=3, command=lambda: self.action('.')).grid(row=4, column=1)
Button(master, text="(", width=3, command=lambda: self.action('(')).grid(row=2, column=4)
Button(master, text=")", width=3, command=lambda: self.action(')')).grid(row=2, column=5)
Button(master, text="√", width=3, command=lambda: self.evaluation('sqroot')).grid(row=3, column=4)
Button(master, text="x²", width=3, command=lambda: self.evaluation('power')).grid(row=3, column=5)

# bind key strokes
self.e.bind('<Key>', lambda evt: self.keyaction(evt))


# Main
root = Tk()
obj = Calc(root) # object instantiated
root.mainloop()


I don't really care for the names of some of the function names and variable names. I like using descriptive names, so names like self.e would have been called self.textbox or something. These things are leftovers from the web copy I found and haven't changed them.



Per request, the original code for this is below.




#-*-coding: utf-8-*-
from Tkinter import *
import math

class calc:
def getandreplace(self):
"""replace x with * and ÷ with /"""

self.expression = self.e.get()
self.newtext=self.expression.replace(self.newdiv,'/')
self.newtext=self.newtext.replace('x','*')

def equals(self):
"""when the equal button is pressed"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.e.delete(0,END)
self.e.insert(0,self.value)

def squareroot(self):
"""squareroot method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqrtval=math.sqrt(self.value)
self.e.delete(0,END)
self.e.insert(0,self.sqrtval)

def square(self):
"""square method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqval=math.pow(self.value,2)
self.e.delete(0,END)
self.e.insert(0,self.sqval)

def clearall(self):
"""when clear button is pressed,clears the text input area"""
self.e.delete(0,END)

def clear1(self):
self.txt=self.e.get()[:-1]
self.e.delete(0,END)
self.e.insert(0,self.txt)

def action(self,argi):
"""pressed button's value is inserted into the end of the text area"""
self.e.insert(END,argi)

def __init__(self,master):
"""Constructor method"""
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0,column=0,columnspan=6,pady=3)
self.e.focus_set() #Sets focus on the input text area

self.div='÷'
self.newdiv=self.div.decode('utf-8')

#Generating Buttons
Button(master,text="=",width=10,command=lambda:self.equals()).grid(row=4, column=4,columnspan=2)
Button(master,text='AC',width=3,command=lambda:self.clearall()).grid(row=1, column=4)
Button(master,text='C',width=3,command=lambda:self.clear1()).grid(row=1, column=5)
Button(master,text="+",width=3,command=lambda:self.action('+')).grid(row=4, column=3)
Button(master,text="x",width=3,command=lambda:self.action('x')).grid(row=2, column=3)
Button(master,text="-",width=3,command=lambda:self.action('-')).grid(row=3, column=3)
Button(master,text="÷",width=3,command=lambda:self.action(self.newdiv)).grid(row=1, column=3)
Button(master,text="%",width=3,command=lambda:self.action('%')).grid(row=4, column=2)
Button(master,text="7",width=3,command=lambda:self.action('7')).grid(row=1, column=0)
Button(master,text="8",width=3,command=lambda:self.action(8)).grid(row=1, column=1)
Button(master,text="9",width=3,command=lambda:self.action(9)).grid(row=1, column=2)
Button(master,text="4",width=3,command=lambda:self.action(4)).grid(row=2, column=0)
Button(master,text="5",width=3,command=lambda:self.action(5)).grid(row=2, column=1)
Button(master,text="6",width=3,command=lambda:self.action(6)).grid(row=2, column=2)
Button(master,text="1",width=3,command=lambda:self.action(1)).grid(row=3, column=0)
Button(master,text="2",width=3,command=lambda:self.action(2)).grid(row=3, column=1)
Button(master,text="3",width=3,command=lambda:self.action(3)).grid(row=3, column=2)
Button(master,text="0",width=3,command=lambda:self.action(0)).grid(row=4, column=0)
Button(master,text=".",width=3,command=lambda:self.action('.')).grid(row=4, column=1)
Button(master,text="(",width=3,command=lambda:self.action('(')).grid(row=2, column=4)
Button(master,text=")",width=3,command=lambda:self.action(')')).grid(row=2, column=5)
Button(master,text="√",width=3,command=lambda:self.squareroot()).grid(row=3, column=4)
Button(master,text="x²",width=3,command=lambda:self.square()).grid(row=3, column=5)
#Main
root = Tk()
obj=calc(root) #object instantiated
root.mainloop()










share|improve this question
























  • For comparison, could you add a link to where you got the original code from?
    – 200_success
    Sep 21 at 1:30










  • @200_success - per your request I included a full copy of the code I got from the web. The link is techinfected.net/2016/02/…
    – Riv
    Sep 21 at 3:07












  • Our convention is to put original code that is not to be reviewed in blockquotes, which is what I did in Rev 6.
    – 200_success
    Sep 21 at 3:14










  • Oh, I was wondering what happened, I thought I messed it up
    – Riv
    Sep 21 at 3:15










  • Any reason to pick Tk verses Kivy?
    – C. Harley
    Sep 25 at 6:11















up vote
4
down vote

favorite
1












I have been programming for a long time. Only recently have I decided to take a stab at Python (I should be working with C# as I am in school for it, but I don't care for Windows, long story).



I was on this site and it showed a source for a calculator. I took it, and put it in PyCharm and started to study. By the time I was done I had changed the source significantly. I had added keyboard binding and reduced a lot of the redundant code in it.



My question is simple: is this code that I wrote efficient from a python standard viewpoint?



# -*-coding: utf-8-*-
# !/usr/bin/python3.5

from tkinter import Tk, Button, Entry, END
import math


class Calc:
def getandreplace(self): # replace x, + and % to symbols that can be used in calculations
# we wont re write this to the text box until we are done with calculations

self.txt = self.e.get() # Get value from text box and assign it to the global txt var
self.txt = self.txt.replace('÷', '/')
self.txt = self.txt.replace('x', '*')
self.txt = self.txt.replace('%', '/100')

def evaluation(self, specfunc): # Evaluate the items in the text box for calculation specfunc = eq, sqroot or power
self.getandreplace()
try:
self.txt = eval(str(self.txt)) # evaluate the expression using the eval function
except SyntaxError:
self.displayinvalid()
else:
if any([specfunc == 'sqroot', specfunc == 'power']): # Square Root and Power are special
self.txt = self.evalspecialfunctions(specfunc)

self.refreshtext()

def displayinvalid(self):
self.e.delete(0, END)
self.e.insert(0, 'Invalid Input!')

def refreshtext(self): # Delete current contents of textbox and replace with our completed evaluatioin
self.e.delete(0, END)
self.e.insert(0, self.txt)

def evalspecialfunctions(self, specfunc): # Calculate square root and power if specfunc is sqroot or power
if specfunc == 'sqroot':
return math.sqrt(float(self.txt))
elif specfunc == 'power':
return math.pow(float(self.txt), 2)

def clearall(self): # AC button pressed on form or 'esc" pressed on keyboard
self.e.delete(0, END)
self.e.insert(0, '0')

def clear1(self, event=None):
# C button press on form or backspace press on keyboard event defined on keyboard press

if event is None:
self.txt = self.e.get()[:-1] # Form backspace done by hand
else:
self.txt = self.getvalue() # No need to manually delete when done from keyboard

self.refreshtext()

def action(self, argi: object): # Number or operator button pressed on form and passed in as argi
self.txt = self.getvalue()

self.stripfirstchar()

self.e.insert(END, argi)

def keyaction(self, event=None): # Key pressed on keyboard which defines event
self.txt = self.getvalue()

if any([event.char.isdigit(), event.char in '/*-+%().']):
self.stripfirstchar()
elif event.char == 'x08':
self.clear1(event)
elif event.char == 'x1b':
self.clearall()
elif event.char == 'r':
self.evaluation('eq')
else:
self.displayinvalid()
return 'break'

def stripfirstchar(self): # Strips leading 0 from text box with first key or button is pressed
if self.txt[0] == '0':
self.e.delete(0, 1)

def getvalue(self): # Returns value of the text box
return self.e.get()

def __init__(self, master): # Constructor method
self.txt = 'o' # Global var to work with text box contents
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0, column=0, columnspan=6, pady=3)
self.e.insert(0, '0')
self.e.focus_set() # Sets focus on the text box text area

# Generating Buttons
Button(master, text="=", width=10, command=lambda: self.evaluation('eq')).grid(row=4, column=4, columnspan=2)
Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)
Button(master, text='C', width=3, command=lambda: self.clear1()).grid(row=1, column=5)
Button(master, text="+", width=3, command=lambda: self.action('+')).grid(row=4, column=3)
Button(master, text="x", width=3, command=lambda: self.action('x')).grid(row=2, column=3)
Button(master, text="-", width=3, command=lambda: self.action('-')).grid(row=3, column=3)
Button(master, text="÷", width=3, command=lambda: self.action('÷')).grid(row=1, column=3)
Button(master, text="%", width=3, command=lambda: self.action('%')).grid(row=4, column=2)
Button(master, text="7", width=3, command=lambda: self.action('7')).grid(row=1, column=0)
Button(master, text="8", width=3, command=lambda: self.action('8')).grid(row=1, column=1)
Button(master, text="9", width=3, command=lambda: self.action('9')).grid(row=1, column=2)
Button(master, text="4", width=3, command=lambda: self.action('4')).grid(row=2, column=0)
Button(master, text="5", width=3, command=lambda: self.action('5')).grid(row=2, column=1)
Button(master, text="6", width=3, command=lambda: self.action('6')).grid(row=2, column=2)
Button(master, text="1", width=3, command=lambda: self.action('1')).grid(row=3, column=0)
Button(master, text="2", width=3, command=lambda: self.action('2')).grid(row=3, column=1)
Button(master, text="3", width=3, command=lambda: self.action('3')).grid(row=3, column=2)
Button(master, text="0", width=3, command=lambda: self.action('0')).grid(row=4, column=0)
Button(master, text=".", width=3, command=lambda: self.action('.')).grid(row=4, column=1)
Button(master, text="(", width=3, command=lambda: self.action('(')).grid(row=2, column=4)
Button(master, text=")", width=3, command=lambda: self.action(')')).grid(row=2, column=5)
Button(master, text="√", width=3, command=lambda: self.evaluation('sqroot')).grid(row=3, column=4)
Button(master, text="x²", width=3, command=lambda: self.evaluation('power')).grid(row=3, column=5)

# bind key strokes
self.e.bind('<Key>', lambda evt: self.keyaction(evt))


# Main
root = Tk()
obj = Calc(root) # object instantiated
root.mainloop()


I don't really care for the names of some of the function names and variable names. I like using descriptive names, so names like self.e would have been called self.textbox or something. These things are leftovers from the web copy I found and haven't changed them.



Per request, the original code for this is below.




#-*-coding: utf-8-*-
from Tkinter import *
import math

class calc:
def getandreplace(self):
"""replace x with * and ÷ with /"""

self.expression = self.e.get()
self.newtext=self.expression.replace(self.newdiv,'/')
self.newtext=self.newtext.replace('x','*')

def equals(self):
"""when the equal button is pressed"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.e.delete(0,END)
self.e.insert(0,self.value)

def squareroot(self):
"""squareroot method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqrtval=math.sqrt(self.value)
self.e.delete(0,END)
self.e.insert(0,self.sqrtval)

def square(self):
"""square method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqval=math.pow(self.value,2)
self.e.delete(0,END)
self.e.insert(0,self.sqval)

def clearall(self):
"""when clear button is pressed,clears the text input area"""
self.e.delete(0,END)

def clear1(self):
self.txt=self.e.get()[:-1]
self.e.delete(0,END)
self.e.insert(0,self.txt)

def action(self,argi):
"""pressed button's value is inserted into the end of the text area"""
self.e.insert(END,argi)

def __init__(self,master):
"""Constructor method"""
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0,column=0,columnspan=6,pady=3)
self.e.focus_set() #Sets focus on the input text area

self.div='÷'
self.newdiv=self.div.decode('utf-8')

#Generating Buttons
Button(master,text="=",width=10,command=lambda:self.equals()).grid(row=4, column=4,columnspan=2)
Button(master,text='AC',width=3,command=lambda:self.clearall()).grid(row=1, column=4)
Button(master,text='C',width=3,command=lambda:self.clear1()).grid(row=1, column=5)
Button(master,text="+",width=3,command=lambda:self.action('+')).grid(row=4, column=3)
Button(master,text="x",width=3,command=lambda:self.action('x')).grid(row=2, column=3)
Button(master,text="-",width=3,command=lambda:self.action('-')).grid(row=3, column=3)
Button(master,text="÷",width=3,command=lambda:self.action(self.newdiv)).grid(row=1, column=3)
Button(master,text="%",width=3,command=lambda:self.action('%')).grid(row=4, column=2)
Button(master,text="7",width=3,command=lambda:self.action('7')).grid(row=1, column=0)
Button(master,text="8",width=3,command=lambda:self.action(8)).grid(row=1, column=1)
Button(master,text="9",width=3,command=lambda:self.action(9)).grid(row=1, column=2)
Button(master,text="4",width=3,command=lambda:self.action(4)).grid(row=2, column=0)
Button(master,text="5",width=3,command=lambda:self.action(5)).grid(row=2, column=1)
Button(master,text="6",width=3,command=lambda:self.action(6)).grid(row=2, column=2)
Button(master,text="1",width=3,command=lambda:self.action(1)).grid(row=3, column=0)
Button(master,text="2",width=3,command=lambda:self.action(2)).grid(row=3, column=1)
Button(master,text="3",width=3,command=lambda:self.action(3)).grid(row=3, column=2)
Button(master,text="0",width=3,command=lambda:self.action(0)).grid(row=4, column=0)
Button(master,text=".",width=3,command=lambda:self.action('.')).grid(row=4, column=1)
Button(master,text="(",width=3,command=lambda:self.action('(')).grid(row=2, column=4)
Button(master,text=")",width=3,command=lambda:self.action(')')).grid(row=2, column=5)
Button(master,text="√",width=3,command=lambda:self.squareroot()).grid(row=3, column=4)
Button(master,text="x²",width=3,command=lambda:self.square()).grid(row=3, column=5)
#Main
root = Tk()
obj=calc(root) #object instantiated
root.mainloop()










share|improve this question
























  • For comparison, could you add a link to where you got the original code from?
    – 200_success
    Sep 21 at 1:30










  • @200_success - per your request I included a full copy of the code I got from the web. The link is techinfected.net/2016/02/…
    – Riv
    Sep 21 at 3:07












  • Our convention is to put original code that is not to be reviewed in blockquotes, which is what I did in Rev 6.
    – 200_success
    Sep 21 at 3:14










  • Oh, I was wondering what happened, I thought I messed it up
    – Riv
    Sep 21 at 3:15










  • Any reason to pick Tk verses Kivy?
    – C. Harley
    Sep 25 at 6:11













up vote
4
down vote

favorite
1









up vote
4
down vote

favorite
1






1





I have been programming for a long time. Only recently have I decided to take a stab at Python (I should be working with C# as I am in school for it, but I don't care for Windows, long story).



I was on this site and it showed a source for a calculator. I took it, and put it in PyCharm and started to study. By the time I was done I had changed the source significantly. I had added keyboard binding and reduced a lot of the redundant code in it.



My question is simple: is this code that I wrote efficient from a python standard viewpoint?



# -*-coding: utf-8-*-
# !/usr/bin/python3.5

from tkinter import Tk, Button, Entry, END
import math


class Calc:
def getandreplace(self): # replace x, + and % to symbols that can be used in calculations
# we wont re write this to the text box until we are done with calculations

self.txt = self.e.get() # Get value from text box and assign it to the global txt var
self.txt = self.txt.replace('÷', '/')
self.txt = self.txt.replace('x', '*')
self.txt = self.txt.replace('%', '/100')

def evaluation(self, specfunc): # Evaluate the items in the text box for calculation specfunc = eq, sqroot or power
self.getandreplace()
try:
self.txt = eval(str(self.txt)) # evaluate the expression using the eval function
except SyntaxError:
self.displayinvalid()
else:
if any([specfunc == 'sqroot', specfunc == 'power']): # Square Root and Power are special
self.txt = self.evalspecialfunctions(specfunc)

self.refreshtext()

def displayinvalid(self):
self.e.delete(0, END)
self.e.insert(0, 'Invalid Input!')

def refreshtext(self): # Delete current contents of textbox and replace with our completed evaluatioin
self.e.delete(0, END)
self.e.insert(0, self.txt)

def evalspecialfunctions(self, specfunc): # Calculate square root and power if specfunc is sqroot or power
if specfunc == 'sqroot':
return math.sqrt(float(self.txt))
elif specfunc == 'power':
return math.pow(float(self.txt), 2)

def clearall(self): # AC button pressed on form or 'esc" pressed on keyboard
self.e.delete(0, END)
self.e.insert(0, '0')

def clear1(self, event=None):
# C button press on form or backspace press on keyboard event defined on keyboard press

if event is None:
self.txt = self.e.get()[:-1] # Form backspace done by hand
else:
self.txt = self.getvalue() # No need to manually delete when done from keyboard

self.refreshtext()

def action(self, argi: object): # Number or operator button pressed on form and passed in as argi
self.txt = self.getvalue()

self.stripfirstchar()

self.e.insert(END, argi)

def keyaction(self, event=None): # Key pressed on keyboard which defines event
self.txt = self.getvalue()

if any([event.char.isdigit(), event.char in '/*-+%().']):
self.stripfirstchar()
elif event.char == 'x08':
self.clear1(event)
elif event.char == 'x1b':
self.clearall()
elif event.char == 'r':
self.evaluation('eq')
else:
self.displayinvalid()
return 'break'

def stripfirstchar(self): # Strips leading 0 from text box with first key or button is pressed
if self.txt[0] == '0':
self.e.delete(0, 1)

def getvalue(self): # Returns value of the text box
return self.e.get()

def __init__(self, master): # Constructor method
self.txt = 'o' # Global var to work with text box contents
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0, column=0, columnspan=6, pady=3)
self.e.insert(0, '0')
self.e.focus_set() # Sets focus on the text box text area

# Generating Buttons
Button(master, text="=", width=10, command=lambda: self.evaluation('eq')).grid(row=4, column=4, columnspan=2)
Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)
Button(master, text='C', width=3, command=lambda: self.clear1()).grid(row=1, column=5)
Button(master, text="+", width=3, command=lambda: self.action('+')).grid(row=4, column=3)
Button(master, text="x", width=3, command=lambda: self.action('x')).grid(row=2, column=3)
Button(master, text="-", width=3, command=lambda: self.action('-')).grid(row=3, column=3)
Button(master, text="÷", width=3, command=lambda: self.action('÷')).grid(row=1, column=3)
Button(master, text="%", width=3, command=lambda: self.action('%')).grid(row=4, column=2)
Button(master, text="7", width=3, command=lambda: self.action('7')).grid(row=1, column=0)
Button(master, text="8", width=3, command=lambda: self.action('8')).grid(row=1, column=1)
Button(master, text="9", width=3, command=lambda: self.action('9')).grid(row=1, column=2)
Button(master, text="4", width=3, command=lambda: self.action('4')).grid(row=2, column=0)
Button(master, text="5", width=3, command=lambda: self.action('5')).grid(row=2, column=1)
Button(master, text="6", width=3, command=lambda: self.action('6')).grid(row=2, column=2)
Button(master, text="1", width=3, command=lambda: self.action('1')).grid(row=3, column=0)
Button(master, text="2", width=3, command=lambda: self.action('2')).grid(row=3, column=1)
Button(master, text="3", width=3, command=lambda: self.action('3')).grid(row=3, column=2)
Button(master, text="0", width=3, command=lambda: self.action('0')).grid(row=4, column=0)
Button(master, text=".", width=3, command=lambda: self.action('.')).grid(row=4, column=1)
Button(master, text="(", width=3, command=lambda: self.action('(')).grid(row=2, column=4)
Button(master, text=")", width=3, command=lambda: self.action(')')).grid(row=2, column=5)
Button(master, text="√", width=3, command=lambda: self.evaluation('sqroot')).grid(row=3, column=4)
Button(master, text="x²", width=3, command=lambda: self.evaluation('power')).grid(row=3, column=5)

# bind key strokes
self.e.bind('<Key>', lambda evt: self.keyaction(evt))


# Main
root = Tk()
obj = Calc(root) # object instantiated
root.mainloop()


I don't really care for the names of some of the function names and variable names. I like using descriptive names, so names like self.e would have been called self.textbox or something. These things are leftovers from the web copy I found and haven't changed them.



Per request, the original code for this is below.




#-*-coding: utf-8-*-
from Tkinter import *
import math

class calc:
def getandreplace(self):
"""replace x with * and ÷ with /"""

self.expression = self.e.get()
self.newtext=self.expression.replace(self.newdiv,'/')
self.newtext=self.newtext.replace('x','*')

def equals(self):
"""when the equal button is pressed"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.e.delete(0,END)
self.e.insert(0,self.value)

def squareroot(self):
"""squareroot method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqrtval=math.sqrt(self.value)
self.e.delete(0,END)
self.e.insert(0,self.sqrtval)

def square(self):
"""square method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqval=math.pow(self.value,2)
self.e.delete(0,END)
self.e.insert(0,self.sqval)

def clearall(self):
"""when clear button is pressed,clears the text input area"""
self.e.delete(0,END)

def clear1(self):
self.txt=self.e.get()[:-1]
self.e.delete(0,END)
self.e.insert(0,self.txt)

def action(self,argi):
"""pressed button's value is inserted into the end of the text area"""
self.e.insert(END,argi)

def __init__(self,master):
"""Constructor method"""
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0,column=0,columnspan=6,pady=3)
self.e.focus_set() #Sets focus on the input text area

self.div='÷'
self.newdiv=self.div.decode('utf-8')

#Generating Buttons
Button(master,text="=",width=10,command=lambda:self.equals()).grid(row=4, column=4,columnspan=2)
Button(master,text='AC',width=3,command=lambda:self.clearall()).grid(row=1, column=4)
Button(master,text='C',width=3,command=lambda:self.clear1()).grid(row=1, column=5)
Button(master,text="+",width=3,command=lambda:self.action('+')).grid(row=4, column=3)
Button(master,text="x",width=3,command=lambda:self.action('x')).grid(row=2, column=3)
Button(master,text="-",width=3,command=lambda:self.action('-')).grid(row=3, column=3)
Button(master,text="÷",width=3,command=lambda:self.action(self.newdiv)).grid(row=1, column=3)
Button(master,text="%",width=3,command=lambda:self.action('%')).grid(row=4, column=2)
Button(master,text="7",width=3,command=lambda:self.action('7')).grid(row=1, column=0)
Button(master,text="8",width=3,command=lambda:self.action(8)).grid(row=1, column=1)
Button(master,text="9",width=3,command=lambda:self.action(9)).grid(row=1, column=2)
Button(master,text="4",width=3,command=lambda:self.action(4)).grid(row=2, column=0)
Button(master,text="5",width=3,command=lambda:self.action(5)).grid(row=2, column=1)
Button(master,text="6",width=3,command=lambda:self.action(6)).grid(row=2, column=2)
Button(master,text="1",width=3,command=lambda:self.action(1)).grid(row=3, column=0)
Button(master,text="2",width=3,command=lambda:self.action(2)).grid(row=3, column=1)
Button(master,text="3",width=3,command=lambda:self.action(3)).grid(row=3, column=2)
Button(master,text="0",width=3,command=lambda:self.action(0)).grid(row=4, column=0)
Button(master,text=".",width=3,command=lambda:self.action('.')).grid(row=4, column=1)
Button(master,text="(",width=3,command=lambda:self.action('(')).grid(row=2, column=4)
Button(master,text=")",width=3,command=lambda:self.action(')')).grid(row=2, column=5)
Button(master,text="√",width=3,command=lambda:self.squareroot()).grid(row=3, column=4)
Button(master,text="x²",width=3,command=lambda:self.square()).grid(row=3, column=5)
#Main
root = Tk()
obj=calc(root) #object instantiated
root.mainloop()










share|improve this question















I have been programming for a long time. Only recently have I decided to take a stab at Python (I should be working with C# as I am in school for it, but I don't care for Windows, long story).



I was on this site and it showed a source for a calculator. I took it, and put it in PyCharm and started to study. By the time I was done I had changed the source significantly. I had added keyboard binding and reduced a lot of the redundant code in it.



My question is simple: is this code that I wrote efficient from a python standard viewpoint?



# -*-coding: utf-8-*-
# !/usr/bin/python3.5

from tkinter import Tk, Button, Entry, END
import math


class Calc:
def getandreplace(self): # replace x, + and % to symbols that can be used in calculations
# we wont re write this to the text box until we are done with calculations

self.txt = self.e.get() # Get value from text box and assign it to the global txt var
self.txt = self.txt.replace('÷', '/')
self.txt = self.txt.replace('x', '*')
self.txt = self.txt.replace('%', '/100')

def evaluation(self, specfunc): # Evaluate the items in the text box for calculation specfunc = eq, sqroot or power
self.getandreplace()
try:
self.txt = eval(str(self.txt)) # evaluate the expression using the eval function
except SyntaxError:
self.displayinvalid()
else:
if any([specfunc == 'sqroot', specfunc == 'power']): # Square Root and Power are special
self.txt = self.evalspecialfunctions(specfunc)

self.refreshtext()

def displayinvalid(self):
self.e.delete(0, END)
self.e.insert(0, 'Invalid Input!')

def refreshtext(self): # Delete current contents of textbox and replace with our completed evaluatioin
self.e.delete(0, END)
self.e.insert(0, self.txt)

def evalspecialfunctions(self, specfunc): # Calculate square root and power if specfunc is sqroot or power
if specfunc == 'sqroot':
return math.sqrt(float(self.txt))
elif specfunc == 'power':
return math.pow(float(self.txt), 2)

def clearall(self): # AC button pressed on form or 'esc" pressed on keyboard
self.e.delete(0, END)
self.e.insert(0, '0')

def clear1(self, event=None):
# C button press on form or backspace press on keyboard event defined on keyboard press

if event is None:
self.txt = self.e.get()[:-1] # Form backspace done by hand
else:
self.txt = self.getvalue() # No need to manually delete when done from keyboard

self.refreshtext()

def action(self, argi: object): # Number or operator button pressed on form and passed in as argi
self.txt = self.getvalue()

self.stripfirstchar()

self.e.insert(END, argi)

def keyaction(self, event=None): # Key pressed on keyboard which defines event
self.txt = self.getvalue()

if any([event.char.isdigit(), event.char in '/*-+%().']):
self.stripfirstchar()
elif event.char == 'x08':
self.clear1(event)
elif event.char == 'x1b':
self.clearall()
elif event.char == 'r':
self.evaluation('eq')
else:
self.displayinvalid()
return 'break'

def stripfirstchar(self): # Strips leading 0 from text box with first key or button is pressed
if self.txt[0] == '0':
self.e.delete(0, 1)

def getvalue(self): # Returns value of the text box
return self.e.get()

def __init__(self, master): # Constructor method
self.txt = 'o' # Global var to work with text box contents
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0, column=0, columnspan=6, pady=3)
self.e.insert(0, '0')
self.e.focus_set() # Sets focus on the text box text area

# Generating Buttons
Button(master, text="=", width=10, command=lambda: self.evaluation('eq')).grid(row=4, column=4, columnspan=2)
Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)
Button(master, text='C', width=3, command=lambda: self.clear1()).grid(row=1, column=5)
Button(master, text="+", width=3, command=lambda: self.action('+')).grid(row=4, column=3)
Button(master, text="x", width=3, command=lambda: self.action('x')).grid(row=2, column=3)
Button(master, text="-", width=3, command=lambda: self.action('-')).grid(row=3, column=3)
Button(master, text="÷", width=3, command=lambda: self.action('÷')).grid(row=1, column=3)
Button(master, text="%", width=3, command=lambda: self.action('%')).grid(row=4, column=2)
Button(master, text="7", width=3, command=lambda: self.action('7')).grid(row=1, column=0)
Button(master, text="8", width=3, command=lambda: self.action('8')).grid(row=1, column=1)
Button(master, text="9", width=3, command=lambda: self.action('9')).grid(row=1, column=2)
Button(master, text="4", width=3, command=lambda: self.action('4')).grid(row=2, column=0)
Button(master, text="5", width=3, command=lambda: self.action('5')).grid(row=2, column=1)
Button(master, text="6", width=3, command=lambda: self.action('6')).grid(row=2, column=2)
Button(master, text="1", width=3, command=lambda: self.action('1')).grid(row=3, column=0)
Button(master, text="2", width=3, command=lambda: self.action('2')).grid(row=3, column=1)
Button(master, text="3", width=3, command=lambda: self.action('3')).grid(row=3, column=2)
Button(master, text="0", width=3, command=lambda: self.action('0')).grid(row=4, column=0)
Button(master, text=".", width=3, command=lambda: self.action('.')).grid(row=4, column=1)
Button(master, text="(", width=3, command=lambda: self.action('(')).grid(row=2, column=4)
Button(master, text=")", width=3, command=lambda: self.action(')')).grid(row=2, column=5)
Button(master, text="√", width=3, command=lambda: self.evaluation('sqroot')).grid(row=3, column=4)
Button(master, text="x²", width=3, command=lambda: self.evaluation('power')).grid(row=3, column=5)

# bind key strokes
self.e.bind('<Key>', lambda evt: self.keyaction(evt))


# Main
root = Tk()
obj = Calc(root) # object instantiated
root.mainloop()


I don't really care for the names of some of the function names and variable names. I like using descriptive names, so names like self.e would have been called self.textbox or something. These things are leftovers from the web copy I found and haven't changed them.



Per request, the original code for this is below.




#-*-coding: utf-8-*-
from Tkinter import *
import math

class calc:
def getandreplace(self):
"""replace x with * and ÷ with /"""

self.expression = self.e.get()
self.newtext=self.expression.replace(self.newdiv,'/')
self.newtext=self.newtext.replace('x','*')

def equals(self):
"""when the equal button is pressed"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.e.delete(0,END)
self.e.insert(0,self.value)

def squareroot(self):
"""squareroot method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqrtval=math.sqrt(self.value)
self.e.delete(0,END)
self.e.insert(0,self.sqrtval)

def square(self):
"""square method"""

self.getandreplace()
try:
self.value= eval(self.newtext) #evaluate the expression using the eval function
except SyntaxError or NameErrror:
self.e.delete(0,END)
self.e.insert(0,'Invalid Input!')
else:
self.sqval=math.pow(self.value,2)
self.e.delete(0,END)
self.e.insert(0,self.sqval)

def clearall(self):
"""when clear button is pressed,clears the text input area"""
self.e.delete(0,END)

def clear1(self):
self.txt=self.e.get()[:-1]
self.e.delete(0,END)
self.e.insert(0,self.txt)

def action(self,argi):
"""pressed button's value is inserted into the end of the text area"""
self.e.insert(END,argi)

def __init__(self,master):
"""Constructor method"""
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0,column=0,columnspan=6,pady=3)
self.e.focus_set() #Sets focus on the input text area

self.div='÷'
self.newdiv=self.div.decode('utf-8')

#Generating Buttons
Button(master,text="=",width=10,command=lambda:self.equals()).grid(row=4, column=4,columnspan=2)
Button(master,text='AC',width=3,command=lambda:self.clearall()).grid(row=1, column=4)
Button(master,text='C',width=3,command=lambda:self.clear1()).grid(row=1, column=5)
Button(master,text="+",width=3,command=lambda:self.action('+')).grid(row=4, column=3)
Button(master,text="x",width=3,command=lambda:self.action('x')).grid(row=2, column=3)
Button(master,text="-",width=3,command=lambda:self.action('-')).grid(row=3, column=3)
Button(master,text="÷",width=3,command=lambda:self.action(self.newdiv)).grid(row=1, column=3)
Button(master,text="%",width=3,command=lambda:self.action('%')).grid(row=4, column=2)
Button(master,text="7",width=3,command=lambda:self.action('7')).grid(row=1, column=0)
Button(master,text="8",width=3,command=lambda:self.action(8)).grid(row=1, column=1)
Button(master,text="9",width=3,command=lambda:self.action(9)).grid(row=1, column=2)
Button(master,text="4",width=3,command=lambda:self.action(4)).grid(row=2, column=0)
Button(master,text="5",width=3,command=lambda:self.action(5)).grid(row=2, column=1)
Button(master,text="6",width=3,command=lambda:self.action(6)).grid(row=2, column=2)
Button(master,text="1",width=3,command=lambda:self.action(1)).grid(row=3, column=0)
Button(master,text="2",width=3,command=lambda:self.action(2)).grid(row=3, column=1)
Button(master,text="3",width=3,command=lambda:self.action(3)).grid(row=3, column=2)
Button(master,text="0",width=3,command=lambda:self.action(0)).grid(row=4, column=0)
Button(master,text=".",width=3,command=lambda:self.action('.')).grid(row=4, column=1)
Button(master,text="(",width=3,command=lambda:self.action('(')).grid(row=2, column=4)
Button(master,text=")",width=3,command=lambda:self.action(')')).grid(row=2, column=5)
Button(master,text="√",width=3,command=lambda:self.squareroot()).grid(row=3, column=4)
Button(master,text="x²",width=3,command=lambda:self.square()).grid(row=3, column=5)
#Main
root = Tk()
obj=calc(root) #object instantiated
root.mainloop()







python beginner python-3.x calculator tkinter






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 21 at 13:27

























asked Sep 21 at 0:13









Riv

212




212












  • For comparison, could you add a link to where you got the original code from?
    – 200_success
    Sep 21 at 1:30










  • @200_success - per your request I included a full copy of the code I got from the web. The link is techinfected.net/2016/02/…
    – Riv
    Sep 21 at 3:07












  • Our convention is to put original code that is not to be reviewed in blockquotes, which is what I did in Rev 6.
    – 200_success
    Sep 21 at 3:14










  • Oh, I was wondering what happened, I thought I messed it up
    – Riv
    Sep 21 at 3:15










  • Any reason to pick Tk verses Kivy?
    – C. Harley
    Sep 25 at 6:11


















  • For comparison, could you add a link to where you got the original code from?
    – 200_success
    Sep 21 at 1:30










  • @200_success - per your request I included a full copy of the code I got from the web. The link is techinfected.net/2016/02/…
    – Riv
    Sep 21 at 3:07












  • Our convention is to put original code that is not to be reviewed in blockquotes, which is what I did in Rev 6.
    – 200_success
    Sep 21 at 3:14










  • Oh, I was wondering what happened, I thought I messed it up
    – Riv
    Sep 21 at 3:15










  • Any reason to pick Tk verses Kivy?
    – C. Harley
    Sep 25 at 6:11
















For comparison, could you add a link to where you got the original code from?
– 200_success
Sep 21 at 1:30




For comparison, could you add a link to where you got the original code from?
– 200_success
Sep 21 at 1:30












@200_success - per your request I included a full copy of the code I got from the web. The link is techinfected.net/2016/02/…
– Riv
Sep 21 at 3:07






@200_success - per your request I included a full copy of the code I got from the web. The link is techinfected.net/2016/02/…
– Riv
Sep 21 at 3:07














Our convention is to put original code that is not to be reviewed in blockquotes, which is what I did in Rev 6.
– 200_success
Sep 21 at 3:14




Our convention is to put original code that is not to be reviewed in blockquotes, which is what I did in Rev 6.
– 200_success
Sep 21 at 3:14












Oh, I was wondering what happened, I thought I messed it up
– Riv
Sep 21 at 3:15




Oh, I was wondering what happened, I thought I messed it up
– Riv
Sep 21 at 3:15












Any reason to pick Tk verses Kivy?
– C. Harley
Sep 25 at 6:11




Any reason to pick Tk verses Kivy?
– C. Harley
Sep 25 at 6:11










1 Answer
1






active

oldest

votes

















up vote
1
down vote














When I was learning Python, I found The Zen of Python quite helpful.



Formatting



I agree about renaming self.e to self.textbox. Descriptive names are generally better, unless this results in an overly long and unwieldy name. In addition to that there are a few more formatting issues. (You may find the PEP 8 Style Guide helpful)





  • Redundant comments. (See https://www.python.org/dev/peps/pep-0008/#inline-comments) For example, in this line:



    obj = Calc(root)  # object instantiated


    The comment is not particularly helpful here as we can see that Calc(root) clearly instanciates a new Calc object. The comment on the following line, on the other hand, is more helpful:



    self.txt = self.getvalue()  # No need to manually delete when done from keyboard


  • Method names that do not use underscores to separate words. For example, instead of stripfirstchar we should have strip_first_char


  • While I could not find any mention of this in PEP 8, in my experience a class's __init__ method is almost always placed before any other methods.



  • Use docstrings instead of comments to document entire functions. For example:



    def getvalue(self):  # Returns value of the text box
    return self.e.get()


    becomes



    def getvalue(self):
    """Returns value of the text box"""
    return self.e.get()



  • There is no need to wrap a method in a lambda when it be used on its own. For example, this:



    Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)


    can be rewritten as:



    Button(master, text='AC', width=3, command=self.clearall).grid(row=1, column=4)



  • Use or instead of any when all conditions are known beforehand. For example:



    any([event.char.isdigit(), event.char in '/*-+%().'])


    can be rewritten as



    event.char.isdigit() or event.char in '/*-+%().'



Practical Issues




  • The C button in the GUI does not work properly. This is because of an indentation issue in the method clear1. The call to self.refresh_text should be outside the else block.



  • If I remove all characters in the text, then try to type something, the program will raise an IndexError. This can be fixed by changing the condition in the if statement in the strip_first_char method to



    len(self.txt) > 0 and self.txt[0] == '0'


  • Open the window only if this program is being run as __main__. Check if __name__ == '__main__' before opening the window. This is to be sure that this will not happen if someone is trying to use this program as a library. (e.g. embedding this calculator in another application)


  • % should be a special function. As it is, if I type 1%1, the program will interpret this as 1/1001 when it should cause some sort of syntax error. There are other ways to fix this, but this seems to be both the easiest to implement and the way most calculators I have seen handle this.





Using eval is usually a very bad idea



I see no way to remove eval from this code without significant changes. Letting the user type their math directly makes it harder to not use eval here, otherwise you could store the math in an internal, easy to parse form, and convert that to a more user-friendly string before displaying it, but this would require rewriting almost all of the program.






share|improve this answer








New contributor




TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.


















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204094%2fpython-3-calculator-with-tkinter%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote














    When I was learning Python, I found The Zen of Python quite helpful.



    Formatting



    I agree about renaming self.e to self.textbox. Descriptive names are generally better, unless this results in an overly long and unwieldy name. In addition to that there are a few more formatting issues. (You may find the PEP 8 Style Guide helpful)





    • Redundant comments. (See https://www.python.org/dev/peps/pep-0008/#inline-comments) For example, in this line:



      obj = Calc(root)  # object instantiated


      The comment is not particularly helpful here as we can see that Calc(root) clearly instanciates a new Calc object. The comment on the following line, on the other hand, is more helpful:



      self.txt = self.getvalue()  # No need to manually delete when done from keyboard


    • Method names that do not use underscores to separate words. For example, instead of stripfirstchar we should have strip_first_char


    • While I could not find any mention of this in PEP 8, in my experience a class's __init__ method is almost always placed before any other methods.



    • Use docstrings instead of comments to document entire functions. For example:



      def getvalue(self):  # Returns value of the text box
      return self.e.get()


      becomes



      def getvalue(self):
      """Returns value of the text box"""
      return self.e.get()



    • There is no need to wrap a method in a lambda when it be used on its own. For example, this:



      Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)


      can be rewritten as:



      Button(master, text='AC', width=3, command=self.clearall).grid(row=1, column=4)



    • Use or instead of any when all conditions are known beforehand. For example:



      any([event.char.isdigit(), event.char in '/*-+%().'])


      can be rewritten as



      event.char.isdigit() or event.char in '/*-+%().'



    Practical Issues




    • The C button in the GUI does not work properly. This is because of an indentation issue in the method clear1. The call to self.refresh_text should be outside the else block.



    • If I remove all characters in the text, then try to type something, the program will raise an IndexError. This can be fixed by changing the condition in the if statement in the strip_first_char method to



      len(self.txt) > 0 and self.txt[0] == '0'


    • Open the window only if this program is being run as __main__. Check if __name__ == '__main__' before opening the window. This is to be sure that this will not happen if someone is trying to use this program as a library. (e.g. embedding this calculator in another application)


    • % should be a special function. As it is, if I type 1%1, the program will interpret this as 1/1001 when it should cause some sort of syntax error. There are other ways to fix this, but this seems to be both the easiest to implement and the way most calculators I have seen handle this.





    Using eval is usually a very bad idea



    I see no way to remove eval from this code without significant changes. Letting the user type their math directly makes it harder to not use eval here, otherwise you could store the math in an internal, easy to parse form, and convert that to a more user-friendly string before displaying it, but this would require rewriting almost all of the program.






    share|improve this answer








    New contributor




    TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.






















      up vote
      1
      down vote














      When I was learning Python, I found The Zen of Python quite helpful.



      Formatting



      I agree about renaming self.e to self.textbox. Descriptive names are generally better, unless this results in an overly long and unwieldy name. In addition to that there are a few more formatting issues. (You may find the PEP 8 Style Guide helpful)





      • Redundant comments. (See https://www.python.org/dev/peps/pep-0008/#inline-comments) For example, in this line:



        obj = Calc(root)  # object instantiated


        The comment is not particularly helpful here as we can see that Calc(root) clearly instanciates a new Calc object. The comment on the following line, on the other hand, is more helpful:



        self.txt = self.getvalue()  # No need to manually delete when done from keyboard


      • Method names that do not use underscores to separate words. For example, instead of stripfirstchar we should have strip_first_char


      • While I could not find any mention of this in PEP 8, in my experience a class's __init__ method is almost always placed before any other methods.



      • Use docstrings instead of comments to document entire functions. For example:



        def getvalue(self):  # Returns value of the text box
        return self.e.get()


        becomes



        def getvalue(self):
        """Returns value of the text box"""
        return self.e.get()



      • There is no need to wrap a method in a lambda when it be used on its own. For example, this:



        Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)


        can be rewritten as:



        Button(master, text='AC', width=3, command=self.clearall).grid(row=1, column=4)



      • Use or instead of any when all conditions are known beforehand. For example:



        any([event.char.isdigit(), event.char in '/*-+%().'])


        can be rewritten as



        event.char.isdigit() or event.char in '/*-+%().'



      Practical Issues




      • The C button in the GUI does not work properly. This is because of an indentation issue in the method clear1. The call to self.refresh_text should be outside the else block.



      • If I remove all characters in the text, then try to type something, the program will raise an IndexError. This can be fixed by changing the condition in the if statement in the strip_first_char method to



        len(self.txt) > 0 and self.txt[0] == '0'


      • Open the window only if this program is being run as __main__. Check if __name__ == '__main__' before opening the window. This is to be sure that this will not happen if someone is trying to use this program as a library. (e.g. embedding this calculator in another application)


      • % should be a special function. As it is, if I type 1%1, the program will interpret this as 1/1001 when it should cause some sort of syntax error. There are other ways to fix this, but this seems to be both the easiest to implement and the way most calculators I have seen handle this.





      Using eval is usually a very bad idea



      I see no way to remove eval from this code without significant changes. Letting the user type their math directly makes it harder to not use eval here, otherwise you could store the math in an internal, easy to parse form, and convert that to a more user-friendly string before displaying it, but this would require rewriting almost all of the program.






      share|improve this answer








      New contributor




      TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.




















        up vote
        1
        down vote










        up vote
        1
        down vote










        When I was learning Python, I found The Zen of Python quite helpful.



        Formatting



        I agree about renaming self.e to self.textbox. Descriptive names are generally better, unless this results in an overly long and unwieldy name. In addition to that there are a few more formatting issues. (You may find the PEP 8 Style Guide helpful)





        • Redundant comments. (See https://www.python.org/dev/peps/pep-0008/#inline-comments) For example, in this line:



          obj = Calc(root)  # object instantiated


          The comment is not particularly helpful here as we can see that Calc(root) clearly instanciates a new Calc object. The comment on the following line, on the other hand, is more helpful:



          self.txt = self.getvalue()  # No need to manually delete when done from keyboard


        • Method names that do not use underscores to separate words. For example, instead of stripfirstchar we should have strip_first_char


        • While I could not find any mention of this in PEP 8, in my experience a class's __init__ method is almost always placed before any other methods.



        • Use docstrings instead of comments to document entire functions. For example:



          def getvalue(self):  # Returns value of the text box
          return self.e.get()


          becomes



          def getvalue(self):
          """Returns value of the text box"""
          return self.e.get()



        • There is no need to wrap a method in a lambda when it be used on its own. For example, this:



          Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)


          can be rewritten as:



          Button(master, text='AC', width=3, command=self.clearall).grid(row=1, column=4)



        • Use or instead of any when all conditions are known beforehand. For example:



          any([event.char.isdigit(), event.char in '/*-+%().'])


          can be rewritten as



          event.char.isdigit() or event.char in '/*-+%().'



        Practical Issues




        • The C button in the GUI does not work properly. This is because of an indentation issue in the method clear1. The call to self.refresh_text should be outside the else block.



        • If I remove all characters in the text, then try to type something, the program will raise an IndexError. This can be fixed by changing the condition in the if statement in the strip_first_char method to



          len(self.txt) > 0 and self.txt[0] == '0'


        • Open the window only if this program is being run as __main__. Check if __name__ == '__main__' before opening the window. This is to be sure that this will not happen if someone is trying to use this program as a library. (e.g. embedding this calculator in another application)


        • % should be a special function. As it is, if I type 1%1, the program will interpret this as 1/1001 when it should cause some sort of syntax error. There are other ways to fix this, but this seems to be both the easiest to implement and the way most calculators I have seen handle this.





        Using eval is usually a very bad idea



        I see no way to remove eval from this code without significant changes. Letting the user type their math directly makes it harder to not use eval here, otherwise you could store the math in an internal, easy to parse form, and convert that to a more user-friendly string before displaying it, but this would require rewriting almost all of the program.






        share|improve this answer








        New contributor




        TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.










        When I was learning Python, I found The Zen of Python quite helpful.



        Formatting



        I agree about renaming self.e to self.textbox. Descriptive names are generally better, unless this results in an overly long and unwieldy name. In addition to that there are a few more formatting issues. (You may find the PEP 8 Style Guide helpful)





        • Redundant comments. (See https://www.python.org/dev/peps/pep-0008/#inline-comments) For example, in this line:



          obj = Calc(root)  # object instantiated


          The comment is not particularly helpful here as we can see that Calc(root) clearly instanciates a new Calc object. The comment on the following line, on the other hand, is more helpful:



          self.txt = self.getvalue()  # No need to manually delete when done from keyboard


        • Method names that do not use underscores to separate words. For example, instead of stripfirstchar we should have strip_first_char


        • While I could not find any mention of this in PEP 8, in my experience a class's __init__ method is almost always placed before any other methods.



        • Use docstrings instead of comments to document entire functions. For example:



          def getvalue(self):  # Returns value of the text box
          return self.e.get()


          becomes



          def getvalue(self):
          """Returns value of the text box"""
          return self.e.get()



        • There is no need to wrap a method in a lambda when it be used on its own. For example, this:



          Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)


          can be rewritten as:



          Button(master, text='AC', width=3, command=self.clearall).grid(row=1, column=4)



        • Use or instead of any when all conditions are known beforehand. For example:



          any([event.char.isdigit(), event.char in '/*-+%().'])


          can be rewritten as



          event.char.isdigit() or event.char in '/*-+%().'



        Practical Issues




        • The C button in the GUI does not work properly. This is because of an indentation issue in the method clear1. The call to self.refresh_text should be outside the else block.



        • If I remove all characters in the text, then try to type something, the program will raise an IndexError. This can be fixed by changing the condition in the if statement in the strip_first_char method to



          len(self.txt) > 0 and self.txt[0] == '0'


        • Open the window only if this program is being run as __main__. Check if __name__ == '__main__' before opening the window. This is to be sure that this will not happen if someone is trying to use this program as a library. (e.g. embedding this calculator in another application)


        • % should be a special function. As it is, if I type 1%1, the program will interpret this as 1/1001 when it should cause some sort of syntax error. There are other ways to fix this, but this seems to be both the easiest to implement and the way most calculators I have seen handle this.





        Using eval is usually a very bad idea



        I see no way to remove eval from this code without significant changes. Letting the user type their math directly makes it harder to not use eval here, otherwise you could store the math in an internal, easy to parse form, and convert that to a more user-friendly string before displaying it, but this would require rewriting almost all of the program.







        share|improve this answer








        New contributor




        TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        share|improve this answer



        share|improve this answer






        New contributor




        TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        answered 19 mins ago









        TheGreatGeek

        112




        112




        New contributor




        TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.





        New contributor





        TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.






        TheGreatGeek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204094%2fpython-3-calculator-with-tkinter%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            404 Error Contact Form 7 ajax form submitting

            How to know if a Active Directory user can login interactively

            Refactoring coordinates for Minecraft Pi buildings written in Python