#Python Command Line Calculator


Continuing from yesterday's post on the #python cmd command line library, I have written a more complete example.  The command line calculator is a simple program for performing simple calculations in the command line using only python standard libraries.

  • Calculations are entered into the accumulator in the form <operator> <operand>.  For example, to add 3.4 to the accumulator, type "add 3.4".  
  • Supports addition, subtraction, multiplication, division, floor division, modulo, exponential power and reciprocal operations.
  • You can store any value as a variable and use the variables in calculations.  
  • Chain multiple calculations at once.


import cmd
import datetime
import textwrap

class Calc(cmd.Cmd):
    """A Command line calculator"""
    def __init__(self):
        """Constructor"""
        cmd.Cmd.__init__(self)
        cmd.Cmd.prompt = ""
        self.__acc = 0 #the accumulator
        self.__version = "1.0.1.3"
        self.__vars = {}

    def initialise(self):
        self.__makePrompt()

    def __getVersion(self):
        return self.__version

    
    def __showKeyError(self, args):
        return ("There is a problem with that variable\n'"+args+"' is not stored.")


    def __showSyntaxError(self, args):
        return ("I'm sorry old boy, there is no such command or variable '"+args+"'!")


    def __showVariableError(self, args):
        return ("Bad variable name '"+args+"'!")
    

    def __makePrompt(self):
        cmd.Cmd.prompt = "--->"+str(self.getAcc())+"\nEnter command or 'help' : "

        
    def __setAcc(self, avalue):
        """Sets the accumulator to a value"""
        self.__acc = avalue


    def getAcc(self):
        """get method for the accumulator"""
        return (self.__acc)


    def __setVariable(self, key, value):
        """Add item to dictionary"""
        try:
            self.__vars[key] = str(value)
        except NameError:
            print(self.__showVariableError(key))


    def __getVariable(self, key):
        """Returns the value associated with the key"""
        return self.__vars[key]

    def __showAllVars(self):
        for key in self.__vars:
            print ("  "+key+" = "+self.__vars[key], end=", ")
        print ("\n")
        
    def do_version(self, args):
        """Displays the calculator version number"""
        print("Version: ", self.__getVersion(), end="\n")


    def do_clear(self, args):
        """Clears all variables and sets the accumulator to zero"""
        self.__vars.clear()
        self.__setAcc(0)

    def __evaluate(self, args, b, op ):
        try:
            #Try to add the value onto the accumulator
            a = float(args)
            self.__setAcc( eval(str(b) + op +str( a ) ) )
        except ValueError:
            try:
                #Now look for a key in the dictionary
                a = float( self.__getVariable(args) )
                self.__setAcc( eval(str(b))+ op + str( a ) )
            except KeyError:
                print( self.__showKeyError(args) )
            
        
    def do_add(self, args):
        """Adds the argument to the accumulator. Example, add 5.3"""
        self.__evaluate( args, self.getAcc(), "+" )


    def do_sub(self, args):
        """Subtracts the argument from the accumulator. Example, sub 5.3"""
        self.__evaluate( args, self.getAcc(), "-" )     


    def do_divide(self, args):
        """Divides the accumulator by the argument. Example, divide 5.3"""
        self.__evaluate( args, self.getAcc(), "/" )    


    def do_multiply(self, args):
        """Multiplies the accumulator by the argument. Example, multiply 5.3"""
        self.__evaluate( args, self.getAcc(), "*" )      


    def do_div(self, args):
        """Performs floor division between the accumulator and the argument.  Example, div 3"""
        self.__evaluate( args, self.getAcc(), "//" )      


    def do_mod(self, args):
        """Performs modulo operation between the accumulator and the argument.  Example, mod 3"""
        self.__evaluate( args, self.getAcc(), "%" )      


    def do_raise(self, args):
        """Raises the value in the accumulator to the power.  Example raise 2"""
        self.__evaluate( args, self.getAcc(), "**" )


    def do_recip(self, args):
        """Find the reciprocal of the vale in the accumulator"""
        self.__setAcc( 1/self.getAcc() )

      
    def do_store(self, args):
        """Store the value in the accumulator into a named variable.  Exmaple, store x"""
        self.__setVariable(args, str(self.getAcc()) )


    def do_load(self, args):
        """Load a value into the accumulator"""
        try:
            #Try to find a value and put it in the accumulator
            a = float(args)
            self.__setAcc( a )
        except ValueError:
            try:
                #Now look for a key in the dictionary
                a = float( self.__getVariable(args) )
                self.__setAcc(  a )
            except KeyError:
                print( self.__showKeyError(args) )


    def do_time(self, args):
        """Displays the current time."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%H:%M:%S') )

      
    def do_date(self, args):
        """Displays the current date."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%Y-%m-%d') )
   

    def help_me(self):
        msg = "Enter command line calculations (e.g.  add 3.2).  Perform multiple calculations with the 'chain' command (eg chain 4; add 4; div 2).  Assign to a variable with 'store <varname> (e.g. store x).  Clear the accumulator and all variables with 'clear'."
        for x in textwrap.wrap(msg, 50):
            print(x)


    def help_about(self):
        msg = "Command Line Calculator "+self.__getVersion()+" superdecade games. http://www.superdecadegames.com or http://www.superdecade.blogspot.com"
        for x in textwrap.wrap(msg, 50):
            print(x)


    def do_chain(self, args):
        """Execute a series of commands separated y semi colons.  Example: 8; add 3; sub 5"""
        for x in args.split(";"):
            self.onecmd(x)
        
           
    def emptyline(self):
        """deals __makePromptwith empty input line"""
        #print("\nThe accumulator is...", self.getAcc())


    def default(self, args):
        """Overides the default error message"""
        try:
            #user types a number so set accumulator to that number
            a = float(args)
            self.__setAcc( a )
        except ValueError:
            try:
                #user has types a variable so set accumulator to that number
                a = float( self.__getVariable(args) )
                self.__setAcc( a )
            except KeyError:
                print( self.__showSyntaxError(args) )

        
        
    def do_show(self, args):
        """See the value of a variable.  Example show x"""
        if args == "":
            self.__showAllVars()
        else:
            try:
                #user has types a variable so set accumulator to that number
                a = float( self.__getVariable(args) )
                print("  "+args+" = "+str(a))
            except KeyError:
                print( self.__showSyntaxError(args) )

        
    def postcmd(self, stop, line):
        """Script that runs when the current command has completed"""
        self.__makePrompt() # remake prompt so it includes the accumulator value
        
if __name__ == "__main__":
    mycalc = Calc()
    mycalc.initialise()
    mycalc.cmdloop("Command Line Calculator\n")
    


Python learners should learn this #python first

I have recently discovered the python cmd library - a really useful library for creating command line programs in python.  I am beginning to wonder why we don't teach beginning python programmers the cmd library first.

Here is a really simple (and rather pointless) python command line program to demonstrate how to use the various features.

import cmd

class App(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)

if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop()





Here we begin by importing the cmd library (it is part of the standard library so you will have it available).  I create an App class to contain my various command line functions and create a constructor method __init__.

This program doesn't really do much at the moment.  Running it you will see a command prompt.

We have created a new command line.  The only command that is currently implemented is 'help'. Type 'help' and see what happens...

Now we will add a new command.  It would be useful to have a way of stopping our program, so we will implement a 'quit' command.

import cmd

class App(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)

    def do_quit(self, args):
        return True
 
if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop()




Adding a new command is as simple as creating a new method called do_something().  Returning True from a method will exit the cmdloop() method, so this will suffice for our quit method, for now.

Run the program and you will notice that a new command 'quit' has been added to the program documentation.


import cmd

class App(cmd.Cmd):

    def __init__(self):
        cmd.Cmd.__init__(self)

    def do_quit(self, args):

        """Exits the program."""
        return True
    
if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop()


We don't like 'undocumented commands'.  Adding documentation is as easy as adding a docstring to the method.  The docstring is the triple-quoted string.

Yay!  Documented commands.  Try typing 'help quit' at the prompt.


import cmd

class App(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        cmd.Cmd.prompt = "(enter command or 'help'): "

    def do_quit(self, args):
        """Exits the program."""
        return True
 
if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop()




The default prompt is not very helpful.  You will probably want to change the prompt to a useful symbol or helpful message.  This is easily done by changing the value of the prompt attribute of your cmd object.

Customizing the prompt.

import cmd
import datetime

class App(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        cmd.Cmd.prompt = "(enter command or 'help'): "

    def do_time(self, args):
        """Displays the current time."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%H:%M:%S') )
     
     
    def do_quit(self, args):
        """Exits the program."""
        return True
 
if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop("Command Prompt")



Let's get the program to do something a little more interesting.  Here we have added a new method do_time, such that when you type 'time' you will see the current time...

...and the date....

import cmd
import datetime

class App(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        cmd.Cmd.prompt = "(enter command or 'help'): "

    def do_time(self, args):
        """Displays the current time."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%H:%M:%S') )
     
    def do_date(self, args):
        """Displays the current date."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%Y-%m-%d') )
   
    def do_quit(self, args):
        """Exits the program."""
        return True
 
if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop("Command Prompt")

Notice that we can pass a banner or a greeting message to the program by passing a string as parameter to the cmdloop() method?

Our app now does something useful!

Notice that each of the user-defined methods has an argument called 'args'.  Our one-word commands can actually take other commands as parameters and then do something useful with them.  For example, here we create a new command 'path'...

import cmd
import datetime
import os

class App(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        cmd.Cmd.prompt = "(enter command or 'help'): "

    def do_path(self, args):
        """Lists the directory eg c:\\users"""
        try:
            for filename in os.listdir(args):
                print(filename)
        except (FileNotFoundError):
            print ("Cannot find files for '", args, "'")
         
    def do_time(self, args):
        """Displays the current time."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%H:%M:%S') )
     
    def do_date(self, args):
        """Displays the current date."""
        print( datetime.datetime.strftime((datetime.datetime.now()), '%Y-%m-%d') )
   
    def do_quit(self, args):
        """Exits the program."""
        return True
 
if __name__ == "__main__":
    myapp = App()
    myapp.cmdloop("Command Prompt")


An example of passing a parameter to one of our user-defined methods.  Here we obtain a directory listing and display it on the screen.

Notice that it is necessary to add exception handing in the do_path() method.  This is because it is highly likely that the user will type something meaningless (such as a non-existent filepath) which might otherwise crash the program.

The following method shows how we can use other commands as though the user had typed them.  For example, it might be useful to have a users command, which works in the same way as though the user had typed path c:\\users


    def do_users(self, args):
        """Displays a list of users on this system"""
        myapp.onecmd("path c:\\users")

    
That's it for now.  We've looked at how we can implement simple but powerful command line interfaces to our python programs.  We've seen how to add documentation and pass parameters to our commands.  We have also seen how we can get python to run our own commands as though the user had typed them.

Hopefully you agree that the cmd library provides a whole load of useful and powerful features for python beginners, and it is easy too.

Read more about the cmd library here.

Cool things found on The Web today

(or, how I spent my weekend)

Rock-Paper-Scissors

This version of the game Rock-Paper-Scissors (or 'Paper-Scissors-Stone' in my day), is a demonstration of basic artificial intelligence.  Play against a computer opponent who uses the last 200,000 rounds of experience against you.  There are two modes of play: 'Novice' learns from your own tendencies, where 'Veteran' builds knowledge based on thousands of games against people all over the world.  Good luck!




Earth and Moon Viewer

This site from John Walker allows you to view the Earth from Space, or how it looks right now.  You can view from any latitude or longitude; from any preset city; comprehensive list of satellites, or from the sun or moon.  Comprehensive weather data can be superimposed onto the image.  In addition to viewing the earth, you can also view the moon (as the name of the site might suggest).



Morse Code Machine

A simple morse code generator from boyslife.org.  Hit the buttons to generate the morse pattern or play a game to test your morse knowledge. .-.. --- .-..



Questionaut

Questionaut is a beautiful learning game from BBC bitesize games.  Suitable for 7 to 11 year olds, the aim of questionaut is to guide your very cute avatar through a series of animated puzzles.  The first level requires you to build a hot-air balloon so that you can travel up to each new level.  On each level, by interacting with the characters in the various strange worlds you will encounter, you must earn more 'fuel' for your balloon by answer questions correctly.  Once five questions are answered, you can carry on your way.





Curvy in HTML5

A frustrating puzzle that seems simple enough.  Rotate the hexagons to form continuous paths from one to the next.  Just when you think you are doing well, you discover that one path no longer works and the whole pattern needs rearranging.  Various levels of play allow you to create more and more challenging puzzles to fit your needs.

Th...th....th...that's all folks (until next time...)

The Internet of Fish

My new favourite site - aquard.io


Aquard.io is an online fishtank.  There are three webcameras allowing you to remotely view over fifty happy tropical fish through your web-browser.  An arduino computer controls the backend allowing viewers to request feedings and squirt bubbles into the water.


If you want to teach the concept of The Internet of Things to children, then this is the site to come to.  I showed this to three year 9 girls who are really into computers and they had to admit that it was  'quite cool,' however they wished that there was a chat feature.  Presumably so you can chat to other viewers (not the fish).  They also said that it 'wasn't fair' because somebody has 'bought a fishtank and now we look after it for free' (!).

You can join the fun on Twitter @aquardio.

A video explaining more.


Awesome things I found on the web today

Bored already?  Well it is Sunday.  Here are a few cool things I have found on the Web recently that might help waste a few moments for you too.  Enjoy!

Instacalc - online calculator you can share and embed.


A really nice online calculator.  Get answers as you type them using natural language.  You can also embed your calculator onto your website or blog.  Use variables and scientific notation; plenty of built in unit conversion tools, etc etc.








Track the ISS

Heavens Above

Try out this International Space Station tracker.  Plenty of space station information, and there is an Android App too.







Stargazing Augmented reality app, well worth checking out.

Star Walk

Try out this award-winning star-gazing app from Vito Technology.  Star Walk is an interactive astronomy guide that shows celestial objects in the exact positions on the sky above you, providing detailed information about them. Augmented reality meets star-gazing.






Monsterland

Monsterland

Waste some time with monsterland.  Try and get the little red monster to touch his big red brother by eliminating the other cute blocks in the way.






Connect Me

A great little puzzle game from the people who brought you monsterland (above).  Complete each puzzle by connecting the cute critters the only way they can.  Critters come in three types - critters you can move, critters you can rotate, and critters you can rotate AND move.

New version of Spellunker


I am delighted to announce that the new version of my spelling game Spellunker - the adventures of Wordsworth Spellunker - now features even more artwork by the fantastically talented Pob.

Head over to the Downloads page.

Spellunker is a spelling game AND an adventure game.  Follow Worsdworth Spellunker as he tries to find his missing parents.  Each level requires you to defeat the obstacles by casting 'spells' from the magic letters you are provided with.  You need to score an increasing amount of points on each three-minute level in order to proceed to ever more challenging levels.

Cast a 'spell' in Spelluker to defeat each level.

Each of the thirty-or-so levels features new artwork.  Here is a small taste of what is waiting for you...

Spellunking the bridge abridged.

Richard Wightly.  This foul spirit will bamboozle you with puns unless you can Spellunk him back.
Head over to the Downloads page.

Quartermaster


Continuing our series on PoopScoop solutions, here we look at the "Quatermaster" level (I am sure I meant "Quartermaster", but unfortunately my spelling is atrocious)

The pits of nothingness are very useful to you on this level.  Start by clearing a path to the upper left corner of the map, pushing obstacles into the pits as you go.  Then pull the blocks away so you can push one block onto the buttons.  Once the button is pressed a new way opens up and scooping the poop becomes trivial.

Solution:

reverse 1
left
forward 1
jump
//repeat loop
repeat 2
{
forward 1
right
}
end
push
left
forward 1
drain
forward 1
dig
forward 2
//repeat loop
repeat 5
{
pull
}
end
left 
//repeat loop
repeat 2
{
     forward 1
     right 
}
end
push
left 
forward 7
//repeat loop
repeat 8
{
     pull
}
end
left 
//repeat loop
repeat 2
{
     forward 1
     right 
}
end
push
left 
forward 8
//repeat loop
repeat 9
{
     pull
}
end
left 
//repeat loop
repeat 2
{
     forward 1
     right 
}
end
push
left 
forward 9
//repeat loop
repeat 10
{
     pull
}
end
left 
//repeat loop
repeat 2
{
     forward 1
     right 
}
end
push
left 
forward 8
right 
push
reverse 1
left 
forward 2
right 
pull
right 
forward 1
left 
forward 1
right 
forward 1
left 
forward 1
left 
//repeat loop
repeat 2
{
     push
}
end
right 
//repeat loop
repeat 4
{
     push
}
end
right
forward 1
left
push
right
forward 2
left
forward 1
left
push
reverse 2
flip
//repeat loop
repeat 2
{
     pull
}
end
jump 
flip
push
push
reverse 3
flip
dig
drain
forward 2
//repeat loop
repeat 6
{
     pull
}
end
right 
forward 1
left 
forward 2
left 
forward 1
left 
//repeat loop
repeat 2
{
     push
}
end
reverse 7
flip
//repeat loop
repeat 7
{
     pull
}
end
right 
forward 1
left 
forward 2
left 
forward 1
left 
//repeat loop
repeat 2
{
     push
}
end
reverse 8
flip
//repeat loop
repeat 8
{
     pull
}
end
right 
forward 1
left 
forward 2
left 
forward 1
left 
//repeat loop
repeat 2
{
     push
}
end
reverse 9
flip
//repeat loop
repeat 9
{
     pull
}
end
left 
forward 1
right 
forward 2
right 
forward 1
left 
forward 8
right 
push
reverse 1
right 
forward 3
left 
drain
forward 2
right 
forward 4
dig
right 
dig
reverse 2
flip
//repeat loop
repeat 3
{
     pull
}
end
left 
forward 4
right 
forward 2
right 
forward 4
left 
forward 2
right 
forward 1
left 
scoop
forward 1
scoop




Label