Controls

Learn which type of controls are supported and how to use them.

Overview

Controls are minimal building blocks of dstack applications. Controls allow the user of the application to change input parameters and see the corresponding outputs. An application may have any number of controls that may depend on each other. The user may define the Python code that initializes or updates the state of controls.

The supported controls include text inputs, single and multiple selection controls, sliders, check-boxes, file uploaders, markdown outputs, table outputs, chart output, and markdown outputs.

Example

Here's a simple example:

import dstack as ds
import plotly.express as px
app = ds.app() # Create an instance of the application
# An utility function that loads the data
def get_data():
return px.data.stocks()
# A drop-down control that shows stock symbols
stock = app.select(items=get_data().columns[1:].tolist())
# A handler that updates the plot based on the selected stock
def output_handler(self, stock):
# A plotly line chart where the X axis is date and Y is the stock's price
self.data = px.line(get_data(), x="date", y=stock.value())
# A plotly chart output
app.output(handler=output_handler, depends=[stock])
# Deploy the application with the name "stocks" and print its URL
url = app.deploy("stocks")
print(url)

If we run the code above and open the link, we'll see the following application:

Control State

As you saw above, you can initialize a control by setting either the initial state of the control (e.g. by setting items to select), or a handler (as we did above for output).

Here's the example of setting the state directly (note, you can pass values or even a function that returns them):

# A drop-down control that shows stock symbols
stock = app.select(items=get_data().columns[1:].tolist())

Here's the example of using a handler:

# A handler that updates the plot based on the selected stock
def output_handler(self, stock):
# A plotly line chart where the X axis is date and Y is the stock's price
self.data = px.line(get_data(), x="date", y=stock.value())
# A plotly chart output
app.output(handler=output_handler, depends=[stock])

A handler is a function in which the first argument is always self of the same type as the control of the handler. In case the control depends on other controls, these controls are passed to the handler too.

As soon as the stock control gets updates, dstack invokes the handler to update the state of the output. Inside the handler, it's possible to update any field of the control.

Now, let's look at a more complicated example, where the items of the drop-down control are populated dynamically, and which has another drop-down that depends on the first drop-down control:

import dstack as ds
import pandas as pd
app = ds.app() # Create an instance of the application
# An utility function that loads the data
def get_data():
return pd.read_csv("https://www.dropbox.com/s/cat8vm6lchlu5tp/data.csv?dl=1", index_col=0)
# An utility function that returns regions
def get_regions():
df = get_data()
return df["Region"].unique().tolist()
# A drop-down control that shows regions
regions = app.select(items=get_regions, label="Region")
# A handler that updates the drop-down with counties based on the selected region
def countries_handler(self, regions):
region = regions.value() # the selected region
df = get_data()
self.items = df[df["Region"] == region]["Country"].unique().tolist()
# A drop-down control that shows countries
countries = app.select(handler=countries_handler, label="Country", depends=[regions])
# A handler that updates the table output based on the selected country
def output_handler(self, countries):
country = countries.value() # the selected country
df = get_data()
self.data = df[df["Country"] == country] # we assign a pandas dataframe here to self.data
# An output that shows companies based on the selected country
app.output(handler=output_handler, depends=[countries])
# Deploy the application with the name "dependant_control" and print its URL
url = app.deploy("dependant_control")
print(url)

If you run this code and open the application, you'll see the following:

By default, all controls are placed in the main area. Sometimes, it may be useful to place certain control in a sidebar. To do that, you have to use the dstack.Application.sidebar() function. Here's a very simple example:

import dstack as ds
import plotly.express as px
app = ds.app() # create an instance of the application
siebar = app.sidebar() # create a sidebar
# an utility function that loads the data
def get_data():
return px.data.stocks()
# a drop-down control inside the sidebar
stock = siebar.select(items=get_data().columns[1:].tolist())
# a handler that updates the plot based on the selected stock
def output_handler(self, stock):
# a plotly line chart where the X axis is date and Y is the stock's price
self.data = px.line(get_data(), x='date', y=stock.value())
# a plotly chart output in the main area
app.output(handler=output_handler, depends=[stock])
# deploy the application with the name "stocks_sidebar" and print its URL
url = app.deploy("stocks_sidebar")
print(url)

Now, if you open the application, you'll see the following:

Require Apply

By default, the application triggers the application update (including updating outputs) every time the user changes anything. If you'd like any control to update only if the user clicks Apply, you can set require_apply=True when you create a control:

# An output that shows companies based on the selected country
app.output(handler=output_handler, depends=[countries], require_apply=True)

Now, if you open the application, you'll see the following:

Now, if you click Apply, you'll see the output updated:

Layout

Controls may be visually arranged within the application using the layout system. Currently, dstack supports only the "grid" layout. This means an application is divided into columns and rows, and controls may take up any number of these columns and rows.

Here's how it works:

import dstack as ds
# create an instance of the application that has three columns
app = ds.app(columns = 3)
# an input that takes one column and one row
input_1 = app.input(label="Input 1", colspan=1)
# an input that takes one column and one row
input_2 = app.input(label="Input 2", colspan=1)
# an input that takes one column and two rows
input_3 = app.input(label="Input 3", colspan=1, rowspan=2)
url = app.deploy("layout_1")
print(url)

If we open the application, we'll see the following:

If you don't specify columns within dstack.app(), it will be set to 12.

The sidebar just like the main area also uses the "grid" layout. However, by default, it has 2 columns instead of 12.

Control Reference

Below, you'll find the entire list of supported controls, with a complete list of their attributes, and examples.

Input

The Input control can be used to enter text:

import dstack as ds
app = ds.app() # Create an instance of the application
# A handler that updates the markdown output based on the input text
def markdown_handler(self, name):
if len(name.text) > 0:
self.text = "Hi, **" + name.text + "**!"
else:
self.text = "No name"
# An input control
name = app.input(label="What's your name?")
# A markdown output that greets the users using the given name
app.markdown(handler=markdown_handler, depends=[name])
# Deploy the application with the name "controls/input" and print its URL
url = app.deploy("controls/input")
print(url)

Here's the list of arguments of the dstack.Application.input() function:

Parameter

Type

Description

Required

text

Can be one of the following:

  • str

  • Callable[[], str]

The text value of the control.

Not required if handler is used.

handler

Callable[..., None]

The function that initializes or updates the state of the control.

Required if text is not set.

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

colspan

int

The number of columns the control is taking up. By default, it's 2.

No

rowspan

int

The number of rows the control is taking up. By default, it's 1.

No

TODO: Add a screenshot

Select

The Select control can be used to select one or multiple items:

import dstack as ds
import plotly.express as px
app = ds.app() # create an instance of the application
# an utility function that loads the data
def get_data():
return px.data.stocks()
# a drop-down control that shows stock symbols
stock = app.select(items=get_data().columns[1:].tolist())
# a handler that updates the plot based on the selected stock
def output_handler(self, stock):
symbol = stock.value() # the selected stock
# a plotly line chart where the X axis is date and Y is the stock's price
self.data = px.line(get_data(), x='date', y=symbol)
# a plotly chart output
app.output(handler=output_handler, depends=[stock])
# deploy the application with the name "stocks" and print its URL
url = app.deploy("stocks")
print(url)

Here's the list of arguments of the dstack.Application.select() function:

Parameter

Type

Description

Required

items

Can be one of the following:

  • List[Any]

  • Callable

Can be one of the following:

  • A list of items.

  • A function that returns a list of items. See example B.

Not required if handler is used.

handler

Callable[..., None]

The function that initializes or updates the state of the control.

Required if items is not set.

selected

Can be one of the following:

  • int

  • List[int]

Can be one of the following:

  • An index of the currently selected item. Only if multiple set to False.

  • A list of indexes of the currently selected items. Only if multiple set to True.

No

multiple

bool

True if multiple selection is allowed. False by default.

No

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

colspan

int

The number of columns the control is taking up. By default, it's 2.

No

rowspan

int

The number of rows the control is taking up. By default, it's 1.

No

Checkbox

The Checkbox control can used to select or unselect a certain boolean property. Here's an example:

import dstack as ds
app = ds.app() # Create an instance of the application
# A handler that updates the label of the checkbox based on wether it's selected or not
def checkbox_handler(self):
if self.selected:
self.label = "Selected"
else:
self.label = "Not selected"
# A checkbox control
name = app.checkbox(handler=checkbox_handler)
# Deploy the application with the name "controls/checkbox" and print its URL
url = app.deploy("controls/checkbox")
print(url)

Here's the list of arguments of the dstack.Application.checkbox() function:

Parameter

Type

Description

Required

selected

Can be one of the following:

  • bool

  • Callable

The initial value of the control.

Not required if handler is used.

handler

Callable

The function that initializes or updates the state of the control.

Required if selected is not set.

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

colspan

int

The number of columns the control is taking up. By default, if the checkbox has a label, it's 2. Otherwise, it's 1.

No

rowspan

int

The number of rows the control is taking up. By default, it's 1.

No

Slider

The Slider control can be used to select a number out of a given range. It supports both integer and double numbers. Here's an example:

import dstack as ds
import plotly.express as px
app = ds.app() # Create an instance of the application
# An utility function that loads the data
def get_data():
return px.data.gapminder()
# A handler that updates the plot output based on the selected year
def output_handler(self, year):
value = year.value() # the selected year
self.data = px.scatter(get_data().query("year==" + str(value)), x="gdpPercap", y="lifeExp",
size="pop", color="country", hover_name="country", log_x=True, size_max=60)
# A slider control that prompts to select a year
slider = app.slider(values=get_data()["year"].unique().tolist())
# An output control that shows the chart
app.output(handler=output_handler, depends=[slider])
# Deploy the application with the name "controls/slider" and print its URL
url = app.deploy("controls/slider")
print(url)

Here's the list of arguments of the dstack.Application.slider() function:

Parameter

Type

Description

Required

values

Can be one of the following:

  • Iterable[float]

  • Callable

Can be one of the following:

  • A list of possible values. See example A.

  • A function that returns a list of possible values. See example B.

Not required if handler is used.

handler

Callable[..., None]

The function that initializes or updates the state of the control.

Required if items is not set.

selected

Can be one of the following:

  • int

  • List[int]

Can be one of the following:

  • An index of the currently selected item. Only if multiple set to False.

  • A list of indexes of the currently selected items. Only if multiple set to True.

No

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

colspan

int

The number of columns the control is taking up. By default, it's 2.

No

rowspan

int

The number of rows the control is taking up. By default, it's 1.

No

Uploader

The Uploader control can be used to upload single or multiple files and access their content. Here's an example:

import dstack as ds
import pandas as pd
app = ds.app() # Create an instance of the application
# A handler that loads a dataframe from the content of the uploaded CSV file and passes it to the output
def app_handler(self, uploader):
if len(uploader.uploads) > 0:
with uploader.uploads[0].open() as f:
self.label = uploader.uploads[0].file_name
self.data = pd.read_csv(f).head(100)
else:
self.label = "No file selected"
self.data = None
# A file uploader control
uploader = app.uploader(label="Select a CSV file", colspan=12)
# An output control that shows the content of the uploaded file
app.output(handler=app_handler, depends=[uploader])
# Deploy the application with the name "controls/uploader" and print its URL
url = app.deploy("controls/uploader")
print(url)

Now, if you upload a CSV file, you'll see the following:

Here's the list of arguments of the dstack.Application.uploader() function:

Parameter

Type

Description

Required

uploads

Can be one of the following:

  • List[Upload]

  • Callable[[], List[Upload]]

The list of uploaded files.

Not required if handler is used.

handler

Callable[..., None]

The function that initializes or updates the state of the control.

Required if uploads is not set.

multiple

bool

True if multiple files are allowed. False by default.

No

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

colspan

int

The number of columns the control is taking up. By default, it's 2.

No

rowspan

int

The number of rows the control is taking up. By default, it's 1.

No

Markdown

The Markdown control can be used to display a markdown text. Here's the same example that we used earlier:

import dstack as ds
app = ds.app() # Create an instance of the application
# A handler that updates the markdown output based on the input text
def markdown_handler(self, name):
if name.text:
self.text = "Hi, **" + name.text + "**!"
else:
self.text = "No name"
# An input control
name = app.input(label="What's your name?")
# A markdown output that greets the users using the given name
app.markdown(handler=markdown_handler, depends=[name])
# Deploy the application with the name "controls/markdown" and print its URL
url = app.deploy("controls/markdown")
print(url)

Above, you see a Markdown control that displays the text based on the text specified in another control. Below is a more simple example, where the Markdown control just displays a static text:

import dstack as ds
app = ds.app() # Create an instance of the application
# A markdown output
app.markdown(text="Hello, **World!**")
# Deploy the application with the name "markdown" and print its URL
url = app.deploy("markdown")
print(url)

Here's the list of arguments of the dstack.Application.markdown() function:

Parameter

Type

Description

Required

text

Can be one of the following:

  • str

  • Callable[[], str]

The text value of the control.

Not required if handler is used.

handler

Callable[..., None]

The function that initializes or updates the state of the control.

Required if text is not set.

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

require_apply

bool

True if the field requires an Apply button to be clicked for the control to update. False by default.

No

colspan

int

The number of columns the control is taking up. By default, it's 12.

No

rowspan

int

The number of rows the control is taking up. By default, it's 6.

No

Output

The Output control can be used to display table data or charts. Outputs support Pandas dataframes, and Plotly, Bokeh, Matplotlib, and Seaborn charts.

Pandas

TODO: Add Pandas example

Plotly

TODO: Add Plotly example

Bokeh

TODO: Add Bokeh example

Matplotlib

TODO: Add Pandas example

Seaborn

TODO: Add Seaborn example

Here's the list of arguments of the dstack.Application.output() function:

Parameter

Type

Description

Required

data

Can be one of the following:

  • pandas.core.frame.DataFrame

  • plotly.basedatatypes.BaseFigure

  • matplotlib.figure.Figure

  • bokeh.plotting.Figure

  • Callable

The data to display in the output.

Not required if handler is used.

handler

Callable[..., None]

The function that initializes or updates the state of the control.

Required if text is not set.

label

str

The caption of the control.

No

depends

Can be one of the following:

  • List[Control]

  • Control

The other controls this control depends on.

No

require_apply

bool

True if the field requires an Apply button to be clicked for the control to update. False by default.

No

colspan

int

The number of columns the control is taking up. By default, it's 6.

No

rowspan

int

The number of rows the control is taking up. By default, it's 6.

No

Find more details on how to use outputs by following the tutorial on building an application with multiple outputs: