Layout, themes, HTML
Single page layout
Exercises
- Re-create sidebar layout app’s appearance using
ui.row()andui.column().
solutions/action-layout/recreate-sidebar/app.py
from shiny import App, ui, render
import numpy as np
from matplotlib import pyplot as plt
app_ui = ui.page_fluid(
ui.panel_title("Central limit theorem"),
ui.row(
ui.column(4,
ui.input_numeric("m", "Number of samples:", 2, min=1, max=100),
),
ui.column(8,
ui.output_plot("hist"),
),
),
)
def server(input, output, session):
@render.plot
def hist():
means = [np.mean(np.random.uniform(size=input.m())) for _ in range(10000)]
fig, ax = plt.subplots()
ax.hist(means, bins=20)
return fig
app = App(app_ui, server)This app approximately recreates appearance of an old-style app that uses ui.panel_sidebar() and ui.panel_main(), but not a new-style app that uses ui.sidebar().
- Modify the central limit theorem app to put the sidebar on the right instead of the left.
solutions/action-layout/right-sidebar/app.py
from shiny import App, ui, render
import numpy as np
from matplotlib import pyplot as plt
app_ui = ui.page_fluid(
ui.panel_title("Central limit theorem"),
ui.layout_sidebar(
ui.sidebar(
ui.input_numeric("m", "Number of samples:", 2, min=1, max=100),
position='right',
),
ui.output_plot("hist"),
),
)
def server(input, output, session):
@render.plot
def hist():
means = [np.mean(np.random.uniform(size=input.m())) for _ in range(10000)]
fig, ax = plt.subplots()
ax.hist(means, bins=20)
return fig
app = App(app_ui, server)- Create multirow layout app with two plots
solutions/action-layout/multirow/app.py
from shiny import App, ui, render, reactive
import numpy as np
from matplotlib import pyplot as plt
app_ui = ui.page_fluid(
ui.panel_title("Central limit theorem"),
ui.row(
ui.column(6,
ui.output_plot("hist"),
),
ui.column(6,
ui.output_plot("freqploy"),
),
),
ui.row(
ui.input_numeric("m", "Number of samples:", 2, min=1, max=100),
),
)
def server(input, output, session):
@reactive.calc
def means():
return [np.mean(np.random.uniform(size=input.m())) for _ in range(10000)]
@render.plot
def hist():
fig, ax = plt.subplots()
ax.hist(means(), bins=20)
return fig
@render.plot
def freqploy():
counts, bins = np.histogram(means(), bins=20)
return plt.plot(bins[:-1], counts)
app = App(app_ui, server)Multipage layouts
Tabsets
examples/action-layout/tabset-ui/app.py
from shiny import App, ui
app_ui = ui.page_fluid(
ui.navset_tab(
ui.nav_panel("Import data",
ui.input_file("file", "Data", button_label="Upload..."),
ui.input_text("delim", "Delimiter (leave blank to guess)", ""),
ui.input_numeric("skip", "Rows to skip", 0, min=0),
ui.input_numeric("rows", "Rows to preview", 10, min=1),
),
ui.nav_panel("Set parameters"),
ui.nav_panel("Visualise results"),
)
)
def server(input, output, session):
...
app = App(app_ui, server)ui.nav() has been deprecated since 2023-12-18 in favor of ui.nav_panel().
examples/action-layout/tabset-server/app.py
from shiny import App, ui, render
app_ui = ui.page_fluid(
ui.layout_sidebar(
ui.sidebar(
ui.output_text("panel"),
),
ui.navset_tab(
ui.nav_panel("panel 1", "one"),
ui.nav_panel("panel 2", "two"),
ui.nav_panel("panel 3", "three"),
id="tabset",
),
),
)
def server(input, output, session):
@render.text
def panel():
return f"Current panel: {input.tabset()}"
app = App(app_ui, server)Themes
Shiny themes
examples/action-layout/shiny-theme/app.py
from shiny import App, ui
import shinyswatch
app_ui = ui.page_fluid(
shinyswatch.theme.darkly(),
ui.layout_sidebar(
ui.sidebar(
ui.input_text("txt", "Text input:", "text here"),
ui.input_slider("slider", "Slider input:", min=1, max=100, value=30),
),
ui.h1("Theme: darkly"),
ui.h2("Header 2"),
ui.p("Some text"),
),
)
app = App(app_ui, None)Plot themes
I could not find a way to make a theme to be consistent between app and plot. I hope to come back this topic once I become to know how to apply bottswatch theme to plot.
Under the hood
Let us first define CSS class in file:
css/my-style.css
.my-class {
font-size: 3rem;
background-color: pink;
font-weight: bold
}Then, let us write HTML with the CSS and add to the UI by using ui.HTML().
examples/action-layout/html-css/app.py
from shiny import App, ui
app_ui = ui.page_fluid(
ui.include_css("css/my-style.css"),
ui.HTML(r"""
<h1>This is a heading</h1>
<p class="my-class">This is some text!</p>
<ul>
<li>First bullet</li>
<li>Second bullet</li>
</ul>
""")
)
app = App(app_ui, None)ui.include_css() is used to add CSS files.
You can use the HTML helper that Shiny provides.
examples/action-layout/html-helper/app.py
from shiny import App, ui
app_ui = ui.page_fluid(
ui.include_css("css/my-style.css"),
ui.h1("This is heading"),
ui.p("This is some text", class_="my-class"),
ui.tags.ul(
ui.tags.li("First bullet"),
ui.tags.li("Second bullet"),
),
)
app = App(app_ui, None)When applying a CSS class, note that the parameter name class_ includes underscore _. Parameter name without underscore will throw an error.
For the most important elements tag functions h1() and p(), Shiny’s ui module provides a top level functions. Others can be asccessed via tags submodule.
An example of interweaving Shiny component into a custom structure.
examples/action-layout/html-shiny-component/app.py
from shiny import App, ui, render
app_ui = ui.page_fluid(
ui.p(
"You made ",
ui.tags.b("$", ui.output_text("amount", inline=True)),
" in the last ",
ui.output_text("days", inline=True),
" days ",
),
)
def server(input, output, session):
@render.text
def amount():
return 249
@render.text
def days():
return 7
app = App(app_ui, server)Call ui.output_text() with inline=True to display the output text inline with other elements.