Skip to content

Commit

Permalink
Merge pull request #68 from posit-dev/learn-shiny-overhaul
Browse files Browse the repository at this point in the history
Overhaul "Learn Shiny" content to be Express focused
  • Loading branch information
cpsievert authored Jan 24, 2024
2 parents 810346f + d62e8cd commit 8d0eac9
Show file tree
Hide file tree
Showing 52 changed files with 3,839 additions and 3,458 deletions.
54 changes: 25 additions & 29 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ project:
- display-messages
- layouts
- docs
- in-depth
- development
- gallery
- api
Expand Down Expand Up @@ -65,9 +64,7 @@ website:
search: true
left:
- text: "Learn"
file: docs/overview.qmd
- text: "Install"
href: docs/install.qmd
file: docs/quick-start.qmd
- text: "Deploy"
menu:
- docs/deploy-cloud.qmd
Expand All @@ -84,8 +81,6 @@ website:
target: _blank
- text: "Reference"
href: api/index.qmd
- text: "Help"
href: docs/help.qmd
right:
- icon: discord
href: https://discord.gg/yMGCamUMnS
Expand Down Expand Up @@ -211,37 +206,35 @@ website:
collapse-level: 2
align: left
contents:
- section: "Essentials"
- section: "Get Started"
contents:
- docs/overview.qmd
- docs/inputs.qmd
- docs/outputs.qmd
- docs/server.qmd
- docs/quick-start.qmd
- docs/dashboards.qmd
- docs/jupyter-widgets.qmd
- section: "Workflow"
contents:
- docs/install-create-run.qmd
- docs/debug.qmd
- section: "Reactivity"
contents:
- docs/reactive-programming.qmd
- docs/reactive-calculations.qmd
- docs/reactive-events.qmd
- docs/reactive-values.qmd
- docs/reactive-foundations.qmd
- docs/reactive-patterns.qmd
- docs/reactive-mutable.qmd
- section: "Page Layout and Style"
- section: "User interfaces"
contents:
- docs/ui-page-layouts.qmd
- docs/ui-styling.qmd
- docs/ui-navigation.qmd
- docs/ui-components.qmd
- docs/ui-dynamic.qmd
- docs/ui-feedback.qmd
- docs/ui-static.qmd
- section: "Workflow"
- docs/ui-html.qmd
- docs/ui-customize.qmd
- section: "Express vs Core"
contents:
- docs/workflow-modules.qmd
- docs/workflow-module-communication.qmd
- docs/running-debugging.qmd
- section: "In Depth"
- docs/express-introduction.qmd
- docs/express-in-depth.qmd
- docs/express-to-core.qmd
- section: "Modules"
contents:
- docs/ipywidgets.qmd
- docs/ui-html.qmd
- docs/workflow-server.qmd
- docs/modules.qmd
- docs/module-communication.qmd
- section: "Extending"
contents:
- docs/custom-component-one-off.qmd
Expand All @@ -250,6 +243,9 @@ website:
contents:
- docs/comp-streamlit.qmd
- docs/comp-r-shiny.qmd
- section: "Miscellaneous"
contents:
- docs/routing.qmd
# TODO: if the sidebar only has 1 entry, then it displays for the entire site...
# added entry below to prevent this.
- id: deploy
Expand Down
51 changes: 28 additions & 23 deletions api/index.qmd
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
# API Reference Intro

This website documents the public API of Shiny for Python. See the [Getting Started tutorial](/docs/get-started.qmd) for a more approachable introduction to the API.
The left-hand sidebar gives quick access to the full public API, and the table of contents below shows the same entries plus a brief summary for each.
Most of the reference pages include a live example app at the bottom, or at least mention another page with a relevant example.
This page details the Shiny's full API.
New users are encouraged to start from the [Quick Start tutorial](../docs/quick-start.qmd), and then come back here when you're ready to learn more.
We recommend newcomers start with [Shiny Express](../docs/express-introduction.qmd) instead of the more structured Shiny Core API.

We've intentionally designed Shiny's API so that you can `from shiny import *` to get access to most of what you need for most apps without introducing an excessive amount of namespace pollution.
Namely, it gives you:
::: {.panel-tabset .panel-underline .border-0 .p-0 .justify-content-center}

* User interface (UI/HTML) helpers, available via the `ui` subpackage.
### Express

* To avoid clashing with this `ui` namespace when you do `from shiny import *`, you'll want to name you UI object something else, like `app_ui`.
```{shinylive-python}
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 150
* Reactive programming utilities, available via the `reactive` subpackage.
* Decorators for rendering `output`, available via the `render` subpackage.
from shiny.express import input, render, ui
* 3rd party packages that want to implement their own rendering functions are encouraged to use a `@render_foo()` naming convention so users may import with `from mypkg import render_foo`.
ui.input_slider("val", "Slider label", min=0, max=100, value=50)
* A handful of other things you'll want for most apps (e.g., `App`, `Module`, etc).
* If you're using type checking, you'll also want to use the `Inputs`, `Outputs`, and `Session` Classes
to type the instances supplied to your server function, for example:
@render.text
def slider_val():
return f"Slider value: {input.val()}"
```

### Core

```{shinylive-python}
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 400
## file: app.py
from shiny import *
#| viewerHeight: 150
from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.input_slider("n", "Value of n", min=1, max=10, value=5),
ui.output_text("n2")
app_ui = ui.page_fixed(
ui.input_slider("val", "Slider label", min=0, max=100, value=50),
ui.output_text_verbatim("slider_val")
)
def server(input: Inputs, output: Outputs, session: Session) -> None:
@output
def server(input, output, session):
@render.text
def n2():
return f"The value of n*2 is {input.n() * 2}"
def slider_val():
return f"Slider value: {input.val()}"
app = App(app_ui, server)
```

:::

{{< include _api_index.qmd >}}
Binary file added docs/assets/dashboard-template.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/file-upload.mp4
Binary file not shown.
Binary file added docs/assets/shiny-create.mp4
Binary file not shown.
Binary file added docs/assets/tipping-dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 26 additions & 20 deletions docs/comp-r-shiny.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ All of the main components of Shiny like reactivity, rendering functions, and mo
There are, however, a few differences that you need to keep in mind in order to build effective Shiny applications in Python.
If you're reading this, we expect that you are an existing R Shiny user with some Python knowledge.

::: callout-tip
### Shiny Express

Shiny [express](express-introduction.qmd) is a new, more expressive, way to build PyShiny apps. It is not available in R, so the comparisons drawn below are only relevant to core (i.e., non-express) apps.
:::

# Getting started

R users tend to use the R console to install and run Shiny while Python requires you to use the terminal.
To get started you can do the following (or see the [installation instructions](install.qmd) for a more in-depth explanation):
To get started you can do the following (or see the [installation instructions](install-create-run.qmd) for a more in-depth explanation):

1. In your terminal, create a new directory with `mkdir <my_directory>` and navigate into it with `cd <my_directory>`
2. Install Shiny. We strongly recommend using a virtual environment for this as it will eliminate dependency resolution headaches and make deployment easier.
Expand Down Expand Up @@ -75,7 +81,7 @@ While R doesn't have an exact analog to decorators they are similar to [function

- Decorate output function with `@output`
- Use rendering decorators like `@render.plot`, `@render.text`, or `@render.ui` instead of `renderPlot()`, `renderText`, or `renderUI`
- Reactive calculations (equivalent to reactive expressions in R) are decorated `@reactive.Calc`, and reactive effects (equivalent to observers in R) are decorated with `@reactive.Effect`.
- Reactive calculations (equivalent to reactive expressions in R) are decorated `@reactive.calc`, and reactive effects (equivalent to observers in R) are decorated with `@reactive.effect`.
:::

::: {.grid .column-screen-inset}
Expand Down Expand Up @@ -186,7 +192,7 @@ app = App(app_ui, server)

All of the Shiny R functions are in a single package namespace.
On the Python side we make use of [submodules](https://docs.python.org/3/tutorial/modules.html#packages) to keep related functions together.
Note that "submodules" in this case refers to the generic module which is not the same as [shiny modules](workflow-modules.qmd).
Note that "submodules" in this case refers to the generic module which is not the same as [shiny modules](modules.qmd).
For example, instead of `sliderInput()`, you would call `ui.input_slider()`, where the `ui.` refers to a submodule of the main `shiny` module.

::: callout-tip
Expand Down Expand Up @@ -294,7 +300,7 @@ app_ui = ui.page_fluid(
)
def server(input, output, session):
@reactive.Calc
@reactive.calc
def n():
return input.n()
Expand Down Expand Up @@ -325,11 +331,11 @@ For the most part you can follow this naming pattern to find the function you're

| R Function | Python Equivalent |
|-----------------------------------|-------------------------------------|
| `observeEvent` | `@reactive.Effect` |
| `reactive` | `@reactive.Calc` |
| `observeEvent` | `@reactive.effect` |
| `reactive` | `@reactive.calc` |
| `bindEvent` | `@ractive.event` |
| `reactiveEvent` | `@reactive.Calc` with `@reactive.event` |
| `observeEvent` | `@reactive.Effect` with `@reactive.event` |
| `reactiveEvent` | `@reactive.calc` with `@reactive.event` |
| `observeEvent` | `@reactive.effect` with `@reactive.event` |
| `htmlTemplate` | `page_template` |
| `tabPanelBody` | `navs_content` |
| `*Tab` (`insertTab`, `appendTab`, etc) | `nav_*` (`nav_insert`, `nav_append` etc) |
Expand All @@ -343,7 +349,7 @@ Reactivity works mostly the same in R and Python, but there are a few small diff

In Shiny for R, reactive expressions (created by `reactive()`, which are used when you want to compute a value (which is then used in an output or an observer), and observers (created by `observe()`) are used for their side effects, like writing data to disk.
This is a common source of confusion because the names `reactive()` and `observe()` do not clearly express when they should be used.
To help clarify this confusion we've renamed `reactive()` to `@reactive.Calc`, and `observe()` to `@reactive.Effect` in Python.
To help clarify this confusion we've renamed `reactive()` to `@reactive.calc`, and `observe()` to `@reactive.effect` in Python.

::: {.grid .column-screen-inset}
::: {.g-col-12 .g-col-md-6}
Expand Down Expand Up @@ -392,11 +398,11 @@ app_ui = ui.page_fluid(
)
def server(input, output, session):
@reactive.Calc
@reactive.calc
def val():
return input.n()
@reactive.Effect
@reactive.effect
def _():
input.reset()
ui.update_slider("n", value=40)
Expand Down Expand Up @@ -440,20 +446,20 @@ In Shiny for Python, we've simplified things in the following ways:

- There is no direct analog to R's `reactiveValues`.

- The analog of R's standalone `reactiveVal` is `reactive.Value`.
(The `input` object in Python is a dictionary-like object containing individual `reactive.Value` objects.)
- The analog of R's standalone `reactiveVal` is `reactive.value`.
(The `input` object in Python is a dictionary-like object containing individual `reactive.value` objects.)

- Reactive values have can be retrieved with `my_val()` or `my_val.get()` and can be set with `my_val.set()`.

There is no analog of `reactiveValues` in Python, but you can create something similar by using a dictionary of `reactive.Value` objects.
There is no analog of `reactiveValues` in Python, but you can create something similar by using a dictionary of `reactive.value` objects.

```{python}
vals = {
"x": reactive.Value(1),
"y": reactive.Value(2),
"x": reactive.value(1),
"y": reactive.value(2),
}
z = reactive.Value(3)
z = reactive.value(3)
# Retrieve values
print(vals.x())
Expand Down Expand Up @@ -515,15 +521,15 @@ app_ui = ui.page_fluid(
)
def server(input: Inputs, output: Outputs, session: Session):
val = reactive.Value(0)
val = reactive.value(0)
@reactive.Effect
@reactive.effect
@reactive.event(input.minus)
def _():
newVal = val() - 1
val.set(newVal)
@reactive.Effect
@reactive.effect
@reactive.event(input.plus)
def _():
newVal = val() + 1
Expand Down
30 changes: 28 additions & 2 deletions docs/custom-component-one-off.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def output_tabulator(id, height="200px"):


:::{.callout-note}
We use the `HTMLDependency` function to bind up the assets needed for tabulator that we made in the previous step to make sure th at they're included in our app whenever the `output_tabulator()` function is called (but not more than once).
We use the `HTMLDependency` function to bind up the assets needed for tabulator that we made in the previous step to make sure that they're included in our app whenever the `output_tabulator()` function is called (but not more than once).

Note the use of `all_files=True` here. This makes it so we can do the ESM import of the Tabulator library. Otherwise `tabulator_esm.min.js` would not be hosted and the JS library wouldn't be able to find it.
:::
Expand Down Expand Up @@ -284,10 +284,32 @@ This returned value is then what gets sent to the client side and is available i

Now we have all the components neccesary to use our tabulator output component. Here's an app that uses it to render some number of rows of the indomitable `mtcars` dataset.

::: {.panel-tabset .panel-underline .border-0 .justify-content-center}

### Express

```{.python filename="app.py"}
from shiny import App, Inputs, ui
from pathlib import Path
import pandas as pd
from shiny.express import input, ui
# Code for the custom output
...
# App code
ui.input_slider("n", "Number of rows to show", 1, 20, 10)
@render_tabulator
def tabulatorTable():
return pd.read_csv(Path(__file__).parent / "mtcars.csv").head(input.n())
```

### Core

```{.python filename="app.py"}
from pathlib import Path
import pandas as pd
from shiny import App, Inputs, ui
# Code for the custom output: output_tabulator and render_tabulator
...
Expand All @@ -309,6 +331,10 @@ def server(input: Inputs):
app = App(app_ui, server)
```

:::



Which results in the following app:


Expand Down
Loading

0 comments on commit 8d0eac9

Please sign in to comment.