Skip to main content

Build your first app

note

Estimated time: 30 minutes

Introduction

In this tutorial, you will learn how to create an app with VIKTOR. We will start from scratch and make an app with an interactive 3D model of a beam and show how it deforms when loaded; and don't worry, you don't need any engineering knowledge to get this done.

What the app looks like once you complete this tutorial

In this tutorial, we will:

  1. Create, install and start an empty app
  2. Create a 3D model of the beam
  3. Add input fields to your app
  4. Connect the input fields to the 3D model
  5. Add a graph using Plotly to show how to install and use external packages within VIKTOR
tip

You can find the complete code below.

Pre-requisites

  • You completed the installation section.
  • You have some experience with reading Python code

During the tutorial, we added some links to additional information; but don't let them distract you too much. Stay focused on completing the tutorial. After this, you will know everything you need to create your own apps.

Need help?

Need help with installing VIKTOR or creating apps? The fastest way to get support is by using our Community Forum, where experienced developers are ready to help you!

Creating, installing and starting an empty app

Before we start building the app, we will first ask VIKTOR to create a folder with an empty app containing all files you need, and then install it. We will use this app as a basis to make your first app. So, let's get started!

  1. Open a command-line shell (Command Prompt in Windows) from within your app folder (let's assume it's called 'viktor-apps'). On Windows, the easiest way to do this is to navigate to the folder in the file explorer, click in the address bar, type "cmd", and hit enter:

    Open a command-line shell in the 'viktor-apps' folder

  2. On the command-line, write the following command:

    viktor-cli create-app my-first-app

    This command creates an app with the name 'my-first-app'. After executing the command you will find a folder called 'my-first-app' inside the 'viktor-apps' folder.

  3. Open PyCharm (or Visual Studio Code, or any other IDE) and open the 'my-first-app' folder. We recommend using the terminal that is integrated in the IDE. In PyCharm the terminal window can be opened using the 'Terminal' tab at the bottom (or with Alt+F12):

    The current working directory of the integrated terminal is automatically set to the 'my-first-app' folder.

    note

    In case you prefer to keep using the system command-line shell, navigate to the 'my-first-app' folder manually:

    cd my-first-app
  4. Before installing a new app, you need to clear the database by running the following command:

    viktor-cli clear
  5. Now you can install your app using the command below. Installation can take up to 3 minutes, so just have a little patience and read the information below.

    viktor-cli install

    The command tells VIKTOR to install the app stored inside the folder where you run the command. That is why we ask you to navigate to the app folder first.

    You will know that the installation is ready when you see this at the bottom:

  6. Start the app with:

    viktor-cli start

    Congratulations, your app is up and running! However, do not close your command-line shell; otherwise, your app will stop.

    note

    If you closed your command-line shell, open a new one in the application folder. You can start your app again using viktor-cli start

  7. Open your app in your browser by visiting the URL shown in the command-line shell:

  8. Log in with your username and password, you will be redirected to the app's homepage.

    note

    If you see the "Workspaces" menu upon logging in, click the "Open" button on the "Development" workspace. After opening, you will be redirected to the app's homepage.

Great, we are now inside the app. Now let's go to the fun part: start building!

Creating a 3D model

In this part of the tutorial, we will create a 3D model of the beam using the geometry module of the VIKTOR SDK. Before we start building the 3D model, let’s check out what the current application looks like.

In your browser, click on "+ Create", as shown in the image below.

Create a new design

Fill in a name in the pop-up, and click “Create and open”, to create the object and open its editor. You should now see an empty page. So let’s start creating our 3D model.

Add a 3D model to your app

  1. Use your favourite IDE to open the file app.py found in the folder viktor-apps/my-first-app

  2. Edit app.py, so that it looks like this:

    from viktor import ViktorControllerfrom viktor.geometry import SquareBeamfrom viktor.views import GeometryView, GeometryResultclass Controller(ViktorController):    label = "My Beam"    @GeometryView("My 3D model", duration_guess=1)    def beam_visualisation(self, params, **kwargs):        beam = SquareBeam(length_x=0.3, length_y=0.5, length_z=3)        return GeometryResult(beam)
  3. Save the app.py file. Go to your app, refresh the page, and see your 3D model magically appear!

How does it work?

Let's shortly discuss how this works. If you want to show any results, you need to do this in a view. You can use different types of views, depending on what you want to show, but you always need to add a view inside a controller class (a class that inherits from ViktorController). The controller class is your app's brain that controls everything shown in your app. In this example app there is only one such class (called Controller), but an app might consist of more than one controller class (with different names).

  • By adding @GeometryView, we tell VIKTOR that the function below will be used to create and to show a 3D model.
  • "My 3D model" is the name that the view gets inside your app.
  • We named the method (function) beam_visualisation, but you can give it any name you want.
  • beam_visualization gets the input (self, params, **kwargs). This is mandatory for all views. We will explain this later.
  • SquareBeam creates your beam. This is a standard geometry class inside the viktor.geometry module. There are many other classes you can use to construct 3D models.
  • GeometryResult contains the objects that you would like to view in your GeometryView, which in this case is beam.

Adding input fields

We created a 3D model in the last section, but what if you want a user to change the geometry dynamically? Using VIKTOR’s input fields you can. Let’s add a few input fields to change the length, width and height of your beam.

  1. Go to your app.py file.

  2. After the imports we previously added, but before class Controller, add the following code snippet:

    ...from viktor.parametrization import ViktorParametrization, NumberFieldclass Parametrization(ViktorParametrization):    height = NumberField("Height", default=0.4, step=0.1, suffix="m")    width = NumberField("Width", default=0.2, step=0.1, suffix="m")    length = NumberField("Length", default=1, min=1, max=10, step=0.5, suffix="m", variant="slider")class Controller(ViktorController):  # <---- This line is just for your reference    ...
  3. Now, we need to tell the controller that you want to show these fields. Add the line parametrization = Parametrization inside class Controller, like this:

    ...
    class Controller(ViktorController):
    label = 'My Beam'
    parametrization = Parametrization # <-- Add this line
    ...
  4. Again, save the app.py file. Go to your app, refresh the page and see the NumberFields appear in your app.

Did you notice that changing the values does not modify the 3D model? This is because we have not connected the NumberFields to your 3D model yet. We’ll do this next.

How does it work?

Input fields allow the user to provide input in your app, and there are more than 20 different input fields you can use! You need to add input fields in a parametrization class (a class that inherits from ViktorParametrization), so that VIKTOR can recognize them. Additionally, you need to tell your controller which parametrization you want to use. Because, again, the controller determines what is shown in your app.

  • We created a parametrization class called Parametrization, but we could use any name.
  • We added 3 parameters: height, width and length, and coupled them to the different NumberFields shown in the app.
  • You can configure each NumberField by defining some input like: default, min, max, step and suffix. You can go ahead and explore what they do.
  • The line parametrization = Parametrization inside class Controller, tells VIKTOR to show all the input fields contained in Parametrization.

Connecting the input fields to the 3D model

We’ll connect the input fields to the 3D model to make it dynamic:

  1. Find the line beam = SquareBeam(length_x=0.3, length_y=0.5, length_z=3) and replace it with:

    beam = SquareBeam(length_x=params.length, length_y=params.width, length_z=params.height)
  2. This time, instead of refreshing your app, just change some input values. Did you see that? VIKTOR updated the app, and your 3D model is now dynamic!

Awesome, we already created a fully functioning parametrized model of a beam including a 3D visualisation! If you were wondering whether this is a good time: Yes, this is a perfect moment to give yourself a pat on the back! 🎉

How does it work?

So what happens in the background? Each time you change an input parameter, VIKTOR reruns the corresponding code and shows the results in the view. In technical words, your code is stateless.

  • All the input parameters are stored in the variable params. That is why it is important to pass it to def beam_visualisation(self, params, **kwargs).
  • params is a Munch, which is like a Python dictionary. You can access the data inside params using dot-style notation (as well as the usual ['key'] notation). So, for example, to access height inside params, you write params.height.

Let’s see what is inside params to understand this better:

  1. Add the following code somewhere inside def beam_visualisation(self, params, **kwargs).

    print("This is the current parametrization: ", params)
  2. Go to your app and change some parameters to update your app.

  3. Look in the command-line shell where your application is running. You will see params printed:

    This is the current parametrization: Munch({'height': 0.5, 'width': 0.2, 'length': 1})

Adding a graph using Plotly

You are probably familiar with some great packages that are available within the Python ecosystem, such as Numpy, Pandas and Matplotlib. This section teaches you how to use them inside a VIKTOR app. We will use Numpy to do a simple calculation and show the results in a Plotly graph.

  1. Open your app.py file, and let's import the libraries at the top of the file:

    import numpy as np
    import plotly.graph_objects as go

    from viktor.views import PlotlyView, PlotlyResult

    As usual, save the app.py file and update your app. Do you see something new? Nothing there, right? No worries, it is not your fault. If you look into the command-line shell, you will see: ERROR: No module named 'plotly'. So, next time your app does not work as expected, remember that you can find additional information in the command-line shell.

  2. To use external libraries, you need to add them to your app requirements. Open the requirements.txt file found in your app folder (my-first-app).

    The first line should be # viktor==X.X.X. Under this line, add:

    plotly
    numpy
  3. You need to install and start your app again because you added a new library. In your command-line shell, stop the app that is still running using Ctrl+C (inside the command-line shell, this is not copy but stops a process). As at the beginning of the tutorial, use the following commands in your command-line shell:

    viktor-cli install

    You can see which packages are installed in the command-line shell:

    And then,

    viktor-cli start
  4. Now let's show the results in a Plotly graph using the newly added libraries. Add the following method at the end of your class Controller. Make sure the indentation is correct.

        @PlotlyView("My Graph", duration_guess=1)    def displacement_plot(self, params, **kwargs):        # Some engineering math, for a simply supported beam        distributed_load = 100  # N/m        L = params.length  # m        I = 1.0 / 12.0 * params.width * params.height ** 3  # m^4        E = 210e9  # Pa        x = np.linspace(0, L)  # m        displacement = -(distributed_load * x) / (24 * E * I) * (L**3 - 2 * L * x**2 + x**3) * 1000  # mm        # Create a plot with Plotly        fig = go.Figure(go.Scatter(x=x, y=displacement))        fig.update_layout(xaxis_title="Location (m)", yaxis_title="Displacement (mm)")        return PlotlyResult(fig.to_json())
  5. Save the app.py file and update your app. There it is, your awesome app with an interactive 3D model and a nice Plotly graph!

All code together

That's it, you've made it to the end, and you have done a fantastic job 🎉 🎉 🎉

Here's the complete code of the app:

# First import the parts we needimport numpy as npimport plotly.graph_objects as gofrom viktor import ViktorControllerfrom viktor.geometry import SquareBeamfrom viktor.parametrization import ViktorParametrization, NumberFieldfrom viktor.views import GeometryView, GeometryResult, PlotlyView, PlotlyResult# This is the parametrization, where you define the input fields.class Parametrization(ViktorParametrization):    height = NumberField("Height", default=0.4, step=0.1, suffix="m")    width = NumberField("Width", default=0.2, step=0.1, suffix="m")    length = NumberField("Length", default=1, min=1, max=10, step=0.5, suffix="m", variant="slider")# This is the Controller class, add everything you want to show in your app here.class Controller(ViktorController):    label = "My Beam"    parametrization = Parametrization    @GeometryView("My 3D model", duration_guess=1)    def beam_visualisation(self, params, **kwargs):        beam = SquareBeam(length_x=params.length, length_y=params.width, length_z=params.height)        print("This is the current parametrization:", params)        return GeometryResult(beam)    @PlotlyView("My Graph", duration_guess=1)    def displacement_plot(self, params, **kwargs):        # Some engineering math, for a simply supported beam        distributed_load = 100  # N/m        L = params.length  # m        I = 1.0 / 12.0 * params.width * params.height ** 3  # m^4        E = 210e9  # Pa        x = np.linspace(0, L)  # m        displacement = -(distributed_load * x) / (24 * E * I) * (L**3 - 2 * L * x**2 + x**3) * 1000  # mm        # Create a plot with Plotly        fig = go.Figure(go.Scatter(x=x, y=displacement))        fig.update_layout(xaxis_title="Location (m)", yaxis_title="Displacement (mm)")        return PlotlyResult(fig.to_json())

To infinity and beyond

Congratulations! You managed to make your first app from scratch! You have:

  • Created a blank app
  • Cleared the database, installed and started your app
  • Defined views in a controller class
  • Defined input fields in a parametrization class
  • Linked user input to your 3D model
  • Installed and used external packages
  • Created a plot

Of course, the journey doesn't end here. In the next section, you can see the different paths you can follow in your journey to learn more about VIKTOR.

tip

If you have your own scripts you would like to implement, follow the same steps as in this tutorial, yet insert your own code instead of the code supplied here. Furthermore, you can experiment with different input fields and different types of views.