I have this function that iterates over my data and generates two outputs (in the example, the function check()
. Now I want to show both outputs on different cards. AFAIK the card ID has to be the same as the function that generates the output. In my example, the function check()
is run twice, which is very inefficient (in my real data this is the function generating the most computational load). Is there a way to run this function only once, but still using both outputs on different cards in shiny for Python?
MRE:
from shiny import App, render, ui, reactive
from pathlib import Path
app_ui = ui.page_fillable(
ui.layout_sidebar(
ui.sidebar(
ui.input_text("numbers", "Number you want to check", value="1,2,3,4,5,6,7,8"),
ui.input_action_button("check_numbers", "Check your numbers")
),
ui.output_ui("numbers_frames")
)
)
def server(input, output, session):
@render.ui
@reactive.event(input.check_numbers)
def numbers_frames():
return ui.TagList(
ui.layout_columns(
ui.card(
ui.card_header("even"),
ui.output_text("even"),
),
ui.card(
ui.card_header("odd"),
ui.output_text("odd"),
),
)
)
@reactive.event(input.check_numbers)
def check():
even = []
odd = []
for number in input.numbers().split(','):
number_int = int(number.strip())
if number_int % 2 == 0:
even.append(str(number_int))
else:
odd.append(str(number_int))
print("check() has been exectuted")
return (even, odd)
@output
@render.text
def even():
even_output = check()[0]
return ','.join(even_output)
@output
@render.text
def odd():
odd_output = check()[1]
return ','.join(odd_output)
src_dir = Path(__file__).parent / "src"
app = App(app_ui, server, static_assets=src_dir)
Rewrite your check()
function such that it modifies a reactive.value()
which contains the odd and the even numbers from the input. check()
is decorated by reactive.effect()
and reactive.event()
, where the event is input.check_numbers
. Then check()
only gets executed once when the user clicks the button. And then, inside the render.text()
, you display the current content of the reactive.value()
.
from shiny import App, render, ui, reactive
from pathlib import Path
app_ui = ui.page_fillable(
ui.layout_sidebar(
ui.sidebar(
ui.input_text("numbers", "Number you want to check", value="1,2,3,4,5,6,7,8"),
ui.input_action_button("check_numbers", "Check your numbers")
),
ui.output_ui("numbers_frames")
)
)
def server(input, output, session):
outputNumbers = reactive.value({})
@render.ui
@reactive.event(input.check_numbers)
def numbers_frames():
return ui.TagList(
ui.layout_columns(
ui.card(
ui.card_header("even"),
ui.output_text("even"),
),
ui.card(
ui.card_header("odd"),
ui.output_text("odd"),
),
)
)
@reactive.effect()
@reactive.event(input.check_numbers)
def check():
nums = [n for n in input.numbers().split(',') if n]
evenNums = [str(num) for num in nums if int(num.strip()) % 2 == 0]
oddNums = [str(num) for num in nums if int(num.strip()) % 2 != 0]
outputNumbers.set({'odd': ','.join(oddNums), 'even': ','.join(evenNums)})
print("check() has been executed")
@output
@render.text
def even():
return outputNumbers()['even']
@output
@render.text
def odd():
return outputNumbers()['odd']
src_dir = Path(__file__).parent / "src"
app = App(app_ui, server, static_assets=src_dir)