Create your first app
- 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
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.
If you encounter any problems during this tutorial: our developers are ready to help you on the Community Forum
You can find the complete code at the bottom.
Creating an app
We will start by creating a folder with an empty app template, as a basis from which we will create your first app.
Create a folder on your hard disk in which to generate the empty app template (and any future apps):
- Windows
- Linux
Suggestion:
C:\Users\<username>\viktor-apps
tipRight-click on the created folder and pin to "Quick Access" to enable easy navigation to this folder.
Suggestion:
~/viktor-apps
cautionMake sure the CLI has full rights in the apps directory, by changing its permissions with:
sudo chmod -R 777 ~/viktor-apps
Open your IDE of preference and create a new project:
- PyCharm
- Visual Studio Code
- Gitpod
- Open
File -> New Project...
- Set location to:
- Windows:
C:\Users\<username>\viktor-apps\my-first-app
- Linux:
~/viktor-apps/my-first-app
- Windows:
- 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:
- Create a folder with the name of your app (e.g.
my-first-app
) in your newly created app folder:- Windows:
C:\Users\<username>\viktor-apps\my-first-app
- Linux:
~/viktor-apps/my-first-app
- Windows:
- In Visual Studio Code open the folder with
File -> Open Folder...
- Open the command-line shell by selecting
Terminal -> New Terminal
(or by pressing Ctrl+Shift+`). The current working directory of the integrated shell is automatically set to the 'my-first-app' folder.
- Create a new workspace (see the remote development guide for more information)
- Open the command-line on the Terminal. The current working directory of the integrated shell is automatically set to the newly created project folder.
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 .
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
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:
Start the app with:
viktor-cli start
noteThe 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.
Open your app in your browser by visiting the URL shown in the command-line shell:
Finally, log in with your username and password, you will be redirected to the app's homepage.
noteIf 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
Use your favourite IDE to open the file
app.py
found in the folderviktor-apps/my-first-app
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)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 theviktor.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 yourGeometryView
, which in this case isbeam
.
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.
Go to your
app.py
file.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
...Now, we need to tell the controller that you want to show these fields. Add the line
parametrization = Parametrization
insideclass 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
...Again, save the
app.py
file. Go to your app, refresh the page and see theNumberField
s 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
NumberField
s 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
andlength
, and coupled them to the differentNumberField
s shown in the app. - You can configure each
NumberField
by defining some input like:default
,min
,max
,step
andsuffix
. You can go ahead and explore what they do. - The line
parametrization = Parametrization
insideclass Controller
, tells VIKTOR to show all the input fields contained inParametrization
.
Connecting the input fields to the 3D model
We’ll connect the input fields to the 3D model to make it dynamic:
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)
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 todef beam_visualisation(self, params, **kwargs)
. params
is aMunch
, 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 accessheight
insideparams
, you writeparams.height
.
Let’s see what is inside params
to understand this better:
Add the following code somewhere inside
def beam_visualisation(self, params, **kwargs)
.print("This is the current parametrization: ", params)
Go to your app and change some parameters to update your app.
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.
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, PlotlyResultAs 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.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
).Below the line
viktor==X.X.X
, add:plotly
numpyYou 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
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())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())
Publishing the app
Congratulations! You managed to make your first app from scratch! You have:
- Learned how to use the CLI to:
- create a template app
- clear the database
- install and start 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
- Used an external package to create a plot
A logical next step is to publish the application, such that users can benefit from it online. For this step we will refer you to the publishing guide.
To infinity and beyond
Nice! You are now able to create an app and share it online with the world!
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.
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.