-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separate UI Customization into layout, themes and dynamic UI
- Loading branch information
Showing
8 changed files
with
316 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
--- | ||
title: "Dynamic UI" | ||
title-slide-attributes: | ||
data-background-image: images/shiny-for-python-seattle.jpg | ||
data-background-position: bottom left | ||
data-background-size: cover | ||
format: | ||
positconfslides-revealjs: | ||
incremental: false | ||
chalkboard: true | ||
slide-number: c/t | ||
code-copy: true | ||
center-title-slide: false | ||
code-link: true | ||
highlight-style: a11y | ||
width: "1600" | ||
height: "900" | ||
css: "styles.css" | ||
filters: | ||
- positconfslides | ||
- reveal-auto-agenda | ||
auto-agenda: | ||
heading: Agenda | ||
--- | ||
|
||
```{python} | ||
# | echo: false | ||
import os | ||
import sys | ||
exercises_path = "./exercises" | ||
if exercises_path not in sys.path: | ||
sys.path.append(exercises_path) | ||
from helpers import include_shiny_folder | ||
``` | ||
|
||
# Composable functions | ||
|
||
## This is getting a bit complicated | ||
|
||
- UIs can start to get complicated | ||
- You can end up with deeply nested function calls | ||
- Too many brackets | ||
- Too many indents | ||
- Luckily You can break UIs into variables and compose them | ||
|
||
## UI functions are composable | ||
|
||
```{.python} | ||
import shiny.experimental as x | ||
ui.navset_tab( | ||
ui.nav( | ||
"Tab1", | ||
ui.card(ui.output_plot("Plot")), | ||
ui.output_text("some_text"), | ||
), | ||
ui.nav("Tab2", ui.output_data_frame("data")), | ||
ui.nav("Tab3", ui.output_image("image")), | ||
) | ||
``` | ||
|
||
## UI functions are composable | ||
|
||
```{.python} | ||
card1 = ui.card(ui.output_plot("Plot")) | ||
tab1 = ui.nav( | ||
"Tab1", | ||
card1, | ||
ui.output_text("some_text"), | ||
) | ||
tab2 = ui.nav("Tab2", ui.output_data_frame("data")) | ||
tab3 = ui.nav("Tab3", ui.output_image("image")) | ||
ui = ui.fluid_page(ui.navset_tab(tab1, tab2, tab3)) | ||
``` | ||
|
||
## Your turn | ||
|
||
{{< yourturn 'express-304-layout-columns' >}} | ||
|
||
<!-- Go to [exercises/10-layout](./exercises/10-layout.html) or run `apps/core/exercises/4-ui-customization/4.3-layout` locally. --> | ||
|
||
## Your turn | ||
|
||
{{< yourturn 'express-305-render-express' >}} | ||
|
||
|
||
# Review | ||
|
||
## Review: Value boxes | ||
|
||
- Value boxes are another experimental layout container | ||
- Just like `ui.card()` | ||
- Called with `ui.value_box()` | ||
- Useful for visually highlighting important numbers or text | ||
- Usually contain `ui.output_text()` as a child element | ||
|
||
## Your turn | ||
|
||
{{< yourturn 'express-307-value-box' >}} | ||
|
||
|
||
# Dynamic user interfaces | ||
|
||
## Why create dynamic UIs? | ||
|
||
- Guide the user along a happy path | ||
- If you don't want them to click on something, don't show it to them | ||
- Much better to prevent errors than to handle them | ||
|
||
## Dynamic UI example | ||
|
||
{{< yourview '302-dynamic-ui' >}} | ||
|
||
|
||
## Shiny Dynamic UI | ||
|
||
Function | Description | ||
------------------- | ---------------- | ||
`@render.ui` | Generate UI elements on the server | ||
`ui.conditional_panel` | Hide things on the browser | ||
`ui.update_*` | Modify existing UI elements | ||
|
||
## Dynamic UI | ||
|
||
- UI elements can be generated like any other element | ||
- Use `@render.ui` on the server | ||
- Function returns any `ui` element | ||
- Referred to like a regular ui element | ||
|
||
## Dynamic UI | ||
|
||
{{< yourview '302-dynamic-ui' >}} | ||
|
||
|
||
## UI side | ||
|
||
```{.python code-line-numbers="5-6"} | ||
from shiny.express import render, ui, input | ||
with ui.card(): | ||
ui.input_checkbox("show_checkbox", "Show Checkbox") | ||
with ui.panel_conditional( "input.show_checkbox"): | ||
ui.input_checkbox("show_slider", "Show Slider"), | ||
@render.ui | ||
def dynamic_slider(): | ||
if input.show_slider(): | ||
return ui.input_slider("n", "N", 0, 100, 20) | ||
``` | ||
|
||
## Server side | ||
|
||
```{.python code-line-numbers="7-10"} | ||
from shiny.express import render, ui, input | ||
with ui.card(): | ||
ui.input_checkbox("show_checkbox", "Show Checkbox") | ||
with ui.panel_conditional( "input.show_checkbox"): | ||
ui.input_checkbox("show_slider", "Show Slider"), | ||
@render.ui | ||
def dynamic_slider(): | ||
if input.show_slider(): | ||
return ui.input_slider("n", "N", 0, 100, 20) | ||
``` | ||
|
||
## Summary of dynamic UI | ||
|
||
- Dynamic UIs can be intimidating, but they follow a familiar pattern: | ||
- Create a rendering function which returns a UI chunk | ||
- Decorate the function with and `@render.ui` | ||
|
||
- Very powerful, but | ||
- Round trip to the server | ||
- You can lose session state | ||
|
||
# Conditional panel | ||
|
||
## Conditional panel | ||
- `ui.panel_conditional` hides UI elements based on conditions | ||
- Less flexible than `ui.output_ui` | ||
- Preserves input state | ||
- Doesn't require trip to the server | ||
|
||
## Conditional panel | ||
|
||
{{< yourview '302-dynamic-ui' >}} | ||
|
||
|
||
## Conditional panel | ||
|
||
```{.python code-line-numbers="3-6"} | ||
app_ui = ui.page_fluid( | ||
ui.input_checkbox("show_checkbox", "Show Checkbox"), | ||
ui.panel_conditional( | ||
"input.show_checkbox", | ||
ui.input_checkbox("show_slider", "Show Slider"), | ||
), | ||
ui.output_ui("dynamic_slider"), | ||
) | ||
def server(input, output, session): | ||
@output | ||
@render.ui | ||
def dynamic_slider(): | ||
print(input.show_slider()) | ||
if input.show_slider(): | ||
return ui.input_slider("n", "N", 0, 100, 20) | ||
app = App(app_ui, server) | ||
``` | ||
|
||
## Things to note | ||
- Uses JavaScript condition, not python | ||
- JavaScript condition will be the same as R, Google and chatGPT can help | ||
- You often want `===` | ||
|
||
## Your turn | ||
|
||
{{< yourturn 'express-308-layout-column' >}} | ||
|
||
|
||
|
||
# Conclusion | ||
|
||
## That's a lot of stuff! | ||
|
||
- Shiny gives you a lot of power to customize your app | ||
- Important to remember _that_ you can do these things, even if you forget _how_ | ||
- Learning to build intuitive UIs is a journey | ||
- Ask for advice on Discord |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
title: Dynamic UI | ||
--- | ||
|
||
To view in full screen, click on the slides then press {{< kbd f >}}, or <a href = "ui-dynamic-slides.html" target="_blank">open in new tab.</a> | ||
|
||
|
||
```{=html} | ||
<iframe class="slide-deck" src="ui-dynamic-slides.html" height="420" width="747" style="border: 1px solid #123233;"></iframe> | ||
``` | ||
|
||
{{< yourturnIframeContainer >}} |
Oops, something went wrong.