Writing a calculator program in Python

Python based calculator using tkinter

Many computer courses in Python suggest writing a calculator program in Python to develop both your logic and user interface skills. In this article we discuss some more advanced concepts you might use to write a good calculator program with a minimum of effort. We are not, however, proposing to do your entire assignment, but only to give you some helpful pointers that may make your program easier to write well.

If you look at our calculator interface, you quickly note that it is really a 5 x 4 matrix of buttons, but actually 6 x 4 counting the value display line, which actually just a Label field. So even if you haven’t bothered to learn about layout managers (there are only two in tkinter) now would be a good time to start.

It is also helpful to note that all of the numerical buttons (all the gray ones) have the same function: they put a number into the display line. The only exception is the period button which also has to check to make sure that only one period gets added to the number.

Command buttons

The trouble with an interface using all these buttons is deciding where the clicks for all these buttons go. One really simple solution is to put the command to be executed right inside the button.  Here is the interface for such buttons:

# abstract class defines command interface
class DButton (Button):
    def __init__(self, master, **kwargs):
        super().__init__(master, command=self.comd, **kwargs)
   
    def comd(self):
        pass

This is an abstract class, in that the comd function just has a place holder pass statement. But if we derive buttons from DButton, we can fill in each comd method with actual code as we see below. All of the buttons in this example are derived from DButton and each derived button has its own comd code.

In all cases, these buttons will not know much about the value display or the state of the period or anything else. Instead, we create a Mediator class that knows about the value display and receives commands from all the buttons. So, our Numeric button is just the following: mostly just setting up the font and color:

class NumButton(DButton):
    def __init__(self, master, med, **kwargs):   
        super().__init__(master, width=2, fg='white', 
                       bg="#aaa", **kwargs)

        butnfont = tkFont.Font(family='Helvetica',size=16, weight='bold')
        self['font'] = butnfont
        self.med= med         # save copy of Mediator

   

 Our comd function calls the Mediator’s numClick method and sends it the label of the button, which is the text representing the actual number we want to add to the value display.

def comd(self):
        self.med.numClick(self['text']) # gets number from text label 

At the top of the list of widgets we create the top label, and then we create the Mediator and pass it that label. The Mediator is the only class that knows about the contents of that label, and it receives calls from the various number buttons to add that text to the label. In this method it adds a leading space, which could be changed to a minus sign if you change the numbers sign, and then just appends numbers to the label:

# Any number is clicked
def numClick(self, text):
    if self.first: # if first char clear the zero
        self.setlabelText(" ") # leading space
        self.first = False
    st = self.getLabelText() + text
    self.setlabelText(st)

So, we create all of the number buttons in a grid as shown in the figure. Here is a bit of that code for buttons 7, 8 and 9. They are placed in grid row 2 in columns 0, 1 and 2.

but = NumButton(root, med, text='7')
but.grid(row=2, column=0, padx=1, pady=1)
but = NumButton(root, med, text='8')
but.grid(row=2, column=1, padx=1, pady=1)
but = NumButton(root, med, text='9')
but.grid(row=2, column=2, padx=1, pady=1)

To illustrate the structure of this part of the program, the Mediator communicates with the label and the buttons all communicate with the Mediator:

In a similar fashion, all the other buttons are command buttons that tell the Mediator what to do. The C and CE button clear all the data and the CE buttons just clears the text in that top label.

Clicking one of the operation buttons (+, -, *, or / ) tells the program that the current number is done and that a new one will start so that the two numbers needed to carry out that operations get created. You should store each of those in some sort of array or stack along with the operation symbol. Usually this array can be stored right in the Mediator class. Then, when the Equals sign is clicked, you carry out the calculation.

If you enter

2 + 3

the Equals sign button should combine those and display “5.0”. (We convert all of them to floats for simplicity.)

This will work for more than two numbers, and combining them left to right mainly works:

5 * 3 + 2

will correctly display 17.  However, if you remember the priority rule My Dear Aunt Sally, you carry out multiplication and division before addition and subtraction, and simple to right combining of the data will produce the wrong answer for

2 + 5 * 3

This should still result in 17, but unless you remember the priority rule, you will get the wrong answer.

Since this simple exercise is to construct a simple calculator, limiting it to two values is a reasonable first approximation. Handling this priority issue is left as an exercise for the reader, but obviously you have to find a way to perform multiplication and division first. Most likely, you will use the Python eval() function.

Using a Dictionary to choose operations

One other clever trick you might want to consider using is finding a way to choose the function to be carried out based on which of the operation buttons you select. You can use a simple dictionary which returns the function you want to carry out:

funcs = {"+": self.addfun,
         "-": self.subfun,
         "*": self.multfun,
         "/": self.divfun
         }
 

Then, you can use the text of the operation sign to get the right function:

func = funcs.get(sign) # will be one of four symbols
sum = func()           # execute that function

Conclusions

By deriving new classes from the Button class, you can make a really nice-looking calculator interface in a grid layout of 6 x 4, where the top line is the label and the 5 x 4 represents the control and numeric buttons. All of the buttons benefit from having a Command interface, and all of the numeric buttons can call the same routine since they all put numbers into the top label. The Dot button differs only in that you must limit it to one decimal point per number.

The major class for handling all these button clicks is called a Mediator, and it is the only class that has access to the top data line display.

You keep the various numbers and operations that the user enters in an array or stack, and you can use the dictionary method to selection the function to carry out the right operation. Carrying out computations of more than two numbers means remembering My Dear Aunt Sally to choose the operations to carry out first.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s