Login
Back to bricks list
Introduction Version

Streamlit

Streamlit is a popular Python package for rapidly building data apps. You can find more information and examples on the Streamlit website: https://streamlit.io/. With this you can integrate interactive dashboards in Constellab that aggregate informations of you experiment or even across multiple experiments.


When you're deploying a Streamlit app in Constellab, the system takes care of deployment, security, and app cleanup, so you don't need to worry about those aspects.



Introduction


To create a streamlit dashboard, you will need to generate a StreamlitApp resource. Here is the documentation about this resource. This is a special resource where the default view is the streamlit interactive dashboard. 



The StreamlitApp contains sub resources which allows you to view others resources that were used in the dashboard. 



Build streamlit app


There is 2 ways of building a StreamlitApp :


  • Through a streamlit live task
    • Though a custom task in you brick

      Live task


      The Streamlit live task is a special type of task that lets you create a Streamlit dashboard app connected to your lab's data. 



      The inputs of the live task will be automatically added to the StreamlitApp and will be ready to use in the streamlit code. The sources variable will contains the list of input resources, similar to the python live task. 


      Here is an example of a simple live task that generates a StreamlitApp. It takes a Table as input to generate an app with the dataframe and the possibility to select 2 columns to generate a scatter plot. 


      # This is a template for a streamlit live task.
      # This generates an app with one dataframe as input. Then the user can select 2 columns to plot a scatter plot.
      
      import plotly.express as px
      import streamlit as st
      from pandas import DataFrame
      
      # Your Streamlit app code here
      st.title("Dashboard example")
      
      # show a table from file_path which is a csv file full width
      if sources:
          df: DataFrame = sources[0].get_data()
      
          # show the dataframe
          st.dataframe(df)
      
          # add a select widget with the columns names with no default value
          # set the selectbox side by side
          col1, col2 = st.columns(2)
      
          with col1:
              x_col = st.selectbox("Select x column", options=df.columns, index=0)
      
          with col2:
              y_col = st.selectbox("Select y column", options=df.columns, index=1)
      
          if x_col and y_col:
              # Generate a scatter plot with plotly express
              fig = px.scatter(df, x=x_col, y=y_col)
              st.plotly_chart(fig)
      

      Here is the result app :




      Custom task


      You can also build a StreamlitApp in you brick. This allows you to complex app with multiple files.



      Create a custom task that has a StreamlitResource as output. In this task you will build the StreamlitResource, add resource to it, set parameters and provide the code for the streamlit dashboard. View StreamlitResource documentation for more information about its methods.


      Inputs


      To add input to you StreamlitResource, simply call the add_resource method. The inputs resources will be available under the sources variable (with the same order as added) in the streamlit code.   


      Any resource can be added to the streamlit app. We recommend added pre-computed data so the streamlit code does not do heavy computation which will result in a slow dashboard. 


      Config


      You can add parameters to generate pre-configured dashboard. It can be useful to limit access to certains data for example. 


      To set the parameters you can call the set_param or set_params methods. The parameters will be available as a dict under the params variable in the streamlit code.


      Code


      You can now provide the code of your streamlit app. To do this you have 2 options:


      Provide a folder (recommended)


      Provide a folder containing you streamlit app code. Call set_streamlit_folder and provide the path of the folder that contains your streamlit code. This allows you to provide a complete app with multiple files.


      Here is few rules for the folder


      • The folder must contains the entry point main.py file.
        • The folder name must start with an underscore so the streamlit code is not executed when the brick is loaded.


          Set code dynamically


          Set code dynamically (from a string) by calling set_streamlit_code. Only call this method is the code is autonomous and doesn't rely on others files (this will not work as only this code will be copied to the streamlit app).


          You can also call the set_streamlit_code_path which will read the contains of the file and save it as dynamic code. 


          Example


          Here is a example of a streamlit app using a folder.


          First we create the structure for our StreamlitApp and our task. 



          The folder _dashboard_code contains the streamlit code. The streamlit_generator.py file contains the task that will create the dashboard. The _dashboard_code folder maintains the main.py file (entrypoint for the app). It also contains a sub module plot imported by main.py to work with multiple file. The _dashboard_code folder a a common python module structure. 


          Here is the content of the main.py file. 


          # This is a template for a streamlit live task.
          # This generates an app with one dataframe as input. Then the user can select 2 columns to plot a scatter plot.
          
          from typing import List
          
          import streamlit as st
          from gws_core import Resource
          from pandas import DataFrame
          from plot.plot_generator import generate_scatter_plot
          
          # thoses variable will be set by the streamlit app
          # don't initialize them, there are create to avoid errors in the IDE
          sources: List[Resource]
          params: dict
          
          # Your Streamlit app code here
          st.title(params.get('title'))
          
          # show a table from file_path which is a csv file full width
          if sources:
              df: DataFrame = sources[0].get_data()
          
              # show the dataframe
              st.dataframe(df)
          
              # add a select widget with the columns names with no default value
              # set the selectbox side by side
              col1, col2 = st.columns(2)
          
              with col1:
                  x_col = st.selectbox("Select x column", options=df.columns, index=0)
          
              with col2:
                  y_col = st.selectbox("Select y column", options=df.columns, index=1)
          
              generate_scatter_plot(df, x_col, y_col)
          

          And the content of plot_generator sub file.


          import plotly.express as px
          import streamlit as st
          from pandas import DataFrame
          
          
          def generate_scatter_plot(dataframe: DataFrame, x_col: str, y_col: str) -> None:
              if x_col and y_col:
                  # Generate a scatter plot with plotly express
                  fig = px.scatter(dataframe, x=x_col, y=y_col)
                  st.plotly_chart(fig)
          

          Then we create a task that will generates the StreamlitApp. It takes a table as input and a title as config.


          import os
          
          from gws_core import (ConfigParams, ConfigSpecs, InputSpec, InputSpecs,
                                OutputSpec, OutputSpecs, StreamlitResource, StrParam,
                                Table, Task, TaskInputs, TaskOutputs, task_decorator)
          
          
          @task_decorator("StreamlitGenerator", human_name="Generate dashboard for table",
                          short_description="Task to generate a custom Streamlit dashboard from a table")
          class StreamlitGenerator(Task):
          
              input_specs: InputSpecs = InputSpecs({'table': InputSpec(Table, human_name="Table")})
              output_specs: OutputSpecs = OutputSpecs({
                  'streamlit_app': OutputSpec(StreamlitResource, human_name="Streamlit app")
              })
              config_specs: ConfigSpecs = {
                  'title': StrParam(human_name='Dashboard title')
              }
          
              # retrieve the path of the app folder, relative to this file
              # the dashboard code folder starts with a underscore to avoid being loaded when the brick is loaded
              streamlit_app_folder = os.path.join(
                  os.path.abspath(os.path.dirname(__file__)),
                  "_dashboard_code"
              )
          
              def run(self, params: ConfigParams, inputs: TaskInputs) -> TaskOutputs:
          
                  # build the streamlit resource with the code and the resources
                  streamlit_resource = StreamlitResource()
          
                  # set the input in the streamlit resource
                  table: Table = inputs.get('table')
                  streamlit_resource.add_resource(table)
          
                  # set the param of the streamlit resource
                  title = params.get_value('title')
                  streamlit_resource.set_param('title', title)
          
                  # set the app folder
                  streamlit_resource.set_streamlit_folder(self.streamlit_app_folder)
          
                  return {'streamlit_app': streamlit_resource}
          

          Note that we retrieve the path of the folder relative to the path of the takes file. Never set an hardcoded absolute path in a task, as the brick might be installed in another folder. 


          That's it 🎉, the streamlit app is ready to be used. You can find the complete code of this in the academy brick.


          Use brick classes in streamlit app


          In a streamlit app the gws environment to work with classes from your brick (like resources, tasks, database...) is automatically loaded. With this you can interact with the database to create real time dashboard, generate experiments or report...


          To do this, simply import your classes from your brick.


          Here is an example of a streamlit app that creates an empty experiment when the button is clicked. 


          import streamlit as st
          from gws_core import ExperimentService, Logger, User, CurrentUserService
          
          # Your Streamlit app code here
          st.title("Create experiment app")
          
          if st.button("Create experiment"):
              st.write("Creating experiment")
              Logger.info("Creating experiment from streamlit app")
              # Call gws_core service to create the experiment
              ExperimentService.create_experiment(title="Super streamlit")
          else:
              st.write("No creating")
            


          Develop the streamlit app


          When you're deploying a Streamlit app in Constellab, the system takes care of deployment, security, and app cleanup, so you don't need to worry about those aspects.



          You can develop and test your Streamlit app directly in the Codelab environment. Write your Streamlit app within a folder. Upload file data to your codelab and import those file in your app (you can hardcode the path, they will be removed once integrated in Constellab). 


          Once it's ready, run the Streamlit app using port 8501 with the following command: streamlit run [PATH_TO_FILE] --server.port 8501 --server.allowRunOnSave true. You should see the message: "You can now view your Streamlit app in your browser." The --server.allowRunOnSave option will automatically refresh the app when a file is save, making testing faster.


          To open your app, open the URL: https://dashboard-dev.[LAB].constellab.app. Basically, you need to take the URL of your Codelab, replace "codelab" with "streamlit-dev," and remove everything after "/". This will open the Streamlit app, allowing you to test it.



          Once your app is ready, you can test it within a Streamlit live task and connect it with your data. Replace your input variables with the sources variable and remove the st.set_page_config statement. The app should work as tested in the Codelab.


          Custom components


          In gws_core we developed few custom component for streamlit app. They can be imported from gws_streamlit_helper package. 


          You can find the code of those component here.


          Dataframe paginator


          Streamlit doesn't have a way of paginate big dataframe natively. You can use the dataframe_paginated method from gws_streamlit_helper. It support row and column pagination.


          Here is an example on how to use it:


          from gws_streamlit_helper import dataframe_paginated
          from pandas import DataFrame
          
          my_df : DataFrame = sources[0]
          
          dataframe_paginated(my_df, paginate_rows=True, row_page_size_options=[50,100 ,250], 
                             paginate_columns=False, column_page_size_options=None)

          Troubleshoot


          Connecting / Connection lost / Connection timed out


          If you encounter connection issues such as "Connecting/Connection lost/Connection timed out" when opening the Streamlit app, please close the resource or view and then reopen it. This should resolve the issue and allow you to use the app again.


          Port 8501 already is use


          When developing a streamlit app, if you encounter error 'Port 8501 already is use', this means that you have another streamlit app that is already running. Streamlit app can be running : 


          • in a terminal of the codelab, in this case, kill the terminal
            • in the lab in dev mode. In Monitoring > Other > Streamlit app you can check if there is an app running and stop it. You can also kill the dev environment.

              If you can't find the running streamlit app, another solution might be to restart the lab from the lab manager section.