You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ui.input_checkbox("rug", "Show rug marks", value = False) # <6>
43
+
```
44
+
45
+
[Learn more](https://pypi.org/project/palmerpenguins/) about the
46
+
Palmer Penguins dataset.
47
+
48
+
# Plots # <7>
49
+
50
+
```{{python}}
51
+
@render.plot
52
+
def depth():
53
+
return sns.displot( # <8>
54
+
filtered_penguins(), x = "bill_depth_mm",
55
+
hue = "species", kind = input.dist(),
56
+
fill = True, rug=input.rug()
57
+
) # <8>
58
+
```
59
+
60
+
```{{python}}
61
+
@render.plot
62
+
def length():
63
+
return sns.displot(
64
+
filtered_penguins(), x = "bill_length_mm",
65
+
hue = "species", kind = input.dist(),
66
+
fill = True, rug=input.rug()
67
+
)
68
+
```
69
+
70
+
# Data
71
+
72
+
```{{python}}
73
+
@render.data_frame
74
+
def dataview():
75
+
return render.DataGrid(filtered_penguins()) # <9>
76
+
```
77
+
````
78
+
79
+
1. The `server: shiny` option instructs Quarto to run a Shiny Server behind the document.
80
+
81
+
2. The `context: setup` cell option indicates that this code cell should run when the application starts (as opposed to when each new client session starts). Expensive initialization code (e.g. loading data) should be placed in `context: setup`.
82
+
83
+
3. Create global sidebars by adding the `.sidebar` class to a level 1 header. Sidebars can include code cells as well as images, narrative, and links.
84
+
85
+
4. These checkbox input groups have their contents dynamically driven from the available categories in the `species` and `islands` fields of the dataset.
86
+
87
+
5. When the user interacts with the checkbox groups this results in a different filtered view of the dataset. The `@reactive.Calc` function recomputes the filtered dataset and makes it available as `filtered_penguins()`.
88
+
89
+
6. These inputs affect the display of plots but not the contents of the filtered dataset.
90
+
91
+
7. Level 1 headings (here `# Plots` and `# Data`) create pages within the dashboard.
92
+
93
+
8. Plots are rendered by referencing the filtered dataset (`filtered_penguins()` as well as the plot display oriented inputs (`input.dist()` and `input.rug()`). Plots are automatically re-rendered when the dataset or these inputs change.
94
+
95
+
9. The Data tab also references `filtered_penguins()` and is updated whenever the filtered data changes.
Copy file name to clipboardExpand all lines: docs/dashboards/interactivity/shiny-python/_shiny-requirements.qmd
+2-2Lines changed: 2 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -2,9 +2,9 @@
2
2
::: {.callout-note}
3
3
### Shiny Prerequisites
4
4
5
-
In order to use Shiny within Quarto documents you will need the latest version of the `shiny`package (>=0.6). You can install the latest version of shiny with:
5
+
In order to use Shiny within Quarto documents you will need the latest version of the `shiny` (>=0.6) and `shinywidgets` (>=0.2.2). You can install the latest version of these with:
The [Shiny](https://shiny.posit.co/py/) package provides an easy way to build web applications with Python. Quarto dashboards can include embedded Shiny components (e.g. a plot with sliders that control its inputs).
16
16
17
-
This section covers integrating Shiny with Quarto and assumes that you already have basic familiarity with Shiny. To learn more about Shiny please visit <https://shiny.posit.co/py/>. If you are using R rather than Python, see the documentation on using [Shiny for R](../shiny-r.qmd).
17
+
This section assumes you have no experience with Shiny, and will teach you the basic concepts you need to get up and running with Shiny with Quarto. <!-- You can learn much more about Shiny at the official [Shiny website](https://shiny.posit.co/py/)--although note that the documentation there is written for non-Quarto uses of Shiny, which currently involve a slightly different syntax. -->
18
+
19
+
If you are using R rather than Python, see the documentation on using [Shiny for R](../shiny-r.qmd).
18
20
19
21
{{< include _shiny-requirements.qmd >}}
20
22
@@ -24,110 +26,147 @@ We'll start with a very simple dashboard that consists of a single plot and some
24
26
25
27
{{< include _shiny-sidebar.md >}}
26
28
29
+
In this dashboard, you can choose different values from the select boxes on the left, and the plot will update to reflect your choices. You can also click the checkbox to show or hide rug marks. Let's go through the construction of this Shiny-enabled dashboard one step at a time.
27
30
28
-
##Next Steps
31
+
### Metadata
29
32
30
-
Next, we'll explore a more in-depth example that covers many of the techniques you'll use when creating dashboards with Shiny, including factoring out setup code, reactive calculations, and more advanced layout constructs like pages. Here is the interactive document we'll be building:
33
+
The first thing to do is add `server: shiny` to the front matter. This tells Quarto that the document should be rendered as a Shiny dashboard (which requires a Python runtime whenever the dashboard is being viewed), rather than as a static HTML page. When you run `quarto preview <filename>.qmd` on a `server: shiny` document, Quarto will start and maintain a Shiny process for you and open the dashboard in your browser.
31
34
32
-
{.border fig-alt="Screenshot of a Palmer Penguins dashboard. Navigation bar shows two pages: Plots and Data. On the left is a sidebar with an image of penguins followed by four inputs: a set of checkboxes for Species; a set of checkboxes for Islands; and dropdown for Distribution; and a checkbox to show rug marks. On the right the page is divided into two rows each showing a density plot: the top row of bill_depth_mm; the bottom row of bill_length_mm"}
35
+
### Adding Input Controls
33
36
34
-
Here is the source code for this dashboard (click on the numbers on the far right for additional explanation of syntax and mechanics):
37
+
Next, we use functions matching the pattern `ui.input_xxx` to create input controls. For example, `ui.input_select()` creates a select box, `ui.input_slider()` creates a slider, and so on. The values returned by these functions are then rendered into HTML and JavaScript by Quarto.
35
38
36
-
````{.python .pymd}
37
-
---
38
-
title: "Palmer Penguins"
39
-
author: "Cobblepot Analytics"
40
-
format: dashboard
41
-
server: shiny # <1>
42
-
---
39
+
**Every input function takes an input ID as its first argument.** An input ID is string that uniquely identifies this input; it must be a simple, syntactically valid Python variable name. We will use this ID to access the input's value from other parts of the dashboard.
43
40
44
-
```{{python}} # <2>
45
-
#| context: setup
46
-
import seaborn as sns
47
-
from shiny import render, reactive, ui
48
-
penguins = sns.load_dataset("penguins")
49
-
``` # <2>
41
+
::: {.callout-warning}
42
+
Make sure each input ID in your Shiny dashboard is unique. If you use the same ID for two different inputs, Shiny will not be able to tell them apart, and your dashboard will not work correctly.
43
+
:::
50
44
51
-
# {.sidebar} # <3>
45
+
The second argument for each input function is usually a human-readable string that will be displayed next to the input control. For example, the `ui.input_select()` function passes `"Variable:"`as the second argument, which is why the select box has the label "Variable:" next to it.
52
46
53
-
{width="80%"}
47
+
<!--
48
+
TODO: Introduce components gallery
49
+
-->
54
50
55
-
```{{python}}
56
-
species = list(penguins["species"].value_counts().index) # <4>
data = penguins[penguins["species"].isin(input.species())]
71
-
data = data[data["island"].isin(input.islands())]
72
-
return data
73
-
``` # <5>
51
+
In many dashboards, it's desirable to visually gather all of your input controls into a sidebar. You can do this by adding the `.sidebar` class to a level 2 header, as in the `## {.sidebar}` line above.
ui.input_checkbox("rug", "Show rug marks", value = False) # <6>
78
-
```
53
+
### Displaying Dynamic Output
79
54
80
-
[Learn more](https://pypi.org/project/palmerpenguins/) about the
81
-
Palmer Penguins dataset.
55
+
In Shiny, dashboards can contain outputs---plots, tables, text, etc.---that dynamically update in response to user input.
82
56
83
-
# Plots # <7>
57
+
The example above defined a dynamic plot with the following code:
84
58
59
+
````{.python .pymd}
85
60
```{{python}}
86
61
@render.plot
87
-
def depth():
88
-
return sns.displot( # <8>
89
-
filtered_penguins(), x = "bill_depth_mm",
90
-
hue = "species", kind = input.dist(),
91
-
fill = True, rug=input.rug()
92
-
) # <8>
93
-
```
62
+
def displot():
63
+
sns.displot(
64
+
data=penguins, hue="species", multiple="stack",
65
+
x=input.x(), rug=input.rug(), kind=input.dist())
66
+
```
67
+
````
94
68
95
-
```{{python}}
96
-
@render.plot
97
-
def length():
98
-
return sns.displot(
99
-
filtered_penguins(), x = "bill_length_mm",
100
-
hue = "species", kind = input.dist(),
101
-
fill = True, rug=input.rug()
102
-
)
103
-
```
69
+
The function here is given the name `displot`. The body of the function is using typical Seaborn code to create the plot. And a `@render.plot` decorator is added to the function, to indicate to Shiny that this function should be used to create a plot. (If you haven't seen decorators before, they're a Python feature that allows you to add additional behavior to a function.)
70
+
71
+
The `input.x()` and `input.rug()` method calls are retrieving the values of the `x` and `rug` inputs created earlier in the dashboard.
72
+
73
+
Note that our code never calls the `displot()` function! Just the act of defining the function, and decorating it with `@render.plot`, is enough to tell Shiny and Quarto to:
74
+
75
+
1. Insert a plot into the dashboard at this location.
76
+
2. Use the function body to create the plot.
77
+
3. Automatically re-run the function body whenever the values of `input.x()`, `input.rug()`, or `input.dist()` change due to user interaction, and use the result to update the existing plot.
78
+
79
+
This example only contains a single `@render.plot` output, but it's possible for Shiny apps to contain multiple outputs, and outputs of different types, as you'll see in the following example.
80
+
81
+
TODO: Link to output gallery
104
82
105
-
# Data
83
+
### Reactive Programming
84
+
85
+
In the previous section, we said that the `displot` function would re-run whenever any of the inputs it referred to changed. Shiny is a **reactive programming** framework, meaning it automatically tracks the relationships between inputs and outputs in your app. When an input changes, only outputs that are affected by that input are re-rendered. This is a powerful feature that makes it easy to create dashboards that respond to user input efficiently.
86
+
87
+
::: {.callout-note}
88
+
The `input` object is designed to be tracked by Shiny's reactive framework,
89
+
90
+
Shiny specifically tracks changes to _reactivity-aware_ objects like the `input` object, not to just any arbitrary Python variable. You can't just write `x = 100`, then use `x` from the `displot` function, and expect `displot` to automatically rerun whenever `x` changes.
91
+
92
+
Similarly, Shiny will only automatically re-run functions that are reactivity-aware, like ones decorated with `@render.plot`. It will not help re-execute code at the top level of the document or code in regular Python functions.
93
+
:::
94
+
95
+
## Additional Features
96
+
97
+
Next, we'll explore a more in-depth example that covers more features, including factoring out setup code, reactive calculations, and more advanced layout constructs like pages. Here is the interactive document we'll be building:
98
+
99
+
{.border fig-alt="Screenshot of a Palmer Penguins dashboard. Navigation bar shows two pages: Plots and Data. On the left is a sidebar with an image of penguins followed by four inputs: a set of checkboxes for Species; a set of checkboxes for Islands; and dropdown for Distribution; and a checkbox to show rug marks. On the right the page is divided into two rows each showing a density plot: the top row of bill_depth_mm; the bottom row of bill_length_mm"}
106
100
107
-
```{{python}}
101
+
Here is the source code for this dashboard. You can click on the numbers on the far right for additional explanation of syntax and mechanics, and we'll also explain in more detail below.
102
+
103
+
{{< include _shiny-advanced.md >}}
104
+
105
+
### Setup Chunks
106
+
107
+
In static Quarto documents, `{python}` code chunks run only when the document is rendered, not when it is viewed. In `server: shiny` documents, `{python}` code chunks are run both during render time _and_ each time the dashboard is loaded in a browser. This is important because each visitor to the dashboard needs their own independent copies of inputs/outputs in memory, so that simultaneous users don't interfere with each other.
108
+
109
+
However, sometimes we have code that would be excessive to run for every user, and we only want the code to run once, when the document's Shiny runtime process is starting up. For example, in the example above, we load data using `sns.load_dataset("penguins")`; it would be wasteful in terms of both time and memory to load the data once for each user, instead of once for the process.
110
+
111
+
By simply adding `#| context: setup` to the code chunk, we can tell Quarto to run the code only once, when the Shiny process starts up. This is called a **setup chunk**. Setup chunks are a great way to factor out code that you want to run once, not on every page load. Variables you define in setup chunks can be read by all other code chunks in the document.
112
+
113
+
### Dashboard Pages
114
+
115
+
At the top of this dashboard, you can see "Plots" and "Data" headings. These are called **dashboard pages**. Dashboard pages are a way to organize your dashboard into multiple pages, each with its own set of outputs. You can insert dashboard pages by adding level 1 headers to your Markdown--in this case, `# Plots` and `# Data`.
116
+
117
+
### Data Frame Outputs
118
+
119
+
On the Data page, there's a dynamic data frame output. This is created by the following code:
120
+
121
+
````{.python .pymd}
122
+
```{{python}}
108
123
@render.data_frame
109
124
def dataview():
110
-
return render.DataGrid(filtered_penguins()) # <9>
111
-
```
125
+
return render.DataGrid(filtered_penguins())
126
+
```
112
127
````
113
128
114
-
1.The `server: shiny` option instructs Quarto to run a Shiny Server behind the document.
129
+
In a `@render.data_frame` function, you can simply return a Pandas data frame, and Shiny will automatically render it as an interactive data grid. (The `filtered_penguins()` function is a reactive calculation that returns a data frame--we'll explore that next.)
115
130
116
-
2. The `context: setup` cell option indicates that this code cell should run when the application starts (as opposed to when each new client session starts). Expensive initialization code (e.g. loading data) should be placed in `context: setup`.
131
+
You also have the option of wrapping the data frame object in a [`render.DataGrid`](https://shiny.posit.co/py/api/render.DataGrid.html) or [`render.DataTable`](https://shiny.posit.co/py/api/render.DataTable.html#shiny.render.DataTable) constructor; in this case, we're using the former. This is not strictly necessary, but it allows you to set additional options on the data grid or table, such as filtering and selection.
117
132
118
-
3. Create global sidebars by adding the `.sidebar` class to a level 1 header. Sidebars can include code cells as well as images, narrative, and links.
133
+
The only difference between `render.DataGrid` and `render.DataTable` is the appearance of the rendered table: `render.DataGrid` uses a more compact, spreadsheet-like appearance, while `render.DataTable` uses a more traditional table appearance.
119
134
120
-
4. These checkbox input groups have their contents dynamically driven from the available categories in the `species` and `islands` fields of the dataset.
135
+
### Reactive Calculations
121
136
122
-
5. When the user interacts with the checkbox groups this results in a different filtered view of the dataset. The `@reactive.Calc` function recomputes the filtered dataset and makes it available as `filtered_penguins()`.
137
+
In this example, the user uses select boxes to filter a dataset, and the filtered dataset is displayed in three different dynamic outputs: two plots and a data frame. Remember that as inputs change, Shiny automatically re-executes functions decorated with `@render.plot` and `@render.data_frame` that are affected by those inputs. But where do we put the code that filters the dataset?
123
138
124
-
6. These inputs affect the display of plots but not the contents of the filtered dataset.
139
+
The most obvious place would be to duplicate the code in each of the three rendering functions. But this is a bad idea, both because duplicate code is annoying to maintain, and because it would be inefficient to re-run the same filtering code three times just to get the exact same results. We could extract the duplicated code into a function, which would remove the maintenance problem, but it would not be any more efficient.
125
140
126
-
7. Level 1 headings (here `# Plots`and `# Data`) create pages within the dashboard.
141
+
Shiny has a solution: **reactive calculations**. A reactive calculation is a reactive-aware function that is re-executed whenever its inputs change, but whose return value is not rendered into the dashboard. Instead, the return value is cached, and can be accessed by rendering functions (or even by other reactive calculations). This allows us to place the filtering logic in a single reactive calculation, and then have the three rendering functions access the filtered dataset from the reactive calculation.
127
142
128
-
8. Plots are rendered by referencing the filtered dataset (`filtered_penguins()` as well as the plot display oriented inputs (`input.dist()` and `input.rug()`). Plots are automatically re-rendered when the dataset or these inputs change.
143
+
To create a reactive calculation, we use the `@reactive.Calc` decorator. The following code creates a reactive calculation called `filtered_penguins`:
144
+
145
+
````{.python .pymd}
146
+
```{{python}}
147
+
@reactive.Calc
148
+
def filtered_penguins():
149
+
data = penguins[penguins["species"].isin(input.species())]
150
+
data = data[data["island"].isin(input.islands())]
151
+
return data
152
+
```
153
+
````
154
+
155
+
To read the value of a reactive calc, call it like a function. For example, the `depth` plot looks like this:
156
+
157
+
````{.python .pymd}
158
+
```{{python}}
159
+
@render.plot
160
+
def depth():
161
+
return sns.displot(
162
+
filtered_penguins(), x = "bill_depth_mm",
163
+
hue = "species", kind = input.dist(),
164
+
fill = True, rug=input.rug()
165
+
)
166
+
```
167
+
````
129
168
130
-
9. The Data tab also references `filtered_penguins()` and is updated whenever the filtered data changes.
169
+
Note the `filtered_penguins()` call. To reiterate, this call does not necessarily cause the `filtered_penguins` function to run. Instead, it usually returns the cached value of the function, which is automatically updated whenever the inputs it refers to change. And because the `depth` plot refers to the `filtered_penguins` calculation, it will be re-rendered whenever those inputs change.
0 commit comments