Skip to content

Commit

Permalink
Merge pull request #99 from posit-dev/expressify-shiny-widgets
Browse files Browse the repository at this point in the history
Expressify shiny widgets
  • Loading branch information
wch authored Jan 29, 2024
2 parents 7c24d70 + e296078 commit 590fe6d
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 147 deletions.
61 changes: 16 additions & 45 deletions examples/python/ipyleaflet/app.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,21 @@
import ipyleaflet as L
from htmltools import css
from shiny import App, reactive, render, ui
from shinywidgets import output_widget, reactive_read, register_widget
from shiny import reactive
from shiny.express import input, ui
from shinywidgets import render_widget
import ipyleaflet as ipyl

app_ui = ui.page_fluid(
ui.div(
ui.input_slider("zoom", "Map zoom level", value=12, min=1, max=18),
ui.output_ui("map_bounds"),
style=css(
display="flex", justify_content="center", align_items="center", gap="2rem"
),
),
output_widget("map"),
)
city_centers = {
"London": (51.5074, 0.1278),
"Paris": (48.8566, 2.3522),
"New York": (40.7128, -74.0060),
}
ui.input_select("center", "Center", choices=list(city_centers.keys()))


def server(input, output, session):
# Initialize and display when the session starts (1)
map = L.Map(center=(51.476852, -0.000500), zoom=12, scroll_wheel_zoom=True)
# Add a distance scale
map.add_control(L.leaflet.ScaleControl(position="bottomleft"))
register_widget("map", map)
@render_widget
def map():
return ipyl.Map(zoom=4)

# When the slider changes, update the map's zoom attribute (2)
@reactive.Effect
def _():
map.zoom = input.zoom()

# When zooming directly on the map, update the slider's value (2 and 3)
@reactive.Effect
def _():
ui.update_slider("zoom", value=reactive_read(map, "zoom"))

# Everytime the map's bounds change, update the output message (3)
@output
@render.ui
def map_bounds():
center = reactive_read(map, "center")
if len(center) == 0:
return

lat = round(center[0], 4)
lon = (center[1] + 180) % 360 - 180
lon = round(lon, 4)

return ui.p(f"Latitude: {lat}", ui.br(), f"Longitude: {lon}")


app = App(app_ui, server)
@reactive.effect
def _():
map.widget.center = city_centers[input.center()]
40 changes: 13 additions & 27 deletions examples/python/ipywidgets/app.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
import ipywidgets as ipy
from shiny import App, render, ui
from shinywidgets import output_widget, reactive_read, render_widget
from shiny.express import input, ui
from shinywidgets import render_altair

app_ui = ui.page_fluid(output_widget("slider", height="50px"), ui.output_text("value"))
ui.input_selectize("var", "Select variable", choices=["bill_length_mm", "body_mass_g"])


def server(input, output, session):
s = ipy.IntSlider(
value=5,
min=0,
max=10,
step=1,
description="Test:",
continuous_update=True,
orientation="horizontal",
readout=False,
)

@output
@render_widget
def slider():
return s

@output
@render.text
def value():
return f"The value of the slider is: {reactive_read(s, 'value')}"
@render_altair
def hist():
import altair as alt
from palmerpenguins import load_penguins


app = App(app_ui, server, debug=True)
df = load_penguins()
return (
alt.Chart(df)
.mark_bar()
.encode(x=alt.X(f"{input.var()}:Q", bin=True), y="count()")
)
4 changes: 4 additions & 0 deletions examples/python/ipywidgets/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
altair
anywidget
palmerpenguins
jsonschema
51 changes: 22 additions & 29 deletions examples/python/output_ui/app.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
from shiny import render
from shiny.express import ui, input
from shiny.express import ui, input, output, expressify, render
import numpy as np
import matplotlib.pyplot as plt


ui.input_radio_buttons(
"type",
"Input Type",
choices=["text", "select", "date", "slider", "other"],
),
ui.input_slider("card_n", "Number of cards", value=3, min=1, max=5)


@render.ui
def dyn_ui():
if input.type() == "text":
return ui.input_text("x", "Text input", placeholder="Enter text")
@expressify
def custom_card(id):
id = id + 1
with ui.card():
f"Card {id}"

elif input.type() == "select":
return ui.input_select(
"x",
"Select",
{"a": "Choice A", "b": "Choice B", "c": "Choice C"},
)
# Specifying the ID like this lets us include a renderer in the iterator
# without causing ID conflicts.
@output(id=f"hist_{id }")
@render.plot(alt="A histogram")
def histogram():
np.random.seed(19680801)
x = 100 + 15 * np.random.randn(437)
plt.hist(x, 20, density=True)

elif input.type() == "date":
return ui.input_date("x", "Choose a date")

elif input.type() == "slider":
return ui.input_slider("x", "Select a number", 1, 100, 50)

else:
return ui.div("You selected", ui.tags.b("other", style="color: red;"))


@render.text
def txt():
return f'x is: "{input.x()}"'
@render.express
def cards():
with ui.layout_columns():
for i in range(input.card_n()):
custom_card(i)
57 changes: 11 additions & 46 deletions examples/python/plotly/app.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,14 @@
# Example of using plotly via shinywidgets
import plotly.express as px
from shiny.express import input, ui
from shinywidgets import render_plotly

import numpy as np
import plotly.graph_objs as go
from shiny import App, reactive, ui
from shinywidgets import output_widget, register_widget
from sklearn.linear_model import LinearRegression
ui.page_opts(title="Filling layout", fillable=True)
with ui.layout_columns():

# Generate some data and fit a linear regression
n = 10000
dat = np.random.RandomState(0).multivariate_normal([0, 0], [(1, 0.5), (0.5, 1)], n).T
x = dat[0]
y = dat[1]
fit = LinearRegression().fit(x.reshape(-1, 1), dat[1])
xgrid = np.linspace(start=min(x), stop=max(x), num=30)
@render_plotly
def plot1():
return px.histogram(px.data.tips(), y="tip")

app_ui = ui.page_fluid(
ui.input_checkbox("show_fit", "Show fitted line", value=True),
output_widget("scatterplot"),
)


def server(input, output, session):
scatterplot = go.FigureWidget(
data=[
go.Scattergl(
x=x,
y=y,
mode="markers",
marker=dict(color="rgba(0, 0, 0, 0.05)", size=5),
),
go.Scattergl(
x=xgrid,
y=fit.intercept_ + fit.coef_[0] * xgrid,
mode="lines",
line=dict(color="red", width=2),
),
],
layout={"showlegend": False},
)

register_widget("scatterplot", scatterplot)

@reactive.Effect
def _():
scatterplot.data[1].visible = input.show_fit()


app = App(app_ui, server)
@render_plotly
def plot2():
return px.histogram(px.data.tips(), y="total_bill")

0 comments on commit 590fe6d

Please sign in to comment.