# Build an entire RPN calculator using callback factories for the function keys
# Rhys Price Jones, November 2014
# An RPN calculator uses a stack.  At any stage you can:
#    enter a number -- push it on the stack using the Enter key
#    replace the top two members of the stack with the result of applying
#      an operation to them.  Use the + - or * key
# to calculate 3 * (4 + 5), all you need do is
# 3 Enter 4 Enter 5 Enter * +

import tkinter as TK
# Make prefix versions of the infix operators
add = lambda x,y: x+y
mul = lambda x,y: x*y
sub = lambda x,y: x-y
ops={'+':add,'-':sub,'*':mul}

class Calculator(TK.Tk):
    def __init__(self):
        TK.Tk.__init__(self)
        self.title("Calculator Key Interface")
        self.grid()
        self.startingNewValue = True
        self.value = 0
        self.stack = []        
        self.createWidgets()
        
    def createWidgets(self):
        self.displayContent=TK.StringVar()
        self.displayContent.set('Your number will appear here')
        self.display = TK.Entry(self, textvariable = self.displayContent)
        self.display.grid(row=0, columnspan=3, sticky=TK.E)
        b=TK.Button(self, text='0', command=self.makeFunction(0))
        b.grid(row = 4, columnspan=3, sticky=TK.E+TK.W)
        for i in range(1,10):
            b = TK.Button(self, text=str(i), command=self.makeFunction(i))
            b.grid(row=1+(i-1)/3, column = (i-1)%3, sticky=TK.E+TK.W)
        i = 0
        for op in ops.keys():
            b = TK.Button(self, text=op, command=self.doIt(ops[op]))
            b.grid(row=5+i/3, column=i%3, sticky=TK.E+TK.W)
            i+=1
        b = TK.Button(self, text='Enter', command=self.pushCurrentValue)
        b.grid(row=7, columnspan=3, sticky=TK.E+TK.W)

    def makeFunction(self, i):
        def result():
            if self.startingNewValue:
                self.value = 0
                self.displayContent.set('')
                self.startingNewValue = False
            self.value = self.value*10 + i
            self.displayContent.set(str(self.value))
        return result

    def doIt(self, op):   # factory for + - * callbacks
        def myAction():
            secondOperand = self.stack.pop()
            firstOperand = self.stack.pop()
            self.value = apply(op, (firstOperand, secondOperand))
            self.pushCurrentValue() #self.stack.append(self.value)            
        return myAction
 
    def pushCurrentValue(self):   # just one callback for Enter
        self.stack.append(self.value)
        self.displayContent.set(str(self.value))
        self.startingNewValue = True

        
        
        
        
app=Calculator()
app.mainloop()
     