Skip to main content

Create your first app

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

Estimated time: 30 minutes

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. Don't worry, you don't need any engineering knowledge to get this done.

What the app looks like once you complete this tutorial

caution

Before continuing, make sure to shut down the running demo app (or any other running apps) by using Ctrl+C or closing the command-line shell.

Need help?

If you encounter any problems during this tutorial: our developers are ready to help you on the Community Forum

tip

You can find the complete code at the bottom.

Creating an empty app

We will start by creating a folder with an empty app template, as a basis from which we will create your first app.

  1. Create a folder on your hard disk in which to generate the empty app template (and any future apps):

    Suggestion: C:\Users\<username>\viktor-apps

    tip

    Right-click on the created folder and pin to "Quick Access" to enable easy navigation to this folder.

  2. Open your IDE of preference and create a new project:

    • Open File -> New Project...
    • Set location to:
      • Windows: C:\Users\<username>\viktor-apps\my-first-app
      • Linux: ~/viktor-apps/my-first-app
    • Select "New environment using virtualenv" (you can keep the default 'Location' and 'Base interpreter')
    • Deselect "Create main.py welcome script"
    • Click "Create"
    • Open the command-line shell by clicking the 'Terminal' tab at the bottom (or by pressing Alt+F12). The current working directory of the integrated shell is automatically set to the 'my-first-app' folder:
  3. On the command-line, write the following to create an app (of type editor) in the current project folder:

    viktor-cli create-app --app-type editor .
  4. Assuming you have run the demo (or any other) app before, you need to clear the database before starting the app. You can do so by running the following command:

    viktor-cli clear
  5. Install the app that resides inside the my-first-app folder by:

    viktor-cli install

    Installation can take up to 5 minutes and is finished when the command-line shows the message:

  6. Start the app with:

    viktor-cli start
    note

    The CLI will establish a connection with VIKTOR and notify when ready by showing the message "App is ready". The command-line shell must be kept open to stay connected. Disconnecting from the app can be done by shutting down the running CLI process (Ctrl+C), or by simply closing the command-line shell. If you (accidentally) shut down the running process, just restart using the start command above.

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

  8. Finally, 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.

If everything went well, the empty app has been installed and is running in your browser. Now let's start with the fun part: building your own app!

Creating a 3D model

We use the empty app to create your first app, by adding new features step-by-step. In this section we will create a 3D model of the beam using the geometry module of the VIKTOR SDK.

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 ViktorController
    from viktor.geometry import SquareBeam
    from viktor.views import GeometryView, GeometryResult


    class Controller(ViktorController):
    viktor_enforce_field_constraints = True # This line prevents a warning and can be ignored for the purpose of this tutorial

    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, NumberField


    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")


    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):
    viktor_enforce_field_constraints = True # This line prevents a warning and can be ignored for the purpose of this tutorial

    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!

Complete code

# First import the parts we need
import numpy as np
import plotly.graph_objects as go

from viktor import ViktorController
from viktor.geometry import SquareBeam
from viktor.parametrization import ViktorParametrization, NumberField
from 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):
viktor_enforce_field_constraints = True # This line prevents a warning and can be ignored for the purpose of this tutorial

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.