Flask Application#

You can use pyvista in to make a flask application to display both static and dynamic plots. See the following examples as well as the source at Flask Example.

Dynamic Example#

../_images/dynamic_flask.gif

Example Dynamic Flask Webpage#

Python Application app.py#

"""Simple flask app to display dynamic threejs generated from pyvista.

Expected paths:
dynamic_ex/
└── app.py
    templates/
    └── index.html

"""
import os

from flask import Flask, render_template, request
import numpy as np

import pyvista
from pyvista import examples

static_image_path = os.path.join('static', 'images')
if not os.path.isdir(static_image_path):
    os.makedirs(static_image_path)


app = Flask(__name__)


@app.route("/")
def index():
    return render_template('index.html')


@app.route("/getimage")
def get_img():
    """Generate a screenshot of a simple pyvista mesh.

    Returns
    -------
    str
        Local path within the static directory of the image.

    """
    # get the user selected mesh option
    meshtype = request.args.get('meshtype')
    color = request.args.get('color')
    style = request.args.get('style')
    print(f"'{style}'")

    # bool types
    show_edges = request.args.get('show_edges') == 'true'
    lighting = request.args.get('lighting') == 'true'
    pbr = request.args.get('pbr') == 'true'
    anti_aliasing = request.args.get('anti_aliasing') == 'true'

    if meshtype == 'Sphere':
        mesh = pyvista.Sphere()
    elif meshtype == 'Cube':
        mesh = pyvista.Cube()
    elif meshtype == 'Bohemian Dome':
        mesh = pyvista.ParametricBohemianDome()
    elif meshtype == 'Cylinder':
        mesh = pyvista.Cylinder()
    elif meshtype == 'Vectors':
        n_points = 20
        points = np.random.random((n_points, 3))
        poly = pyvista.PolyData(points)
        poly['direction'] = np.random.random((n_points, 3))
        poly['direction'] -= poly['direction'].mean(axis=0)  # normalize
        mesh = poly.glyph(geom=pyvista.Arrow(), orient=True, scale=True, factor=0.2)
        # reset color as we will want to see the colors of the arrows
        color = None

    elif meshtype == 'Queen Nefertiti':
        mesh = examples.download_nefertiti()
    elif meshtype == 'Lidar':
        mesh = examples.download_lidar()
    else:
        # invalid entry
        raise ValueError('Invalid Option')

    # generate screenshot
    filename = 'mesh.html'
    filepath = os.path.join(static_image_path, filename)

    # create a plotter and add the mesh to it
    pl = pyvista.Plotter(window_size=(600, 600))
    pl.add_mesh(
        mesh,
        style=style,
        lighting=lighting,
        pbr=pbr,
        metallic=0.8,
        split_sharp_edges=True,
        show_edges=show_edges,
        color=color,
    )
    if anti_aliasing:
        pl.enable_anti_aliasing()
    pl.background_color = 'white'
    pl.export_html(filepath)
    return os.path.join('images', filename)


if __name__ == '__main__':
    app.run()

Ajax Template index.html#

This template should be within the templates directory in the same path as app.py.

This template returns the meshtype parameter back to the get_img method in the flask app, which is used to select the type of mesh to be plotted.

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  </head>
   <body>

     <fieldset>

       <select style="background-color: #ffffa0" name="meshtype", id="meshtype" onchange="getState(this.value)">
         <option>Select Mesh Type</option>
         <option>Sphere</option>
         <option>Cube</option>
         <option>Bohemian Dome</option>
         <option>Cylinder</option>
         <option>Vectors</option>
         <option disabled="true" value="divider">-------------</option>
         <option>Queen Nefertiti</option>
         
       </select>

       <div class="item">
         <input type="checkbox" id="show_edges">
         <label for="show_edges">Show Edges</label>
       </div>
       <div class="item">
         <input type="checkbox" id="lighting" checked>
         <label for="lighting">Lighting</label>
       </div>
       <div class="item">
         <input type="checkbox" id="pbr" checked>
         <label for="pbr">Physically Based Rendering</label>
       </div>
       <div class="item">
         <input type="checkbox" id="anti_aliasing" checked>
         <label for="anti_aliasing">Anti-Aliasing</label>
       </div>
       <label for="colorpicker">Color Picker:</label>
       <input type="color" id="colorpicker" value="#a4b2ba">

       <div>
         Style:
         <input type="radio" class="style" name="style" value="points" /> Points
         <input type="radio" class="style" name="style" value="wireframe" /> Wireframe
         <input type="radio" class="style" name="style" value="surface" checked/> Surface

       </div>

       <br>
       <button type='button' id ='retrieve'>Plot</button>

     </fieldset>

     <iframe src="" height="600" width="600" title="PyVista" id="myimg" frameBorder="0" allowtransparency=yes scrolling=no>
       <p>Your browser does not support iframes.</p>
     </iframe>
     
   </body>
  <script>
    $(document).ready(function() {
       $('#retrieve').click(function(){
           $.ajax({
           url: "{{ url_for ('get_img') }}",
           type: "GET",
           data: { 
               meshtype:       $('#meshtype option:selected').val(),
               show_edges:    ($("#show_edges").is(":checked")),
               lighting:      ($("#lighting").is(":checked")),
               pbr:           ($("#pbr").is(":checked")),
               color:          $('#colorpicker').val(),
               style:          $(".style:checked").val(),
               anti_aliasing: ($("#anti_aliasing").is(":checked")),

           },
           success: function(response) {
               $("#myimg").attr('src', '/static/' + response);
          },
          error: function(xhr) {
              alert("Please select a mesh type from the drop down menu.");
         }
         });
       });
    });
  </script>
</html>

Static Example#

../_images/flask_example.png

Example Static Flask Webpage#

Python Application app.py#

"""Simple flask app to display static images generated from pyvista.

Expected paths:
static_ex/
└── app.py
    templates/
    └── index.html

"""
import os

from flask import Flask, escape, render_template, request

import pyvista

static_image_path = os.path.join('static', 'images')
if not os.path.isdir(static_image_path):
    os.makedirs(static_image_path)


app = Flask(__name__)


@app.route("/")
def index():
    return render_template('index.html')


@app.route("/getimage")
def get_img():
    """Generate a screenshot of a simple pyvista mesh.

    Returns
    -------
    str
        Local path within the static directory of the image.

    """
    # get the user selected mesh option
    meshtype = request.args.get('meshtype')
    if meshtype == 'Sphere':
        mesh = pyvista.Sphere()
    elif meshtype == 'Cube':
        mesh = pyvista.Cube()
    elif meshtype == 'Bohemian Dome':
        mesh = pyvista.ParametricBohemianDome()
    elif meshtype == 'Cylinder':
        mesh = pyvista.Cylinder()
    else:
        # invalid entry
        raise ValueError('Invalid Option')

    # generate screenshot
    filename = f'{meshtype}.png'
    filepath = os.path.join(static_image_path, escape(filename))
    mesh.plot(off_screen=True, window_size=(300, 300), screenshot=filepath)
    return os.path.join('images', filename)


if __name__ == '__main__':
    app.run()

Ajax Template index.html#

This template should be within the templates directory in the same path as app.py.

This template returns the meshtype parameter back to the get_img method in the flask app, which is used to select the type of mesh to be plotted.

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  </head>
   <body>

     <select style="background-color: #ffffa0" name="country", id="meshtype" onchange="getState(this.value)">
       <option>Select Mesh Type</option>
       <option>Sphere</option>
       <option>Cube</option>
       <option>Bohemian Dome</option>
       <option>Cylinder</option>
     </select>

     <button type='button' id ='retrieve'>Plot</button>
     <img src="" id="myimg" />
   </body>
  <script>
    $(document).ready(function() {
       $('#retrieve').click(function(){
           $.ajax({
           url: "{{ url_for ('get_img') }}",
           type: "GET",
           data: { 
               meshtype:  $('#meshtype option:selected').val()
           },
           success: function(response) {
               $("#myimg").attr('src', '/static/' + response);
          },
          error: function(xhr) {
              alert("Please select a mesh type from the drop down menu.");
         }
         });
       });
    });
  </script>
</html>