Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul "Learn Shiny" content to be Express focused #68

Merged
merged 16 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
cpsievert marked this conversation as resolved.
Show resolved Hide resolved
- 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
cpsievert marked this conversation as resolved.
Show resolved Hide resolved
tools:
- 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
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.
30 changes: 30 additions & 0 deletions docs/assets/head-content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<style>
.link-shinylive {
position: absolute;
bottom: 0;
right: 0.5em;
background-color: unset;
font-family: var(--bs-font-sans-serif);
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Add shinylive links to code blocks
document.querySelectorAll("[data-shinylive]").forEach(function (el) {
let pre = el.querySelector("pre");
const link = document.createElement("a");
link.classList.add("link-shinylive");
link.target = "_blank";
link.rel = "noopener noreferrer";
link.href = el.dataset.shinylive;
link.innerHTML = `<i class="bi bi-lightning-fill"></i> Run on shinylive`;
pre.appendChild(link);
});
// Add nav-underline to panel-underline where appropriate
document.querySelectorAll(".panel-underline .nav").forEach((x) => {
x.classList.remove("nav-tabs");
x.classList.add("nav-underline");
x.classList.add("justify-content-center");
});
});
cpsievert marked this conversation as resolved.
Show resolved Hide resolved
</script>
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-difference.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
33 changes: 31 additions & 2 deletions docs/custom-component-one-off.qmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
---
title: "Custom JavaScript component"
format:
html:
include-in-header: assets/head-content.html
---

In this post, you will learn how to create a custom element and accompanying output binding in Shiny. This is useful if you want to create an output that is not currently in Shiny for your app.
Expand Down Expand Up @@ -190,7 +193,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 +287,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 +334,10 @@ def server(input: Inputs):
app = App(app_ui, server)
```

:::
cpsievert marked this conversation as resolved.
Show resolved Hide resolved



Which results in the following app:


Expand Down
Loading