Skip to content

Commit

Permalink
Expressify 3 (#93)
Browse files Browse the repository at this point in the history
* Dynamic/insert ui

* Update UI

* Web API

* Translate output_ui

* Additional output_ui fixes
  • Loading branch information
Gordon Shotwell authored Jan 12, 2024
1 parent efe8085 commit 3c30a77
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 329 deletions.
135 changes: 65 additions & 70 deletions examples/python/fetch/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,84 +25,79 @@
from pprint import pformat

import pyodide.http
from shiny import App, reactive, render, ui
from shiny import reactive, render
from shiny.express import ui, input

app_ui = ui.page_fluid(
ui.input_selectize(
"city",
"Select a city:",
[
"",
"Berlin",
"Cairo",
"Chicago",
"Kyiv",
"London",
"Lima",
"Los Angeles",
"Mexico City",
"Mumbai",
"New York",
"Paris",
"São Paulo",
"Seoul",
"Shanghai",
"Taipei",
"Tokyo",
],
),
ui.input_radio_buttons(
"data_type",
"Data conversion type",
{
"json": "Parse JSON and return dict/list",
"string": "String",
"bytes": "Byte object",
},
),
ui.output_text_verbatim("info"),
)

@reactive.Calc
def url():
return f"https://goweather.herokuapp.com/weather/{input.city()}"


def server(input, output, session):
# Weather data API: https://github.com/robertoduessmann/weather-api
@reactive.Calc
def url():
return f"https://goweather.herokuapp.com/weather/{input.city()}"
@reactive.Calc
async def weather_data():
if input.city() == "":
return

@reactive.Calc
async def weather_data():
if input.city() == "":
return
response = await pyodide.http.pyfetch(url())
if response.status != 200:
raise Exception(f"Error fetching {url()}: {response.status}")

response = await pyodide.http.pyfetch(url())
if response.status != 200:
raise Exception(f"Error fetching {url()}: {response.status}")
if input.data_type() == "json":
# .json() parses the response as JSON and converts to dictionary.
data = await response.json()
elif input.data_type() == "string":
# .string() returns the response as a string.
data = await response.string()
else:
# .bytes() returns the response as a byte object.
data = await response.bytes()

if input.data_type() == "json":
# .json() parses the response as JSON and converts to dictionary.
data = await response.json()
elif input.data_type() == "string":
# .string() returns the response as a string.
data = await response.string()
else:
# .bytes() returns the response as a byte object.
data = await response.bytes()
return data

return data

@output
@render.text
async def info():
if input.city() == "":
return ""
ui.input_selectize(
"city",
"Select a city:",
[
"",
"Berlin",
"Cairo",
"Chicago",
"Kyiv",
"London",
"Lima",
"Los Angeles",
"Mexico City",
"Mumbai",
"New York",
"Paris",
"São Paulo",
"Seoul",
"Shanghai",
"Taipei",
"Tokyo",
],
)
ui.input_radio_buttons(
"data_type",
"Data conversion type",
{
"json": "Parse JSON and return dict/list",
"string": "String",
"bytes": "Byte object",
},
)

data = await weather_data()
if isinstance(data, (str, bytes)):
data_str = data
else:
data_str = pformat(data)
return f"Request URL: {url()}\nResult type: {type(data)}\n{data_str}"

@render.text
async def info():
if input.city() == "":
return ""

app = App(app_ui, server)
data = await weather_data()
if isinstance(data, (str, bytes)):
data_str = data
else:
data_str = pformat(data)
return f"Request URL: {url()}\nResult type: {type(data)}\n{data_str}"
216 changes: 25 additions & 191 deletions examples/python/input_update/app.py
Original file line number Diff line number Diff line change
@@ -1,200 +1,34 @@
from datetime import date

from shiny import App, reactive, ui
from shiny.ui import h2, tags
from shiny import reactive
from shiny.express import ui, input

app_ui = ui.page_fluid(
ui.panel_title("Changing the values of inputs from the server"),
ui.row(
ui.column(
4,
ui.panel_well(
tags.h4("These inputs control the other inputs on the page"),
ui.input_text(
"control_label", "This controls some of the labels:", "LABEL TEXT"
),
ui.input_slider(
"control_num", "This controls values:", min=1, max=20, value=15
),
),
),
ui.column(
4,
ui.panel_well(
tags.h4("These inputs are controlled by the other inputs"),
ui.input_text("inText", "Text input:", value="start text"),
ui.input_numeric(
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
),
ui.input_numeric(
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
),
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
ui.input_slider(
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
),
ui.input_slider(
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
),
ui.input_date("inDate", "Date input:"),
ui.input_date_range("inDateRange", "Date range input:"),
),
),
ui.column(
4,
ui.panel_well(
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
ui.input_checkbox_group(
"inCheckboxGroup",
"Checkbox group input:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_radio_buttons(
"inRadio",
"Radio buttons:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_select(
"inSelect",
"Select input:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_select(
"inSelect2",
"Select input 2:",
{
"option1": "label 1",
"option2": "label 2",
},
multiple=True,
),
),
ui.navset_tab(
ui.nav("panel1", h2("This is the first panel.")),
ui.nav("panel2", h2("This is the second panel.")),
id="inTabset",
),
),
),
)


def server(input, output, session):
@reactive.Effect
def _():
# We'll use these multiple times, so use short var names for
# convenience.
c_label = input.control_label()
c_num = input.control_num()

# Text =====================================================
# Change both the label and the text
ui.update_text(
"inText",
label="New " + c_label,
value="New text " + str(c_num),
)

# Number ===================================================
# Change the value
ui.update_numeric("inNumber", value=c_num)

# Change the label, value, min, and max
ui.update_numeric(
"inNumber2",
label="Number " + c_label,
value=c_num,
min=c_num - 10,
max=c_num + 10,
step=5,
)

# Slider input =============================================
# Only label and value can be set for slider
ui.update_slider("inSlider", label="Slider " + c_label, value=c_num)

# Slider range input =======================================
# For sliders that pick out a range, pass in a vector of 2
# values.
ui.update_slider("inSlider2", value=(c_num - 1, c_num + 1))
ui.h1("Updating inputs")

# Only change the upper handle
ui.update_slider("inSlider3", value=(input.inSlider3()[0], c_num + 2))

# Date input ===============================================
# Only label and value can be set for date input
ui.update_date("inDate", label="Date " + c_label, value=date(2013, 4, c_num))

# Date range input =========================================
# Only label and value can be set for date range input
ui.update_date_range(
"inDateRange",
label="Date range " + c_label,
start=date(2013, 1, c_num),
end=date(2013, 12, c_num),
min=date(2001, 1, c_num),
max=date(2030, 1, c_num),
)

# # Checkbox ===============================================
ui.update_checkbox("inCheckbox", value=c_num % 2)

# Checkbox group ===========================================
# Create a list of new options, where the name of the items
# is something like 'option label x A', and the values are
# 'option-x-A'.
opt_labels = [f"option label {c_num} {type}" for type in ["A", "B"]]
opt_vals = [f"option-{c_num}-{type}" for type in ["A", "B"]]
opts_dict = dict(zip(opt_vals, opt_labels))

# Set the label, choices, and selected item
ui.update_checkbox_group(
"inCheckboxGroup",
label="Checkbox group " + c_label,
choices=opts_dict,
selected=f"option-{c_num}-A",
)
ui.markdown(
"""
Each Shiny input has an `update_*` function which can be used to update that input.
Most options can be changed including the value, style, and input label, please see
[the docs](https://shiny.posit.co/py/api/ui.update_sidebar.html) for more examples.
"""
)

# Radio group ==============================================
ui.update_radio_buttons(
"inRadio",
label="Radio " + c_label,
choices=opts_dict,
selected=f"option-{c_num}-A",
)
# Select input =============================================
# Create a list of new options, where the name of the items
# is something like 'option label x A', and the values are
# 'option-x-A'.
ui.update_select(
"inSelect",
label="Select " + c_label,
choices=opts_dict,
selected=f"option-{c_num}-A",
)
ui.input_slider("slider", "Slider", 0, 100, 50, width="50%")
ui.input_action_button(
"to_20", "Set slider to 20", class_="btn btn-primary", width="25%"
)
ui.input_action_button(
"to_60", "Set slider to 60", class_="btn btn-primary", width="25%"
)

# Can also set the label and select an item (or more than
# one if it's a multi-select)
ui.update_select(
"inSelect2",
label="Select label " + c_label,
choices=opts_dict,
selected=f"option-{c_num}-B",
)

# Tabset input =============================================
# Change the selected tab.
# The tabsetPanel must have been created with an 'id' argument
ui.update_navs("inTabset", selected="panel2" if c_num % 2 else "panel1")
@reactive.Effect
@reactive.event(input.to_20)
def set_to_20():
ui.update_slider("slider", value=20)


app = App(app_ui, server, debug=True)
@reactive.Effect
@reactive.event(input.to_60)
def set_to_60():
ui.update_slider("slider", value=60)
Loading

0 comments on commit 3c30a77

Please sign in to comment.