Skip to content

Single-Stage App: Area Calculator

Note

Estimated Time: 30 min

Pre-Requisites

Useful Links

Introduction

Welcome to the first tutorial of our platform! In this example, you'll learn how to create a simple application that calculates the area of geometric shapes—specifically rectangles and circles. This app will also showcase how you can import external assets, such as images, to make your app more engaging.

This tutorial is divided into the following sections:

What you'll Learn

  • Create User Inputs: Add input fields where users can enter the dimensions of shapes, such as the length and width for rectangles and the diameter for circles.
  • Implement Calculations: Write calculations to compute the area of the selected shape.
  • Enhance the User Interface: Incorporate images and text elements to create a user-friendly interface.
  • Use Control Flow: Utilize Python control flow statements to make the app dynamic, showing different inputs, equations, and images depending on whether the user selects a rectangle or a circle.

Note

In this tutorial we'll be setting up everything from scratch in order to explain each step more clearly. However, you'll probably want to use the code of an already existing app as a starting point when building new apps.

1. Setting Up the App

Create the App Directory

By now, you should have an App Collection directory on your machine. If you don't, please follow the steps here to create one.

First, navigate to the apps directory of your collection:

C:\Users\<your-username>/<name-of-app-collection>/apps

Next, create a new directory named sample_area_calculation. Inside this directory, create an empty .py file named sample_area_calculation.py — this will be our app file. Create a subdirectory called resources to store images used in the app. And finally, create the app_meta.yaml and README.md files. The final directory structure should look like this:

<name-of-app-collection>/
├── apps/
│   ├── training_boilerplate/
│   │   ├── app_meta.yaml
│   │   ├── README.md
│   │   └── training_boilerplate.py
│   └── sample_area_calculation/
│       ├── app_meta.yaml
│       ├── README.md
│       ├── sample_area_calculation.py
│       └── resources/
├── typings/
└── collection_requirements.yaml

Add the App's Metadata

Add the following two files to the root directory of the app to set the app's metadata. This information will be available in the App Details section of the app's page. Click here to learn more about these files.

app_meta.yaml

app_title: Area Calculation Rectangle or Circle
discipline: Training
subcategory:
  - Generic
app_status: Verified
reviewers:
  - None
authors:
  - None
is_multi_stage: false

README.md

# Area Calculation Rectangle or Circle

Calculates the area of either a rectangle of a circle.

Tip

Use the README.md file to present users of the app with more information - its features, assumptions, how to use the app, etc. It'll then be rendered nicely in Markdown!

Code the App's Skeleton

First, import the stub files from the typings directory. This step is optional but highly recommended for code auto-completion in your editor. Add the following line at the top of your sample_area_calculation.py file:

from v0_0_1.compute_core.app_imports.basic_imports import * # type: ignore

Next, create a class inheriting from AppStageCompute. In the class's constructor, call the parent class constructor to initialize internal variables of the app:

class SampleAreaCalculation(AppStageCompute):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

Finally, add the three AppStageCompute(1) execution methods with empty bodies:

  1. If you want to know more about these execution methods, please check this article
def register_parameters(self):
    ...

def build_parameters(self):
    ...

def build_report(self):
    ...

The complete app's skeleton should look like this:

from v0_0_1.compute_core.app_imports.basic_imports import * # type: ignore


class SampleAreaCalculation(AppStageCompute):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def register_parameters(self):
        ...

    def build_parameters(self):
        ...

    def build_report(self):
        ...

Understanding App Execution Flow

To learn more about how an app is processed under the hood and the execution order of the different AppStageCompute methods, check this article.

Add the App to the Collection Requirements

To load the app in the App Builder, you need to add it to the collection_requirements.yaml file. Add the following entry to the file:

- app_dir: sample_area_calculation
  entry_module: sample_area_calculation
  entry_class: SampleAreaCalculation

In the Compute.build Engine, go to the App Building tab and select your app's class name from the Entry App dropdown menu. If you used the same name as this tutorial, it should appear as SampleAreaCalculation.

Troubleshooting

If the app's class doesn't appear in the dropdown menu, try clicking on the Refresh Folder button to reload the collection. This is also useful when you change the app's class name.

You can now go to the App Builder in compute.build and start building the app! It should look empty since haven't added anything yet:

empty-app-builder

Also, check the App Details tab to see the app's metadata:

empty-app-builder

2. Adding Inputs for a Rectangle

Registering the Input Parameters

Let's begin by defining the two inputs necessary to compute the area of a rectangle:

  • Width of the rectangle
  • Length of the rectangle

First, register these inputs within the register_parameters method. We can add primitive inputs (basic data types like int, float, string, or bool) using the input_parameter method with appropriate UI elements.

The complete register_parameters method should look like this:

def register_parameters(self):
    self.input_parameter(
        var_name='width',
        fullname='Width of rectangle',
        bind_ui=UiNumberInput(min_val=10, max_val=1000, step=1.0, default=100),
    )
    self.input_parameter(
        var_name='length',
        fullname='Length of rectangle',
        bind_ui=UiRange(min_val=10, max_val=1000, step=1.0, default=200),
    )

In this example, both inputs are numerical, so we use UiNumberInput and UiRange UI elements. We set constraints like minimum and maximum values, step size, and default values.

Registering parameters

The inputs registered within register_parameters are not immediately rendered in the UI. Instead, they're mainly used to define the Parameters section of the App Details, which is helpful when calling this app within another app.

Exposing the Input Parameters

To display the registered inputs in the UI, you need to expose them within the build_parameters method using expose_parameter:

def build_parameters(self):
    self.expose_parameter(var_name="width")
    self.expose_parameter(var_name="length")

Exposing parameters makes them available in the app's memory, allowing you to use them in calculations and retrieve them as Python variables.

After clicking the INIT UI button in the App Builder, the input parameters should appear:

Exposed Inputs

3. Implementing the Area Calculation

Now that we have our inputs, let's add the area calculation for our rectangle using the width and length parameters.

In the build_report method, use the add_calc method to add a calculation. We'll use the equation argument to define the calculation, as it renders nicely in mathematical format in the platform and reports.

def build_report(self):
    self.add_calc(
        var_name='A',
        fullname='Area of the shape',
        equation="{width} * {length}",
    )

Using Equations

When using the equation argument, you can include parameters by surrounding their var_name with curly braces, like {width}.

To compute the area, press the Compute button in the platform:

Rectangle Area Calculation

4. Adding Inputs and Calculation for a Circle

Currently, our app calculates the area of a rectangle. Let's extend it to also calculate the area of a circle.

Inputs for a circle

To compute the area of a circle we need one additional input parameter:

  • Diameter of the circle

First, register the new input and then expose it in build_parameters:

def register_parameters(self):
    ... 

    self.input_parameter(
        var_name='diameter',
        fullname='Diameter of Circle',
        bind_ui=UiRange(min_val=10, max_val=1000, step=1.0, default=200),
    )

def build_parameters(self):
    ...

    self.expose_parameter("diameter")

If you refresh the UI, you'll see all three inputs. However, displaying inputs for both shapes simultaneously may confuse users. Let's make the app dynamic by adding an option for the user to select the shape and then show the appropriate inputs.

Add a boolean UI element to let the user choose between a rectangle and a circle:

def register_parameters(self):
    self.input_parameter(
        var_name='is_rectangle',
        fullname='Is the object rectangle?',
        bind_ui=UiBoolean(flavour='checkbox', default=True),
    )

    ...

def build_parameters(self):
    self.expose_parameter(var_name="is_rectangle")

    ...

By retrieving the value of is_rectangle using get_result, we can control which parameters to expose to the user.

The updated build_parameters section should look something like this:

def build_parameters(self):
    self.expose_parameter(var_name="is_rectangle")
    _is_rectangle = self.get_result("is_rectangle")
    if _is_rectangle:
        self.expose_parameter(var_name="width")
        self.expose_parameter(var_name="length")
    else:
        self.expose_parameter(var_name="diameter")

The app should now include a toggle that dynamically changes the displayed inputs:

Dynamic Inputs

Calculate the Area of a Circle

We want a single calculation named A that adjusts based on the selected shape:

  • Rectangle: is_rectangle=True rectangle_equation="{width} * {length}"
  • Circle: is_rectangle=False circle_equation="(math.pi * {diameter}**2) / 4"

Update the build_report method to include both equations and select the appropriate one based on _is_rectangle:

def build_report(self):
    _is_rectangle = self.get_result('is_rectangle')

    square_equation = '{width}*{length}'
    circle_equation = '(math.pi * {diameter}**2) / 4'
    equation = square_equation if _is_rectangle else circle_equation
    self.add_calc(
        var_name='A',
        fullname='Area of the shape',
        equation=equation,
    )

Using math from the standard library

You can use any method from Python's standard math library in equations, like math.pi.

Now, when you run the computation, the correct equation will be used based on the selected shape:

Dynamic Area Calculation

Success! We have now a functional version of our app. Let's spice it up with some text and image elements.

5. Enhancing the App with Text and Images

Adding Instructions

To improve user experience, let's add some instructions using the add_text method in the build_parameters method:

def build_parameters(self):
    self.add_text('''
    # Area calculator instructions

    1. please choose whether you want to calculate the area of a rectangle
       or a circle by selecting or not the `is the object rectangle` checkbox.
    2. choose the values for the shape input parameters.
    3. click on "compute" to calculate the area of the chosen shape.
    ''')

    ...

Formatting Multi-line Strings

When using multi-line strings, start the content on a new line to avoid formatting issues in Markdown rendering.

After refreshing the UI, the instructions should appear:

Instructions

Adding Images

Let's include diagrams for both shapes to make the app more informative.

  • Rectangle Diagram

text-element

  • Circle diagram

text-element

Save both images in the resources directory of your app.

Next, update the collection_requirements.yaml file to include these static files:

- app_dir: sample_area_calculation
  entry_module: sample_area_calculation
  entry_class: SampleAreaCalculation
  local_imports:
    static_files:
      - resources.legend_rectangle.png
      - resources.legend_circle.png

Specifying File Paths

In local_imports, the path to the file must be specified from the app's root folder using dotted notation. For example, resources.legend_rectangle.png.

Now, add the images to your app using the add_image method:

def build_parameters(self):
    ...

    self.expose_parameter(var_name="is_rectangle")
    _is_rectangle = self.get_result("is_rectangle")

    image_filepath = "resources/legend_rectangle.png" if _is_rectangle else "resources/legend_circle.png"
    image_caption = "Rectangle diagram" if _is_rectangle else "Circle diagram"
    shape_image = CbImage(filepath=image_filepath,
                          image_renderer=ImageRenderExpandable(),
                          var_name="shape_diagram",
                          image_width=300,
                          image_caption=image_caption,
                          visible=False)
    self.add_image(shape_image)

    ...    

The images should now be displayed in the User Input section:

App with Images

Hiding Inputs from the Report

To make the Detailed Output section less cluttered, you can hide input elements by setting visible=False when registering parameters:

def register_parameters(self):
    ...

    self.input_parameter(
        var_name='diameter',
        fullname='Diameter of Circle',
        bind_ui=UiRange(min_val=10, max_val=1000, step=1.0, default=200),
        visible=False
    )

Visibility and Print Report

Setting visible=False will also hide the parameter in the Print Report.

After setting visible=False for inputs, text, and images, the final app page should look like this:

Final App

The complete app code

Below is the full code for the app:

from v0_0_1.compute_core.app_imports.basic_imports import *  # type: ignore


class SampleAreaCalculation(AppStageCompute):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def register_parameters(self):
        self.input_parameter(
            var_name='is_rectangle',
            fullname='Is the object rectangle?',
            bind_ui=UiBoolean(flavour='checkbox', default=True),
            visible=False
        )
        self.input_parameter(
            var_name='width',
            fullname='Width of rectangle',
            bind_ui=UiNumberInput(min_val=10, max_val=1000,
                                  step=1.0, default=100),
            visible=False
        )
        self.input_parameter(
            var_name='length',
            fullname='Length of rectangle',
            bind_ui=UiRange(min_val=10, max_val=1000, step=1.0, default=200),
            visible=False
        )
        self.input_parameter(
            var_name='diameter',
            fullname='Diameter of Circle',
            bind_ui=UiRange(min_val=10, max_val=1000, step=1.0, default=200),
            visible=False
        )

    def build_parameters(self):
        self.add_text('''
        # Area calculator instructions

        1. please choose whether you want to calculate the area of a rectangle
           or a circle by selecting or not the `is the object rectangle` checkbox.
        2. choose the values for the shape input parameters.
        3. click on "compute" to calculate the area of the chosen shape.
        ''',
                      visible=False
                      )

        self.expose_parameter(var_name="is_rectangle")
        _is_rectangle = self.get_result("is_rectangle")

        image_filepath = "resources/legend_rectangle.png" if _is_rectangle else "resources/legend_circle.png"
        image_caption = "Rectangle diagram" if _is_rectangle else "Circle diagram"
        shape_image = CbImage(filepath=image_filepath,
                              image_renderer=ImageRenderExpandable(),
                              var_name="shape_diagram",
                              image_width=300,
                              image_caption=image_caption,
                              visible=False)
        self.add_image(shape_image)

        if _is_rectangle:
            self.expose_parameter(var_name="width")
            self.expose_parameter(var_name="length")
        else:
            self.expose_parameter(var_name="diameter")

    def build_report(self):
        _is_rectangle = self.get_result('is_rectangle')

        square_equation = '{width}*{length}'
        circle_equation = '(math.pi * {diameter}**2) / 4'
        equation = square_equation if _is_rectangle else circle_equation
        self.add_calc(
            var_name='A',
            fullname='Area of the shape',
            equation=equation,
        )

Wrapping up

Fantastic job! You've learned the essentials of building a Compute.build application, including:

  • Adding inputs and calculations
  • Retrieving data during execution
  • Enhancing the user interface with images and text

You're now ready to create and share powerful applications that automate repetitive tasks.

If you enjoyed this tutorial and want to explore more, we recommend following the Volume of a Box Tutorial, which demonstrates how to execute apps within other apps.

Happy coding! 🚀