Calculadora simple escrita en Python

Ahora les dejo el código fuente de una calculadora simple escrita en Python con wxPython. El objetivo principal de este artículo es mostrar nuevamente la simplicidad de Python en el desarrollo de aplicaciones inclusive con interfaz gráfica. La aplicación tiene fallas pero puedes tomar el código y modificarlo como gustes.





Captura de pantalla



#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
#
#  Calculadora.py
#  


__license__ = "GPL"


import sys
import math
import wx



class Calculator(object):
    """Clase para realizar operaciones aritmeticas.
    """
    def __init__(self):
        self.operators = {"+": self.Sum,
                          "-": self.Rest,
                          "*": self.Mult,
                          "/": self.Div,
                          u"±": self.Negative,
                          u"x²": self.Square,
                          u"¹/x": self.OneInX,
                          u"√": self.Squareroot,
                          u"%": self.Percent,
                          u"C": self.Clear
                          }
        self.arith_operators = {"+": self.Sum,
                                "-": self.Rest,
                                "*": self.Mult,
                                "/": self.Div
                                }
        self.espec_operators = {u"±": self.Negative,
                                u"x²": self.Square,
                                u"¹/x": self.OneInX,
                                u"√": self.Squareroot,
                                u"%": self.Percent,
                                u"C": self.Clear}
        
        self.expression = []
        self.expression_string = u""
        self.result = 0.0
    
    def IsNumber(self, string):
        try:
            float(string)
        except ValueError:
            return False
        return True
        
    def Sum(self, num_1, num_2):
        """
        Sumar: num_1 + num_2
        """
        return num_1 + num_2
    
    def Rest(self, num_1, num_2):
        """
        Restar: num_1 - num_2
        """
        return num_1 - num_2
    
    def Mult(self, num_1, num_2):
        """
        Multiplicar: num_1 * num_2
        """
        return num_1 * num_2
    
    def Div(self, num_1, num_2):
        """
        Dividir: num_1 / num_2
        """
        return num_1 / num_2
    
    def Negative(self, num):
        """
        Convirte el numero positivo en negativo y viceversa.
        """
        return -num
    
    def Square(self, num):
        """
        Retorna el cuadrado del numero indicado.
        """
        return num * num
    
    def Squareroot(self, num):
        """
        Retorna la raiz cuadrada del numero indicado.
        Para tal fin utilizamos la funcion sqrt del modulo math.
        """
        return math.sqrt(num)
    
    def OneInX(self, num):
        """
        Dividimos el numero 1 entre el numero indicado.
        """
        return 1.0 / num
    
    def Percent(self, num, percent=100):
        """
        Calculamos el porcentaje indicado al numero.
        """
        return (num / 100.0) * percent
    
    def Clear(self):
        """
        Limpia los datos introduccidos.
        """
        self.expression = []
    
    def Calculate(self, num_1, num_2, operator):
        """
        Calcula num_1 y num_2 segun el operador indicado.
        """
        expression = "%s %s %s" % (num_1, operator, num_2)
        exec "out = %s" % expression
        return out
    
    def Reduce(self, choice):
        """
        Resuelve las operaciones de lista hasta 
        que no quede mas elementos. Retorna el resultado.
        """
        out = 0.0
        result = float(choice[0])
        for n in range(0, len(choice), 2):
            number_1 = result
            
            try:
                operator = choice[n+1]
            except IndexError:
                break
                
            try:
                number_2 = float(choice[n+2])
            except (IndexError, ValueError):
                number_2 = number_1
            
            try:
                if choice[n+3] == "%":
                    number_2 = self.Percent(num_1, num2)
            except IndexError:
                pass
                
            # si es un operador especial
            if operator in self.espec_operators.keys():
                result = self.operators[operator](number_1)
            else:
                result = self.Calculate(number_1, number_2, operator)
            out += result
        return out
    
    def Solve(self):
        """
        Resuelve la expression.
        """
        self.expression_string = unicode.join(u" ", self.expression)
        try:
            exec("self.result = %s" % self.expression_string)
        except SyntaxError as e:
            print(e)
        return self.result
    
    
    
    
#-------------------------------------------------------------------
# Intefaz gráfica
#-------------------------------------------------------------------

class DlgCalculator(wx.Dialog, Calculator):
    """Interfaz grafica de una calculadora simple.
    """
    def __init__(self, parent, *args, **kwargs):
        wx.Dialog.__init__(self, parent, *args, **kwargs)
        Calculator.__init__(self)
        # definimos los controles
        self.textctrl_1 = wx.TextCtrl(self, -1, style=wx.TE_READONLY|wx.NO_BORDER|wx.TE_RIGHT)
        self.textctrl_2 = wx.TextCtrl(self, -1, style=wx.TE_READONLY|wx.NO_BORDER|wx.TE_RIGHT)
        self.button_0 = wx.Button(self, 0, u"0")
        self.button_1 = wx.Button(self, 1, u"1")
        self.button_2 = wx.Button(self, 2, u"2")
        self.button_3 = wx.Button(self, 3, u"3")
        self.button_4 = wx.Button(self, 4, u"4")
        self.button_5 = wx.Button(self, 5, u"5")
        self.button_6 = wx.Button(self, 6, u"6")
        self.button_7 = wx.Button(self, 7, u"7")
        self.button_8 = wx.Button(self, 8, u"8")
        self.button_9 = wx.Button(self, 9, u"9")
        self.button_10 = wx.Button(self, 10, u".")
        self.button_11 = wx.Button(self, 11, u"+")
        self.button_12 = wx.Button(self, 12, u"-")
        self.button_13 = wx.Button(self, 13, u"x")
        self.button_14 = wx.Button(self, 14, u"÷")
        self.button_15 = wx.Button(self, 15, u"±")
        self.button_16 = wx.Button(self, 16, u"x²")
        self.button_17 = wx.Button(self, 17, u"¹/x")
        self.button_18 = wx.Button(self, 18, u"√")
        self.button_19 = wx.Button(self, 19, u"%")
        self.button_20 = wx.Button(self, 20, u"C")
        self.button_21 = wx.Button(self, 21, u"=")
        # variables
        self.numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
        self.expression = []
        self.expression_string = u""
        self.result = 0.0
        
        self.__do_layout()
        self.__set_prop()
        self.__set_event()
    
    def __do_layout(self):
        """
        Establecemos los sizer y 
        organizamos los controles en ellos.
        """
        s1 = wx.BoxSizer(wx.VERTICAL)
        
        s2 = wx.BoxSizer(wx.VERTICAL)
        s2.Add(self.textctrl_1, 0, wx.EXPAND|wx.DOWN, 5)
        s2.Add(self.textctrl_2, 1, wx.EXPAND)
        s1.Add(s2, 1, wx.EXPAND|wx.ALL, 5)
        s1.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
        
        s3 = wx.GridSizer(6, 4, 0, 0) # 6 filas, 4 columnas
        # botones fila 1
        s3.Add(self.button_16, 0, wx.EXPAND)
        s3.Add(self.button_20, 0, wx.EXPAND)
        s3.AddSpacer(1)
        s3.AddSpacer(1)
        # botones fila 2
        s3.Add(self.button_19, 0, wx.EXPAND)
        s3.Add(self.button_18, 0, wx.EXPAND)
        s3.Add(self.button_17, 0, wx.EXPAND)
        s3.Add(self.button_14, 0, wx.EXPAND)
        # botones fila 3
        s3.Add(self.button_7, 0, wx.EXPAND)
        s3.Add(self.button_8, 0, wx.EXPAND)
        s3.Add(self.button_9, 0, wx.EXPAND)
        s3.Add(self.button_13, 0, wx.EXPAND)
        # botones fila 4
        s3.Add(self.button_4, 0, wx.EXPAND)
        s3.Add(self.button_5, 0, wx.EXPAND)
        s3.Add(self.button_6, 0, wx.EXPAND)
        s3.Add(self.button_12, 0, wx.EXPAND)
        # botones fila 5
        s3.Add(self.button_1, 0, wx.EXPAND)
        s3.Add(self.button_2, 0, wx.EXPAND)
        s3.Add(self.button_3, 0, wx.EXPAND)
        s3.Add(self.button_11, 0, wx.EXPAND)
        # botones fila 6
        s3.Add(self.button_15, 0, wx.EXPAND)
        s3.Add(self.button_0, 0, wx.EXPAND)
        s3.Add(self.button_10, 0, wx.EXPAND)
        s3.Add(self.button_21, 0, wx.EXPAND)
        
        s1.Add(s3, 3, wx.EXPAND|wx.ALL, 5)
        self.SetSizer(s1)
        s1.Fit(self)
        self.Layout()
    
    def __set_prop(self):
        """
        Establecemos algunas propiedades extras a los controles.
        """
        self.SetSize((400, 400))
        self.textctrl_2.SetFont(wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.BOLD))
        # color y fuente de los botones
        for id in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9):
            button = self.FindWindowById(id)
            button.SetBackgroundColour(wx.Colour(255, 255, 200))
            button.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.BOLD))
            
        for id in (11, 12, 13, 14):
            button = self.FindWindowById(id)
            button.SetBackgroundColour(wx.Colour(200, 255, 200))
            button.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
            
        for id in (15, 16, 17, 18, 19):
            button = self.FindWindowById(id)
            button.SetBackgroundColour(wx.Colour(200, 200, 255))
            button.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
            
        for id in (20,):
            button = self.FindWindowById(id)
            button.SetBackgroundColour(wx.Colour(255, 200, 200))
            button.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
        
    def __set_event(self):
        """
        Establecemos los eventos a los controles.
        """
        for bid in range(22):
            self.Bind(wx.EVT_BUTTON, self.OnClick, id=bid)

    def OnClick(self, event):
        """
        Al presionar uno de los botones.
        """
        character = event.GetEventObject().GetLabel()
        if character == u"x":
            character = u"*"
        elif character == u"÷":
            character = u"/"
        
        if not self.expression:
            if character in self.numbers:
                self.expression.append(character)
        
        elif character == u"=":
            self.Solve()
        
        elif character == u"C":
            self.expression = []
            self.result = 0.0
        
        elif self.expression[-1] in self.numbers:
            if character == u"±":
                self.expression[-1] = u"%s" % -float(self.expression[-1])
                
            elif character == u"x²":
                self.expression[-1] = u"%s" % (float(self.expression[-1]) * float(self.expression[-1]))
            
            elif character == u"¹/x":
                self.expression[-1] = u"%s" % (1 / float(self.expression[-1]))
            
            elif character == u"√":
                self.expression[-1] = u"%s" % math.sqrt( float(self.expression[-1]) )
                
            elif character == u"%":
                n1 = float(self.expression[-3])
                self.expression[-1] = u"%s" % ((n1 / 100) * float(self.expression[-1]))
            
            elif character in ("*", "/", "-", "+"):
                self.expression.append(character)
                self.Solve()
            
            elif character in self.numbers:
                self.expression[-1] += character
                
            
        else:
            if character in self.numbers:
                self.expression.append(character)
                
            elif character in ("*", "/", "-", "+"):
                self.expression[-1] = character
            
        self.expression_string = unicode.join(u" ", self.expression)
        self.textctrl_1.SetValue(self.expression_string)
        self.textctrl_2.SetValue(str(self.result))
        event.Skip()
    
    def Solve(self):
        try:
            exec("self.result = %s" % self.expression_string)
        except SyntaxError:
            pass
        return self.result
        
            
            
                
            

def main(args):
    app = wx.App(0)
    dlg = DlgCalculator(None, id=-1, title=u"Calculadora")
    dlg.Show()
    app.MainLoop()
    return 0

if __name__ == '__main__':
    sys.exit(main(sys.argv))