# PyPortal Calculator using the Displayio UI Elements

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/078/187/medium800thumb/lcds___displays_calculator_animated.jpg?1563038863)

This guide explores some of the User Interface (UI) elements available through The CircuitPython displayio library by creating a basic calculator. CircuitPython will handle the actual calculations and the program concentrates on the input, output, and logic. This is split into two different files. One file will be the calculator class that handles all of the backend input and calculations and the other file will handle more of the front-end UI.

For the calculator UI elements, we'll be using buttons, the label, and rectangle to create the calculator. To get the value of the button, we'll simply be reading the text that is displayed on the button. The label will normally left-align, but since most calculators are right aligned, we'll be creating a special function to shift the x-position of the label over whenever the text is updated. Since the label is basically just the text, we'll also be using a rectangle UI element to create a bounding box for the display.

The PyPortal is the perfect device for setting up as a calculator with the touchscreen and large display. If you haven't had a chance to take a look, be sure to check out our&nbsp;[Adafruit PyPortal - IoT for CircuitPython](https://learn.adafruit.com/adafruit-pyportal)&nbsp;guide.

### Adafruit PyPortal - CircuitPython Powered Internet Display

[Adafruit PyPortal - CircuitPython Powered Internet Display](https://www.adafruit.com/product/4116)
 **PyPortal** , our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Make custom touch screen interface GUIs, all open-source, and Python-powered using&nbsp;tinyJSON / APIs to get news, stock, weather, cat photos,...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4116)
[Related Guides to the Product](https://learn.adafruit.com/products/4116/guides)
![Front view of a Adafruit PyPortal - CircuitPython Powered Internet Display with a pyportal logo image on the display. ](https://cdn-shop.adafruit.com/640x480/4116-00.jpeg)

The PyPortal has a convenient acrylic enclosure available that makes it perfect for displaying on your desk.

### Adafruit PyPortal Desktop Stand Enclosure Kit

[Adafruit PyPortal Desktop Stand Enclosure Kit](https://www.adafruit.com/product/4146)
PyPortal is&nbsp;our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Create little pocket universes of joy that connect to something good.

And now that you've made a cool internet-connected project...

In Stock
[Buy Now](https://www.adafruit.com/product/4146)
[Related Guides to the Product](https://learn.adafruit.com/products/4146/guides)
![Demo Shot of the Assembled Adafruit PyPortal Desktop Stand Enclosure Kit.](https://cdn-shop.adafruit.com/640x480/4146-03.jpg)

You will need a Micro USB cable if you don't have one.

### Fully Reversible Pink/Purple USB A to micro B Cable - 1m long

[Fully Reversible Pink/Purple USB A to micro B Cable - 1m long](https://www.adafruit.com/product/4111)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also fully reversible! That's right, you will save _seconds_ a day by not having to flip the cable around.

First let's talk about the cover and over-molding. We got these...

In Stock
[Buy Now](https://www.adafruit.com/product/4111)
[Related Guides to the Product](https://learn.adafruit.com/products/4111/guides)
![Fully Reversible Pink/Purple USB A to micro B Cable](https://cdn-shop.adafruit.com/640x480/4111-02.jpg)

Alternatively, if you would like to make it portable and battery-powered, be sure to check out our&nbsp;[Portable PyPortal](https://learn.adafruit.com/portable-pyportal)&nbsp;guide.

# PyPortal Calculator using the Displayio UI Elements

## Initial Setup

To get set up, we will need CircuitPython, a few libraries and the PyPortal Calculator Source Code and font downloaded from Github.

## CircuitPython

First, make sure you are running the&nbsp;[latest version of Adafruit CircuitPython](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython)&nbsp;for your board. Because of the speed-ups that were added,&nbsp;the PyPortal Calculator requires at least CircuitPython 4.1.0.

To download the CircuitPython beta, visit the following link for the PyPortal and download the UF2 for CircuitPython beta.&nbsp; **You must be using CircuitPython 4.1.0 or later for PyPortal Calculator to work fast enough!**

[Download CircuitPython for PyPortal from circuitpython.org](https://circuitpython.org/board/pyportal/)
## PyPortal Calculator Source Code

Download all the needed files from GitHub by clicking the **Download Project Bundle** button below.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/PyPortal/PyPortal_Calculator/code.py

All of the files should go onto your PyPortal main **CIRCUITPY** drive in the directories noted.

![CIRCUITPY](https://adafruit.github.io/Adafruit_Learning_System_Guides/PyPortal_PyPortal_Calculator.png )

Here is the CircuitPython calculator class in its entirety. If you click on **Download Project Bundle** button, it will download all files used in this tutorial also.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/PyPortal/PyPortal_Calculator/calculator.py

After copying everything over, your PyPortal should display a Calculator on it. In the next few sections, we'll go over the User Interface Elements and examine the code more closely.

![](https://cdn-learn.adafruit.com/assets/assets/000/078/017/medium800/circuitpython_calculator.png?1562792597)

## Required CircuitPython Libraries

All the needed libraries should be available in the Project Bundle.

Here are the libraries used:

- **adafruit\_bitmap\_font**
- **adafruit\_display\_shapes**
- **adafruit\_display\_text**
- **adafruit\_button**
- **adafruit\_touchscreen**

Before continuing make sure your board's **lib** folder or root filesystem have the&nbsp; **adafruit\_bitmap\_font** ,&nbsp; **adafruit\_display\_shapes** ,&nbsp; **adafruit\_display\_text,&nbsp;**** adafruit\_button **, and&nbsp;** adafruit\_touchscreen&nbsp;**files and folders copied over.

# PyPortal Calculator using the Displayio UI Elements

## UI Quickstart

There are several User Interface (UI) elements available to use with displayio. You can use these together to create all kinds of fun applications such as a calculator.

## Referencing the Display

On boards that have a display, it is automatically initialized. to reference it, you simply need import `board` and assign it to the built-in display reference:

```python
import board

display = board.DISPLAY
```

On board without a built-in display, we recommend taking a look at the CircuitPython pages regarding your specific display for how to initialize it. You would then assign display to the returned output of the display driver initialization. For instance, if you have an ILI9341 display, it would look something like this, though your pins may vary.

```python
import board
import displayio
import adafruit_ili9341

displayio.release_displays()

spi = board.SPI()
tft_cs = board.D9
tft_dc = board.D10

display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=board.D6
)
display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240)
```

## Groups

[Groups](https://circuitpython.readthedocs.io/en/latest/shared-bindings/displayio/Group.html) are a way for displayio to keep track of all of the elements that it needs to draw. Subgroups can be inside of groups, but you must have at least one main group - called the **root group**. For all of the elements on this page, you must first import displayio, so we'll start with making sure that's at the top of your file.

`import displayio`

You must also create the group that will be used as your root group and set that:

`my_display_group = displayio.Group()display.root_group = my_display_group`

Info: 

## Shapes

The [shapes](https://circuitpython.readthedocs.io/projects/display-shapes/en/latest/api.html) are part of the the [**adafruit\_display\_shapes**](https://github.com/adafruit/Adafruit_CircuitPython_Display_Shapes) library. At the time of this writing, there are four shapes available. They work by generating a bitmap in the specific shape using displayio.

### Rectangle

The rectangle is your most basic shape. It can either be filled, outlined, or both.

![circuitpython_rectangle.png](https://cdn-learn.adafruit.com/assets/assets/000/078/040/medium640/circuitpython_rectangle.png?1562797348)

### Rounded Rectangle

The rounded rectangle is a little more complex and is comprised of 4 lines and quarter circle corners.

![circuitpython_roundrect.png](https://cdn-learn.adafruit.com/assets/assets/000/078/038/medium640/circuitpython_roundrect.png?1562797289)

### Circle

The circle is based on the rounded rectangle and only draws the four corners without any width or height.

![circuitpython_circle.png](https://cdn-learn.adafruit.com/assets/assets/000/078/041/medium640/circuitpython_circle.png?1562797391)

### Triangle

The triangle allows you to supply three sets of coordinates and will either draw an outline between those vertices, fill it in, or both.

![lcds___displays_triangle.png](https://cdn-learn.adafruit.com/assets/assets/000/079/122/medium640/lcds___displays_triangle.png?1565111969)

To use shapes, you first need to import the shapes you want to use at the top of your file. For instance if you wanted to separate import all the shapes, you would add something like this.

`from adafruit_display_shapes.rect import Rect`  
`from adafruit_display_shapes.circle import Circle`  
`from adafruit_display_shapes.roundrect import RoundRect`  
`from adafruit_display_shapes.triangle import Triangle`

Next, you can draw a [rectangle](https://circuitpython.readthedocs.io/projects/display-shapes/en/latest/api.html#rect)&nbsp;with something like:

`rect = Rect(0, 0, 80, 40, fill=0x00FF00)`

For a [circle](https://circuitpython.readthedocs.io/projects/display-shapes/en/latest/api.html#), you can create it with something like:

`circle = Circle(100, 100, 20, fill=0x00FF00, outline=0xFF00FF)`

For a [triangle](https://circuitpython.readthedocs.io/projects/display-shapes/en/latest/api.html#triangle), you can create it with something like:

`triangle = Triangle(170, 50, 120, 140, 210, 160, fill=0x00FF00, outline=0xFF00FF)`

Or you can draw a [rounded rectangle](https://circuitpython.readthedocs.io/projects/display-shapes/en/latest/api.html#roundrect) with something like:

`roundrect = RoundRect(50, 100, 40, 80, 10, fill=0x0, outline=0xFF00FF, stroke=3)`

Finally, you can add all of these shapes to your group.

`my_display_group.append(rect)`  
`my_display_group.append(circle)`  
`my_display_group.append(triangle)`  
`my_display_group.append(roundrect)`

## Fonts

For fonts, there are a couple options that you can use. You can create or provide a custom font file and use that for your label. If you don't want to provide a custom bitmap font, you can use the Built-in Terminal Font.

### Built-in Terminal Font

The terminal font looks a little blocky, but at least you don't need a separate file.

This example comes from the&nbsp;&nbsp;[PyBadger Event Badge](https://learn.adafruit.com/pybadger-event-badge)&nbsp;guide.

![circuitpython_font_terminal.png](https://cdn-learn.adafruit.com/assets/assets/000/078/079/medium640/circuitpython_font_terminal.png?1562874425)

To use the Terminal Font, you first need to import **terminalio** by adding an import statement to the top of your file.

`import terminalio`

Then you simply pass the terminalio font to the UI element. For instance, with the label, you would do something like:

`my_label = Label(terminalio.FONT, text="My Label Text", color=BLACK)`

### Bitmap Fonts

The [Bitmap font](https://circuitpython.readthedocs.io/projects/bitmap-font/en/latest/api.html) uses the [**adafruit\_bitmap\_font**](https://github.com/adafruit/Adafruit_CircuitPython_Bitmap_Font) library and requires a separate BDF (Bitmap Distribution Format) file, but looks nicer on the screen. It doesn't currently have anti-aliasing, so it still looks a little blocky on some fonts.

This example comes from&nbsp;the&nbsp;[PyBadge Conference Badge With Unicode Fonts](https://learn.adafruit.com/pybadge-conference-badge-multi-language-unicode-fonts)&nbsp;guide.

![circuitpython_font_bitmap.png](https://cdn-learn.adafruit.com/assets/assets/000/078/078/medium640/circuitpython_font_bitmap.png?1562874379)

To use&nbsp;a Bitmap Font, you first need to copy your custom file over to your **CIRCUITPY** drive. We like to place fonts into a **/fonts** folder. To find out more about creating your own custom fonts, be sure to check out our&nbsp;[Custom Fonts for CircuitPython Displays](https://learn.adafruit.com/custom-fonts-for-pyportal-circuitpython-display)&nbsp;guide.

Next import **bitmap\_font** by adding an import statement to the top of your file.

`from adafruit_bitmap_font import bitmap_font`

After that, you can create a font instance. For example, if you have a font file named&nbsp; **Arial-12.bdf** in the fonts folder, you would use the following line of code.

`font = bitmap_font.load_font("/fonts/Arial-12.bdf")`

Then you simply pass font instance to the UI element. For instance, with the label, you would do something like:

`my_label = Label(font, text="My Label Text", color=BLACK)`

## Label

The [label](https://circuitpython.readthedocs.io/projects/display-text/en/latest/api.html) requires the [**adafruit\_display\_text**](https://github.com/adafruit/Adafruit_CircuitPython_Display_Text) library. It requires a font to be passed in. This can either be the Terminal Font or a Custom Font. It allows you to display text and place it in your displayio group. The conference badge mentioned in the fonts section makes great use of labels.

To create a label, you first import the required library at the top of your file.

`from adafruit_display_text.label import Label`

Then you create the label.

`my_label = Label(terminalio.FONT, text="My Label Text", color=BLACK)`

Finally, add the label to a displayio group. This can either be your main group or a subgroup.

`my_display_group.append(my_label)`

## Button

The [button](https://circuitpython.readthedocs.io/projects/display-button/en/latest/api.html) makes use of the [**adafruit\_button**](https://github.com/adafruit/Adafruit_CircuitPython_Display_Button) library and builds on top of the **adafruit\_display\_shapes** ,&nbsp; **adafruit\_label** , and [**adafruit\_touchscreen**](https://github.com/adafruit/Adafruit_CircuitPython_Touchscreen) libraries. A button is basically a shape and label together which can also handle presses as well as color inversion.

To use the button, you need to add the required libraries to the top of your file.

`from adafruit_button import Button`  
`import adafruit_touchscreen`

Next create your button. There are lots of options and you can take a look at some of the examples provided in the button library to get an idea of the various things you can do.

`my_button = Button(x=20, y=20, width=80, height=40,`  
`                   label="My Button", label_font=terminalio.FONT)`

The font is required, but again, you can provide either the built-in font or a custom font. Finally add it to your group.

`my_display_group.append(my_button)`

Here's what the simple test example looks like showing many different variations.

![](https://cdn-learn.adafruit.com/assets/assets/000/078/170/medium800/circuitpython_buttons.png?1562956232)

## Images

Images are also available, although they are not used in this calculator project. There are a couple of different ways to display images with displayio.

- **ImageLoad Library** - This library loads images from storage directly into memory. While faster, enough memory to hold the image data must be available.
- **OnDiskBitmap** - This uses the image data directly from storage (disk). While slower, this approach requires less memory.

### ImageLoad Library

[Imageload](https://circuitpython.readthedocs.io/projects/imageload/en/latest/api.html) is the main class in the&nbsp;[**adafruit\_imageload**](https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad) library provides an easy way to decode and display bitmaps. To use it, first include the library at the top of your file.

`import adafruit_imageload`

Second, Generate the Bitmap and Palette from the image:

`my_bitmap, my_palette = adafruit_imageload.load("/my_bitmap.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)`

Third, create a TileGrid from the Bitmap and Palette:

`my_tilegrid = displayio.TileGrid(my_bitmap, pixel_shader=my_palette)`

Finally&nbsp;add the TileGrid to your display group.

`my_display_group.append(my_tilegrid)`

### OnDiskBitmap

[OnDiskBitmap](https://circuitpython.readthedocs.io/en/latest/shared-bindings/displayio/OnDiskBitmap.html) is available directly through displayio and is very easy to use. The first step is to open the image file with **read** and **binary** modes and create a bitmap. Because of its flexibility and low memory use, this is the recommended way.

`my_bitmap = displayio.OnDiskBitmap("/my_bitmap.bmp")`

The second step is to create a TileGrid from the image using the automatic color converter.

`my_tilegrid = displayio.TileGrid(my_bitmap, pixel_shader=my_bitmap.pixel_shader)`

Finally add the TileGrid to your display group.

`my_display_group.append(my_tilegrid)`

## Calculator UI Elements

The PyPortal Calculator makes use of the Rectangle, Label, and Button Elements.

![](https://cdn-learn.adafruit.com/assets/assets/000/078/148/medium800/circuitpython_calc_ui_elements.png?1562950664)

# PyPortal Calculator using the Displayio UI Elements

## CircuitPython Code

Now let's take a look at the CircuitPython code for the calculator program. It's divided into two main files. The Main file sets everything up and runs the main loop for the calculator. The Calculator class handles all of the piecing together the inputs and performing the calculations. It does a little bit of button handling such as changing the All Clear to Clear Entry and back again. You can find the complete code listings on the **Initial Setup** page.

## Main File

We start by importing everything we'll need for the calculator.

```
import time
from collections import namedtuple
import board
import displayio
from adafruit_display_text.label import Label
from adafruit_bitmap_font import bitmap_font
from adafruit_display_shapes.rect import Rect
from adafruit_button import Button
from calculator import Calculator
import adafruit_touchscreen
```

Next, we create a **Named Tuple** called **Coords**. This allows us easily generate a grid and assign buttons into that grid.

```
Coords = namedtuple("Point", "x y")
```

Next we setup the touchscreen. The calibration values provided are the default ones that seem to work well with the touchscreen library.

```
ts = adafruit_touchscreen.Touchscreen(board.TOUCH_XL, board.TOUCH_XR,
                                      board.TOUCH_YD, board.TOUCH_YU,
                                      calibration=((5200, 59000), (5800, 57000)),
                                      size=(320, 240))
```

Next we have settings to make it easy to make small adjustments to the calculator.

```
# Settings
BUTTON_WIDTH = 60
BUTTON_HEIGHT = 30
BUTTON_MARGIN = 8
MAX_DIGITS = 29
BLACK = 0x0
ORANGE = 0xFF8800
WHITE = 0xFFFFFF
GRAY = 0x888888
LABEL_OFFSET = 290
```

Then we create our main group as described on the UI Elements page.

```auto
# Make the display context
calc_group = displayio.Group()
board.DISPLAY.root_group = calc_group
```

Now we create a solid colored background and add it to the group we created.

```
# Make a background color fill
color_bitmap = displayio.Bitmap(320, 240, 1)
color_palette = displayio.Palette(1)
color_palette[0] = GRAY
bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette,
                               x=0, y=0)
calc_group.append(bg_sprite)
```

Next we'll create the bitmap font and create an empty buttons list.

```
# Load the font
font = bitmap_font.load_font("/fonts/Arial-12.bdf")
buttons = []
```

Let's now define a few button functions that make setting up a button grid easy.

**button\_grid** is responsible for calculating the x and y position of the button based on the grid square where we want to add the button.

**add\_button** is a convenience function that will take a few parameters and handle all of the common button parameters itself. Most buttons have a width on 1 meaning it only takes up 1 grid square. However, the 0 button takes up 2 grid squares.

We can also easily set the button colors through here. This automatically adds the new button to the `buttons` list. We also pass back the reference to the button.

**find\_button** allows us to iterate through the `buttons` list and find the button object by the label.

```
# Some button functions
def button_grid(row, col):
    return Coords(BUTTON_MARGIN * (row + 1) + BUTTON_WIDTH * row + 20,
                  BUTTON_MARGIN * (col + 1) + BUTTON_HEIGHT * col + 40)

def add_button(row, col, label, width=1, color=WHITE, text_color=BLACK):
    pos = button_grid(row, col)
    new_button = Button(x=pos.x, y=pos.y,
                        width=BUTTON_WIDTH * width + BUTTON_MARGIN * (width - 1),
                        height=BUTTON_HEIGHT, label=label, label_font=font,
                        label_color=text_color, fill_color=color, style=Button.ROUNDRECT)
    buttons.append(new_button)
    return new_button

def find_button(label):
    result = None
    for _, btn in enumerate(buttons):
        if btn.label == label:
            result = btn
    return result
```

Now let's create a border using a rectangle since a label doesn't have any borders and a label for the display.

```auto
border = Rect(20, 8, 280, 35, fill=WHITE, outline=BLACK, stroke=2)
calc_display = Label(font, text="0", color=BLACK)
calc_display.y = 25
```

Now we'll create all of the buttons using those handy functions.

```
clear_button = add_button(0, 0, "AC")
add_button(1, 0, "+/-")
add_button(2, 0, "%")
add_button(3, 0, "/", 1, ORANGE, WHITE)
add_button(0, 1, "7")
add_button(1, 1, "8")
add_button(2, 1, "9")
add_button(3, 1, "x", 1, ORANGE, WHITE)
add_button(0, 2, "4")
add_button(1, 2, "5")
add_button(2, 2, "6")
add_button(3, 2, "-", 1, ORANGE, WHITE)
add_button(0, 3, "1")
add_button(1, 3, "2")
add_button(2, 3, "3")
add_button(3, 3, "+", 1, ORANGE, WHITE)
add_button(0, 4, "0", 2)
add_button(2, 4, ".")
add_button(3, 4, "=", 1, ORANGE, WHITE)
```

After that, we'll add all of those items to our main group.

```python
calc_group.append(border)
calc_group.append(calc_display)
for b in buttons:
    calc_group.append(b)
```

Now it's time to create the calculator object based on the Calculator class that we'll discuss below. We pass in a reference to a few UI elements that the class will handle. Label Offset is used because the label does not automatically right-align, so we need to adjust it every time we change the label text.

```
calculator = Calculator(calc_display, clear_button, LABEL_OFFSET)
```

Finally, we have our main loop, which mostly handles scanning buttons when pressed and selecting/deselecting the buttons. There is a small delay at the end to allow help debounce the touchscreen presses. The `_`&nbsp;is used in the for loop as a placeholder since we won't be using the variable returned.

```
button = ""
while True:
    point = ts.touch_point
    if point is not None:
        # Button Down Events
        for _, b in enumerate(buttons):
            if b.contains(point) and button == "":
                b.selected = True
                button = b.label
    elif button != "":
        # Button Up Events
        last_op = calculator.get_current_operator()
        op_button = find_button(last_op)
        # Deselect the last operation when certain buttons are pressed
        if op_button is not None:
            if button in ('=', 'AC', 'CE'):
                op_button.selected = False
            elif button in ('+', '-', 'x', '/') and button != last_op:
                op_button.selected = False
        calculator.add_input(button)
        b = find_button(button)
        if b is not None:
            if button not in ('+', '-', 'x', '/') or button != calculator.get_current_operator():
                b.selected = False
        button = ""
    time.sleep(0.05)
```

## Calculator Class
For the **Calculator** class, we'll take a look at each of the functions. The first function actually sits outside of the class and passes the calculating over to CircuitPython using the `eval()`&nbsp;function. This could optionally be changed to just use simple math, but this way we don't need to do any string parsing.

```
def calculate(number_one, operator, number_two):
    result = eval(number_one + operator + number_two)
    if int(result) == result:
        result = int(result)
    return str(result)
```

The ` __init__ ()` function just initializes all of the parameters and places the calculator in an **All Clear** state.

```
    def __init__(self, calc_display, clear_button, label_offset):
        self._error = False
        self._calc_display = calc_display
        self._clear_button = clear_button
        self._label_offset = label_offset
        self._accumulator = "0"
        self._operator = None
        self._equal_pressed = False
        self._operand = None
        self._all_clear()
```

The `get_current_operator()` function is used by the main loop to assist with selecting and deselecting the current operator buttons on the calculator.

```
    def get_current_operator(self):
        operator = self._operator
        if operator == "*":
            operator = "x"
        return operator
```

The `_all_clear()` function sets all of the variables to their initial state. Note that the functions that start with an underscore are private functions and only meant to be used inside the class.

```
    def _all_clear(self):
        self._accumulator = "0"
        self._operator = None
        self._equal_pressed = False
        self._clear_entry()
```

The `_clear_entry()` function sets current clears the current operand to its initial state and changes the CE button to AC.

```
    def _clear_entry(self):
        self._operand = None
        self._error = False
        self._set_button_ce(False)
        self._set_text("0")
```

While not strictly necessary, the&nbsp;`_set_button_ce()`&nbsp;function makes it easy to change the CE/AC button text. Deselecting the clear button was done here because it would sometimes get confused with the button label changing.

```
    def _set_button_ce(self, entry_only):
        self._clear_button.selected = False
        if entry_only:
            self._clear_button.label = "CE"
        else:
            self._clear_button.label = "AC"
```

The `_set_text()` function sets the display text and shifts it over to allow right-alignment of text.&nbsp;The `_`&nbsp;is used as a placeholder since we won't be using the variables returned.

```
    def _set_text(self, text):
        self._calc_display.text = text
        _, _, screen_w, _ = self._calc_display.bounding_box
        self._calc_display.x = self._label_offset - screen_w
```

The `_get_text()` function is the counterpart to the **\_set\_text()** function and makes the code more readable.

```
    def _get_text(self):
        return self._calc_display.text
```

The `_handle_number()` function handles the 0-9 digits as input. The variables that are set depend on the current state of other variables.

```
    def _handle_number(self, input_key):
        display_text = self._get_text()
        if self._operand is None and self._operator is not None:
            display_text = ""
        elif self._operand is not None and self._operator is not None and self._equal_pressed:
            self._accumulator = self._operand
            self._operator = None
            self._operand = None
            display_text = ""
        elif display_text == "0":
            display_text = ""
        display_text += input_key
        self._set_text(display_text)
        if self._operator is not None:
            self._operand = display_text
        self._set_button_ce(True)
        self._equal_pressed = False
```

Likewise, the `_handle_operator()`&nbsp;function handles the `+`, `-`, `x`, and `/`&nbsp;operator buttons. Since `x` isn't a valid operation, we just check for that and change it to `*` for multiplication.

```
    def _handle_operator(self, input_key):
        if input_key == "x":
            input_key = "*"
        if self._equal_pressed:
            self._operand = None
        if self._operator is None:
            self._operator = input_key
        else:
            # Perform current calculation before changing input_keys
            if self._operand is not None:
                self._accumulator = calculate(self._accumulator, self._operator, self._operand)
                self._set_text(self._accumulator)
            self._operand = None
            self._operator = input_key
        self._accumulator = self._get_text()
        self._equal_pressed = False
```

The `_handle_equal()` function handles the equal button. This also handles pressing equal multiple times to continue the current operation.

```
    def _handle_equal(self):
        if self._operator is not None:
            if self._operand is None:
                self._operand = self._get_text()
            self._accumulator = calculate(self._accumulator, self._operator, self._operand)
        self._set_text(self._accumulator)
        self._equal_pressed = True
```

The `_update_operand()`&nbsp;makes sure the operand matches the number displayed for a couple of operations.

```
    def _update_operand(self):
        if self._operand is not None:
            self._operand = self._get_text()
```

Finally we have the `add_input()` function, which handles sending the input to the appropriate handler function. It also handles the error state such as a **Divide by Zero** error or displaying too many numbers.

```
def add_input(self, input_key):
        try:
            if self._error:
                self._clear_entry()
            elif input_key == "AC":
                self._all_clear()
            elif input_key == "CE":
                self._clear_entry()
            elif self._operator is None and input_key == "0":
                pass
            elif len(input_key) == 1 and 48 &lt;= ord(input_key) &lt;= 57:
                self._handle_number(input_key)
            elif input_key in ('+', '-', '/', 'x'):
                self._handle_operator(input_key)
            elif input_key == ".":
                if not input_key in self._get_text():
                    self._set_text(self._get_text() + input_key)
                    self._set_button_ce(True)
                    self._equal_pressed = False
            elif input_key == "+/-":
                self._set_text(calculate(self._get_text(), "*", "-1"))
                self._update_operand()
            elif input_key == "%":
                self._set_text(calculate(self._get_text(), "/", "100"))
                self._update_operand()
            elif input_key == "=":
                self._handle_equal()
        except (ZeroDivisionError, RuntimeError):
            self._all_clear()
            self._error = True
            self._set_text("Error")
```

## Conclusion

We hope you enjoyed this guide and are able to make use of either a Python-based Calculator, the User Interface Elements, or both.


## Featured Products

### Adafruit PyPortal - CircuitPython Powered Internet Display

[Adafruit PyPortal - CircuitPython Powered Internet Display](https://www.adafruit.com/product/4116)
 **PyPortal** , our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Make custom touch screen interface GUIs, all open-source, and Python-powered using&nbsp;tinyJSON / APIs to get news, stock, weather, cat photos,...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4116)
[Related Guides to the Product](https://learn.adafruit.com/products/4116/guides)
### AdaBox011 - PyPortal

[AdaBox011 - PyPortal](https://www.adafruit.com/product/4061)
Reach out beyond your desk - to the stars and beyond - with **PyPortal**! This ADABOX features a new, easy-to-use IoT device that allows you to customize and create your very own "Internet of Things" portal. We take CircuitPython to the max, pairing a SAMD51 chip with a...

No Longer Stocked
[Buy Now](https://www.adafruit.com/product/4061)
[Related Guides to the Product](https://learn.adafruit.com/products/4061/guides)
### Adafruit PyPortal Desktop Stand Enclosure Kit

[Adafruit PyPortal Desktop Stand Enclosure Kit](https://www.adafruit.com/product/4146)
PyPortal is&nbsp;our easy-to-use IoT device that allows you to create all the things for the “Internet of Things” in minutes. Create little pocket universes of joy that connect to something good.

And now that you've made a cool internet-connected project...

In Stock
[Buy Now](https://www.adafruit.com/product/4146)
[Related Guides to the Product](https://learn.adafruit.com/products/4146/guides)
### Pink and Purple Braided USB A to Micro B Cable - 2 meter long

[Pink and Purple Braided USB A to Micro B Cable - 2 meter long](https://www.adafruit.com/product/4148)
This cable is&nbsp;super-fashionable&nbsp;with a woven pink and purple Blinka-like pattern!

First let's talk about the cover and over-molding. We got these in custom colors, and if you&nbsp;_have_&nbsp;to have visible cables, then you might as well have the nicest fabric-bound...

No Longer Stocked
[Buy Now](https://www.adafruit.com/product/4148)
[Related Guides to the Product](https://learn.adafruit.com/products/4148/guides)
### Fully Reversible Pink/Purple USB A to micro B Cable - 1m long

[Fully Reversible Pink/Purple USB A to micro B Cable - 1m long](https://www.adafruit.com/product/4111)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also fully reversible! That's right, you will save _seconds_ a day by not having to flip the cable around.

First let's talk about the cover and over-molding. We got these...

In Stock
[Buy Now](https://www.adafruit.com/product/4111)
[Related Guides to the Product](https://learn.adafruit.com/products/4111/guides)

## Related Guides

- [Adafruit PyPortal - IoT for CircuitPython](https://learn.adafruit.com/adafruit-pyportal.md)
- [PyPortal Event Count-Up Clock](https://learn.adafruit.com/pyportal-event-count-up-clock.md)
- [A Logger for CircuitPython](https://learn.adafruit.com/a-logger-for-circuitpython.md)
- [PyPortal Oblique Strategies](https://learn.adafruit.com/pyportal-oblique-strategies.md)
- [PyPortal LIFX Lighting Controller ](https://learn.adafruit.com/pyportal-lifx-lighting-controller.md)
- [PyPortal Wall Mount](https://learn.adafruit.com/pyportal-wall-mount.md)
- [PyPortal Voice Controlled Smart Switch and Time Display](https://learn.adafruit.com/pyportal-voice-controlled-smart-switch-and-time-display.md)
- [Making a PyPortal User Interface with DisplayIO](https://learn.adafruit.com/making-a-pyportal-user-interface-displayio.md)
- [IoT Power Outlet with CircuitPython and Adafruit IO](https://learn.adafruit.com/adafruit-io-a-c-power-relay.md)
- [Data Logging IoT Weight Scale](https://learn.adafruit.com/data-logging-iot-weight-scale.md)
- [PyPortal Guitar Tuner](https://learn.adafruit.com/pyportal-guitar-tuner.md)
- [PyPortal YouTube Views and Subscribers Display](https://learn.adafruit.com/pyportal-youtube-views-and-subscribers-display.md)
- [PyPortal GitHub Stars Trophy](https://learn.adafruit.com/pyportal-github-stars-trophy.md)
- [Cleveland Museum of Art PyPortal Frame](https://learn.adafruit.com/cleveland-museum-of-art-pyportal-frame.md)
- [A Floppy Thumb Drive with a Color File Icon Display](https://learn.adafruit.com/a-floppy-thumb-drive-with-a-color-file-icon-display.md)
- [PyPortal Air Quality Display](https://learn.adafruit.com/pyportal-air-quality-display.md)
