From 3767d62b5c74abaaab41825a575568b0ca5f2f72 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 8 Sep 2023 08:41:11 -0300 Subject: [PATCH] Ui customization --- _quarto.yml | 16 ++-- exercises/10-layout.qmd | 32 +++++++ exercises/11-value-boxes.qmd | 24 +++++ exercises/12-dynamic-ui.qmd | 32 +++++++ exercises/6-reactive-event.qmd | 4 +- exercises/7-reactive-effects.qmd | 4 +- exercises/8-navsets.qmd | 24 +++++ exercises/9-cards.qmd | 24 +++++ slides/getting-started.qmd | 2 +- slides/reactivity.qmd | 2 +- slides/ui-customization.qmd | 153 +++++++++++++++++++++++++++++++ 11 files changed, 305 insertions(+), 12 deletions(-) create mode 100644 exercises/10-layout.qmd create mode 100644 exercises/11-value-boxes.qmd create mode 100644 exercises/12-dynamic-ui.qmd create mode 100644 exercises/8-navsets.qmd create mode 100644 exercises/9-cards.qmd create mode 100644 slides/ui-customization.qmd diff --git a/_quarto.yml b/_quarto.yml index 7d434fa..8c560d5 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -5,7 +5,7 @@ website: title: "Shiny for Python Workshop" sidebar: contents: - - section: "Session 1" + - section: "Getting Started" contents: - slides/getting-started.qmd - section: "Exercises" @@ -14,7 +14,7 @@ website: - exercises/2-debug.qmd - exercises/3-add-filter.qmd - exercises/4-add-plot.qmd - - section: "Session 2" + - section: "Reactivity" contents: - slides/reactivity.qmd - section: "Exercises" @@ -22,16 +22,16 @@ website: - exercises/5-reactivity.qmd - exercises/6-reactive-event.qmd - exercises/7-reactive-effects.qmd - - section: "Session 3" + - section: "UI Customization" contents: - slides/ui-customization.qmd - section: "Exercises" contents: - - exercises/1-hello-world.qmd - - exercises/2-debug.qmd - - exercises/3-add-filter.qmd - - exercises/4-add-plot.qmd - + - exercises/8-navsets.qmd + - exercises/9-cards.qmd + - exercises/10-layout.qmd + - exercises/11-value-boxes.qmd + - exercises/12-dynamic-ui.qmd navbar: tools: - icon: github diff --git a/exercises/10-layout.qmd b/exercises/10-layout.qmd new file mode 100644 index 0000000..4e2d14d --- /dev/null +++ b/exercises/10-layout.qmd @@ -0,0 +1,32 @@ +--- +title: "Layout" +--- +```{python} +# | echo: false +import os + +os.chdir("..") +from helpers import problem_tabs + + +def ui_tabs(file): + problem_tabs(f"apps/problem-sets/4-ui-customization/{file}") + + +``` + +## Exercise 4.3 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.3-layout") +``` + +## Exercise 4.4 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.4-ui-composition") +``` \ No newline at end of file diff --git a/exercises/11-value-boxes.qmd b/exercises/11-value-boxes.qmd new file mode 100644 index 0000000..b7c6339 --- /dev/null +++ b/exercises/11-value-boxes.qmd @@ -0,0 +1,24 @@ +--- +title: "Value boxes" +--- +```{python} +# | echo: false +import os + +os.chdir("..") +from helpers import problem_tabs + + +def ui_tabs(file): + problem_tabs(f"apps/problem-sets/4-ui-customization/{file}") + + +``` + +## Exercise 4.5 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.5-value-boxes") +``` \ No newline at end of file diff --git a/exercises/12-dynamic-ui.qmd b/exercises/12-dynamic-ui.qmd new file mode 100644 index 0000000..fe65bc8 --- /dev/null +++ b/exercises/12-dynamic-ui.qmd @@ -0,0 +1,32 @@ +--- +title: "Dynamic UI" +--- +```{python} +# | echo: false +import os + +os.chdir("..") +from helpers import problem_tabs + + +def ui_tabs(file): + problem_tabs(f"apps/problem-sets/4-ui-customization/{file}") + + +``` + +## Exercise 4.6 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.6-dynamic-ui") +``` + +## Exercise 4.7 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.7-conditional-panel") +``` \ No newline at end of file diff --git a/exercises/6-reactive-event.qmd b/exercises/6-reactive-event.qmd index 89ec847..83a138b 100644 --- a/exercises/6-reactive-event.qmd +++ b/exercises/6-reactive-event.qmd @@ -1,4 +1,6 @@ - +--- +title: "Reactive events" +--- ```{python} # | echo: false import os diff --git a/exercises/7-reactive-effects.qmd b/exercises/7-reactive-effects.qmd index d35a83b..ed6b389 100644 --- a/exercises/7-reactive-effects.qmd +++ b/exercises/7-reactive-effects.qmd @@ -1,4 +1,6 @@ - +--- +title: "Reactive effects" +--- ```{python} # | echo: false import os diff --git a/exercises/8-navsets.qmd b/exercises/8-navsets.qmd new file mode 100644 index 0000000..9765531 --- /dev/null +++ b/exercises/8-navsets.qmd @@ -0,0 +1,24 @@ +--- +title: "Navsets" +--- +```{python} +# | echo: false +import os + +os.chdir("..") +from helpers import problem_tabs + + +def ui_tabs(file): + problem_tabs(f"apps/problem-sets/4-ui-customization/{file}") + + +``` + +## Exercise 4.1 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.1-tabs") +``` \ No newline at end of file diff --git a/exercises/9-cards.qmd b/exercises/9-cards.qmd new file mode 100644 index 0000000..ea648ce --- /dev/null +++ b/exercises/9-cards.qmd @@ -0,0 +1,24 @@ +--- +title: "Cards" +--- +```{python} +# | echo: false +import os + +os.chdir("..") +from helpers import problem_tabs + + +def ui_tabs(file): + problem_tabs(f"apps/problem-sets/4-ui-customization/{file}") + + +``` + +## Exercise 4.1 + +```{python} +# | echo: false +# | output: asis +ui_tabs("4.2-cards") +``` \ No newline at end of file diff --git a/slides/getting-started.qmd b/slides/getting-started.qmd index 58677bc..81fbb8f 100644 --- a/slides/getting-started.qmd +++ b/slides/getting-started.qmd @@ -1,5 +1,5 @@ --- -title: "Session 1: Intro" +title: "Getting Started" author: "Gordon Shotwell & Joe Cheng" title-slide-attributes: data-background-color: black diff --git a/slides/reactivity.qmd b/slides/reactivity.qmd index f8f54e7..8778569 100644 --- a/slides/reactivity.qmd +++ b/slides/reactivity.qmd @@ -1,5 +1,5 @@ --- -title: "Session 2: Reactivity" +title: "Reactivity" author: "Gordon Shotwell & Joe Cheng" title-slide-attributes: data-background-color: black diff --git a/slides/ui-customization.qmd b/slides/ui-customization.qmd new file mode 100644 index 0000000..87eaf5e --- /dev/null +++ b/slides/ui-customization.qmd @@ -0,0 +1,153 @@ +--- +title: "UI Customization" +author: "Gordon Shotwell & Joe Cheng" +title-slide-attributes: + data-background-color: black + data-background-image: images/toc-people-dark.svg + data-background-size: contain +format: + positconfslides-revealjs: + incremental: true + chalkboard: true + slide-number: c/t + code-copy: true + center-title-slide: false + code-link: true + highlight-style: a11y + width: "1600" + height: "900" + filters: + - positconfslides +--- +```{python} +# | echo: false +import os + +os.chdir("..") +from helpers import problem_tabs, include_shiny_folder + +``` + +## Goals + +- So far we've learned: + - How to build an app + - How Shiny renders things +- This session is all aobut how they look + +## Why care about UI design? +- It's what your stakeholders care about! +- Good UI design simplifies your application code +- Intutive UIs mean fewer questions + +## Anatomy of Shiny's UI + +- The UI is separate from the server +- UI is made up of pure functions + - You can break them into objects + - You can create them with functions +- Opinionated + - Your inputs get labels + - Reasonable look and feel +- Customizable + - Everything can be styled with CSS + - If you know JavaScript, you can use JavaScript + +## Anatomy of Shiny's UI +```{.python} +app_ui = ui.page_fluid( + ui.panel_title("Hello Penguins!"), + ui.layout_sidebar( + ui.panel_sidebar( + ui.input_slider("mass", "Mass" + ), + ui.input_action_button("reset", "Reset Slider"), + ), + ui.panel_main( + ui.output_data_frame("table"), + ), + ), +) +``` + +## Some nomenclature + +- **Container functions**: Lay out **child** elements +- **Element functions**: Generate a UI object like a +- **Child**: Elements contained by a container function +- **Parent**: The container one level up from a particular element + +## Element functions + +```{.python code-line-numbers="5,7,10"} +app_ui = ui.page_fluid( + ui.panel_title("Hello Penguins!"), + ui.layout_sidebar( + ui.panel_sidebar( + ui.input_slider("mass", "Mass" + ), + ui.input_action_button("reset", "Reset Slider"), + ), + ui.panel_main( + ui.output_data_frame("table"), + ), + ), +) +``` + +## Container functions + +```{.python code-line-numbers="1-4,9"} +app_ui = ui.page_fluid( + ui.panel_title("Hello Penguins!"), + ui.layout_sidebar( + ui.panel_sidebar( + ui.input_slider("mass", "Mass" + ), + ui.input_action_button("reset", "Reset Slider"), + ), + ui.panel_main( + ui.output_data_frame("table"), + ), + ), +) +``` + +## Container functions +- Some layout functions take any child elements and lay them out in order +- `layout_sidebar` takes specific children + - `panel_sidebar` defines the sidebar + - `panel_main` defines the main panel + +## Navsets +- It's common to put apps in tabs +- We have a set of `navet` functions which lay out `ui.nav` elements in tabs +- `ui.nav` is itself a container and allows you lay out the things on the tab +```{.python} +ui.navset_tab( + ui.nav( + "Tab1", + ui.output_plot("Plot"), + ui.output_text("some_text"), + ), + ui.nav("Tab2", ui.output_data_frame("data")), + ui.nav("Tab3", ui.output_image("image")), +) +``` + +## Your turn + +Go to [exercises/8-navsets](../exercises/8-navsets.html) or run `apps/problem-sets/exercises/4-ui-customization/4.1-tabs` locally. + + +## TODO: + +- Cards explananation +- Cards exercise +- Rows and columns explanation +- Store parts of UI as object +- Layout exercise +- Value box exercise +- Explain render_ui +- Explain conditional panel +- Dynamic UI exercize \ No newline at end of file