diff --git a/_quarto.yml b/_quarto.yml index 64f67455..a0f731ba 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -13,7 +13,6 @@ project: - display-messages - layouts - docs - - in-depth - development - gallery - api @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/api/index.qmd b/api/index.qmd index 7f60deb4..12fee396 100644 --- a/api/index.qmd +++ b/api/index.qmd @@ -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 >}} diff --git a/docs/assets/dashboard-template.png b/docs/assets/dashboard-template.png new file mode 100644 index 00000000..cb288ff2 Binary files /dev/null and b/docs/assets/dashboard-template.png differ diff --git a/docs/assets/file-upload.mp4 b/docs/assets/file-upload.mp4 new file mode 100644 index 00000000..43a06454 Binary files /dev/null and b/docs/assets/file-upload.mp4 differ diff --git a/docs/assets/shiny-create.mp4 b/docs/assets/shiny-create.mp4 new file mode 100644 index 00000000..26d623e1 Binary files /dev/null and b/docs/assets/shiny-create.mp4 differ diff --git a/docs/assets/tipping-dashboard.png b/docs/assets/tipping-dashboard.png new file mode 100644 index 00000000..6981ae25 Binary files /dev/null and b/docs/assets/tipping-dashboard.png differ diff --git a/docs/comp-r-shiny.qmd b/docs/comp-r-shiny.qmd index 007f65ee..d748f5f8 100644 --- a/docs/comp-r-shiny.qmd +++ b/docs/comp-r-shiny.qmd @@ -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 ` and navigate into it with `cd ` 2. Install Shiny. We strongly recommend using a virtual environment for this as it will eliminate dependency resolution headaches and make deployment easier. @@ -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} @@ -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 @@ -294,7 +300,7 @@ app_ui = ui.page_fluid( ) def server(input, output, session): - @reactive.Calc + @reactive.calc def n(): return input.n() @@ -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) | @@ -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} @@ -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) @@ -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()) @@ -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 diff --git a/docs/custom-component-one-off.qmd b/docs/custom-component-one-off.qmd index 36b9be11..99abfd02 100644 --- a/docs/custom-component-one-off.qmd +++ b/docs/custom-component-one-off.qmd @@ -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. ::: @@ -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 ... @@ -309,6 +331,10 @@ def server(input: Inputs): app = App(app_ui, server) ``` +::: + + + Which results in the following app: diff --git a/docs/custom-components-pkg.qmd b/docs/custom-components-pkg.qmd index 36130779..4ab39d3a 100644 --- a/docs/custom-components-pkg.qmd +++ b/docs/custom-components-pkg.qmd @@ -272,7 +272,7 @@ This is the actual UI function for our component. Aka the one that gets called b Because `makeReactInput()` [works by creating a webcomponent](#srctsindex.tsx), to render our input we just need to pass the tag name we set up in the `tagName` argument to `makeReactInput().` Next, we pass the `fancy_color_picker_deps` html dependency we just made and the ID of the binding and we’re good to go! ::: {.callout-note} -By using the `resolve_id(id)` function here when declaring our ID, we make sure that the component works [Shiny modules](docs/workflow-modules) where the ID of the component needs to be prefixed with the module name. +By using the `resolve_id(id)` function here when declaring our ID, we make sure that the component works [Shiny modules](modules.qmd) where the ID of the component needs to be prefixed with the module name. ::: @@ -342,6 +342,23 @@ This file is used to configure typescript, which we are using to write our compo ### `example-app/app.py` +::: {.panel-tabset .panel-underline .border-0 .justify-content-center} + +#### Express + +```{.python} +from fancy_color_picker import fancy_color_picker +from shiny.express import render + +fancy_color_picker("myComponent") + +@render.text +def valueOut(): + return f"Value from input is {input.myComponent()}" +``` + +#### Core + ```{.python} from fancy_color_picker import fancy_color_picker @@ -360,6 +377,7 @@ def server(input, output, session): app = App(app_ui, server) ``` +::: This is a simple example app that can be used to test the component while developing. It uses the `fancy_color_picker` function we defined in `fancy_color_picker.py` to add the component to the app. It also uses the `render.text` decorator to render the value of the input to the page. @@ -373,6 +391,23 @@ This is a simple example app that can be used to test the component while develo In our output binding example we defined an output that conveniently _displays_ colors. If we were packaging up two components like this we could/should modify the example app to showcase both of them. +::: {.panel-tabset .panel-underline .border-0 .justify-content-center} + +#### Express + +```{.python filename="app.py"} +from fancy_color_picker import fancy_color_picker, render_color +import shiny.express + +fancy_color_picker("myComponent") + +@render_color +def myColor(): + return input.myComponent() +``` + +#### Core + ```{.python filename="app.py"} from fancy_color_picker import fancy_color_picker, output_color, render_color @@ -393,6 +428,8 @@ app = App(app_ui, server) ::: +::: +