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.
Here's a simple example:
import dstack as dsimport plotly.express as pxapp = ds.app() # Create an instance of the application# An utility function that loads the datadef get_data():return px.data.stocks()# A drop-down control that shows stock symbolsstock = app.select(items=get_data().columns[1:].tolist())# A handler that updates the plot based on the selected stockdef output_handler(self, stock):# A plotly line chart where the X axis is date and Y is the stock's priceself.data = px.line(get_data(), x="date", y=stock.value())# A plotly chart outputapp.output(handler=output_handler, depends=[stock])# Deploy the application with the name "stocks" and print its URLurl = app.deploy("stocks")print(url)
If we run the code above and open the link, we'll see the following application:
Live Demo:
https://dstack.cloud/gallery/stocks
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 symbolsstock = 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 stockdef output_handler(self, stock):# A plotly line chart where the X axis is date and Y is the stock's priceself.data = px.line(get_data(), x="date", y=stock.value())# A plotly chart outputapp.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 dsimport pandas as pdapp = ds.app() # Create an instance of the application# An utility function that loads the datadef get_data():return pd.read_csv("https://www.dropbox.com/s/cat8vm6lchlu5tp/data.csv?dl=1", index_col=0)# An utility function that returns regionsdef get_regions():df = get_data()return df["Region"].unique().tolist()# A drop-down control that shows regionsregions = app.select(items=get_regions, label="Region")# A handler that updates the drop-down with counties based on the selected regiondef countries_handler(self, regions):region = regions.value() # the selected regiondf = get_data()self.items = df[df["Region"] == region]["Country"].unique().tolist()# A drop-down control that shows countriescountries = app.select(handler=countries_handler, label="Country", depends=[regions])# A handler that updates the table output based on the selected countrydef output_handler(self, countries):country = countries.value() # the selected countrydf = 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 countryapp.output(handler=output_handler, depends=[countries])# Deploy the application with the name "dependant_control" and print its URLurl = 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 dsimport plotly.express as pxapp = ds.app() # create an instance of the applicationsiebar = app.sidebar() # create a sidebar# an utility function that loads the datadef get_data():return px.data.stocks()# a drop-down control inside the sidebarstock = siebar.select(items=get_data().columns[1:].tolist())# a handler that updates the plot based on the selected stockdef output_handler(self, stock):# a plotly line chart where the X axis is date and Y is the stock's priceself.data = px.line(get_data(), x='date', y=stock.value())# a plotly chart output in the main areaapp.output(handler=output_handler, depends=[stock])# deploy the application with the name "stocks_sidebar" and print its URLurl = app.deploy("stocks_sidebar")print(url)
Now, if you open the application, you'll see the following:
Live Demo:
https://dstack.cloud/gallery/stocks_sidebar
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 countryapp.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:
Source Code: https://github.com/dstackai/dstack-examples/blob/master/controls/select_depends_apply.py
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 columnsapp = ds.app(columns = 3)# an input that takes one column and one rowinput_1 = app.input(label="Input 1", colspan=1)# an input that takes one column and one rowinput_2 = app.input(label="Input 2", colspan=1)# an input that takes one column and two rowsinput_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
.
Live Demo:
https://dstack.cloud/gallery/layout_1
The sidebar just like the main area also uses the "grid"
layout. However, by default, it has 2
columns instead of 12
.
Below, you'll find the entire list of supported controls, with a complete list of their attributes, and examples.
The Input
control can be used to enter text:
import dstack as dsapp = ds.app() # Create an instance of the application# A handler that updates the markdown output based on the input textdef markdown_handler(self, name):if len(name.text) > 0:self.text = "Hi, **" + name.text + "**!"else:self.text = "No name"# An input controlname = app.input(label="What's your name?")# A markdown output that greets the users using the given nameapp.markdown(handler=markdown_handler, depends=[name])# Deploy the application with the name "controls/input" and print its URLurl = app.deploy("controls/input")print(url)
Live Demo:
https://dstack.cloud/gallery/controls/input
Here's the list of arguments of the dstack.Application.input()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| The text value of the control. | Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
| The number of columns the control is taking up. By default, it's | No |
|
| The number of rows the control is taking up. By default, it's | No |
TODO:
Add a screenshot
The Select
control can be used to select one or multiple items:
import dstack as dsimport plotly.express as pxapp = ds.app() # create an instance of the application# an utility function that loads the datadef get_data():return px.data.stocks()# a drop-down control that shows stock symbolsstock = app.select(items=get_data().columns[1:].tolist())# a handler that updates the plot based on the selected stockdef 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 priceself.data = px.line(get_data(), x='date', y=symbol)# a plotly chart outputapp.output(handler=output_handler, depends=[stock])# deploy the application with the name "stocks" and print its URLurl = app.deploy("stocks")print(url)
Live Demo:
https://dstack.cloud/gallery/controls/select
Here's the list of arguments of the dstack.Application.select()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| Can be one of the following:
| Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
| Can be one of the following:
| Can be one of the following:
| No |
|
|
| No |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
| The number of columns the control is taking up. By default, it's | No |
|
| The number of rows the control is taking up. By default, it's | No |
The Checkbox
control can used to select or unselect a certain boolean
property. Here's an example:
import dstack as dsapp = ds.app() # Create an instance of the application# A handler that updates the label of the checkbox based on wether it's selected or notdef checkbox_handler(self):if self.selected:self.label = "Selected"else:self.label = "Not selected"# A checkbox controlname = app.checkbox(handler=checkbox_handler)# Deploy the application with the name "controls/checkbox" and print its URLurl = app.deploy("controls/checkbox")print(url)
Live Demo:
https://dstack.cloud/gallery/controls/checkbox
Here's the list of arguments of the dstack.Application.checkbox()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| The initial value of the control. | Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
| The number of columns the control is taking up. By default, if the checkbox has a | No |
|
| The number of rows the control is taking up. By default, it's | No |
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 dsimport plotly.express as pxapp = ds.app() # Create an instance of the application# An utility function that loads the datadef get_data():return px.data.gapminder()# A handler that updates the plot output based on the selected yeardef output_handler(self, year):value = year.value() # the selected yearself.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 yearslider = app.slider(values=get_data()["year"].unique().tolist())# An output control that shows the chartapp.output(handler=output_handler, depends=[slider])# Deploy the application with the name "controls/slider" and print its URLurl = app.deploy("controls/slider")print(url)
Live Demo:
https://dstack.cloud/gallery/controls/slider
Here's the list of arguments of the dstack.Application.slider()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| Can be one of the following:
| Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
| Can be one of the following:
| Can be one of the following:
| No |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
| The number of columns the control is taking up. By default, it's | No |
|
| The number of rows the control is taking up. By default, it's | No |
The Uploader
control can be used to upload single or multiple files and access their content. Here's an example:
import dstack as dsimport pandas as pdapp = 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 outputdef app_handler(self, uploader):if len(uploader.uploads) > 0:with uploader.uploads[0].open() as f:self.label = uploader.uploads[0].file_nameself.data = pd.read_csv(f).head(100)else:self.label = "No file selected"self.data = None# A file uploader controluploader = app.uploader(label="Select a CSV file", colspan=12)# An output control that shows the content of the uploaded fileapp.output(handler=app_handler, depends=[uploader])# Deploy the application with the name "controls/uploader" and print its URLurl = app.deploy("controls/uploader")print(url)
Now, if you upload a CSV file, you'll see the following:
Live Demo:
https://dstack.cloud/gallery/controls/uploader
Here's the list of arguments of the dstack.Application.uploader()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| The list of uploaded files. | Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
|
|
| No |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
| The number of columns the control is taking up. By default, it's | No |
|
| The number of rows the control is taking up. By default, it's | No |
The Markdown
control can be used to display a markdown text. Here's the same example that we used earlier:
import dstack as dsapp = ds.app() # Create an instance of the application# A handler that updates the markdown output based on the input textdef markdown_handler(self, name):if name.text:self.text = "Hi, **" + name.text + "**!"else:self.text = "No name"# An input controlname = app.input(label="What's your name?")# A markdown output that greets the users using the given nameapp.markdown(handler=markdown_handler, depends=[name])# Deploy the application with the name "controls/markdown" and print its URLurl = app.deploy("controls/markdown")print(url)
Live Demo:
https://dstack.cloud/gallery/controls/input
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 dsapp = ds.app() # Create an instance of the application# A markdown outputapp.markdown(text="Hello, **World!**")# Deploy the application with the name "markdown" and print its URLurl = app.deploy("markdown")print(url)
Live Demo:
https://dstack.cloud/gallery/markdown
Here's the list of arguments of the dstack.Application.markdown()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| The text value of the control. | Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
|
| No |
|
| The number of columns the control is taking up. By default, it's | No |
|
| The number of rows the control is taking up. By default, it's | No |
The Output
control can be used to display table data or charts. Outputs support Pandas
dataframes, and Plotly
, Bokeh
, Matplotlib
, and Seaborn
charts.
TODO:
Add Pandas example
TODO:
Add Plotly example
TODO:
Add Bokeh example
TODO:
Add Pandas example
TODO:
Add Seaborn example
Here's the list of arguments of the dstack.Application.output()
function:
Parameter | Type | Description | Required |
| Can be one of the following:
| The data to display in the output. | Not required if |
|
| The function that initializes or updates the state of the control. | Required if |
|
| The caption of the control. | No |
| Can be one of the following:
| The other controls this control depends on. | No |
|
|
| No |
|
| The number of columns the control is taking up. By default, it's | No |
|
| The number of rows the control is taking up. By default, it's | No |
Find more details on how to use outputs by following the tutorial on building an application with multiple outputs: