Skip to content

Instantly share code, notes, and snippets.

@FerusAndBeyond
Last active December 10, 2022 23:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FerusAndBeyond/9c6144bb57f477521e1608f18feefc9f to your computer and use it in GitHub Desktop.
Save FerusAndBeyond/9c6144bb57f477521e1608f18feefc9f to your computer and use it in GitHub Desktop.
shiny5.py
from shiny import App, render, ui, reactive
app_ui = ui.page_fluid(
ui.row(
# add space from the left
ui.column(3),
# center with content
ui.column(
6,
# title
ui.h1("The to-do list"),
# will show the text output from the
# function cleared_tasks()
ui.output_text("cleared_tasks"),
# line
ui.hr(),
# input_text can be accessed using the id, here "todo_input_text"
# using input.todo_input_text(), see later
ui.input_text("todo_input_text", "", placeholder="Todo text"),
# clicking will increment input.add()
ui.input_action_button("add", "Add to-do"),
# will show UI output from the function tasks()
ui.output_ui("tasks")
),
# add space from the right
ui.column(3)
),
# seen on the tab
title="To-do"
)
# everything is wrapped inside one function
def server(input, output, session):
# reactive/stateful variables
finished_tasks = reactive.Value(0)
todo_counter = reactive.Value(0)
todos = reactive.Value([])
# This will define what is going to be shown
# at the component with id "cleared_tasks" because
# the function name is the same.
# Since finished_tasks() is called inside it,
# it will be called if this variable is changed.
@output
@render.text
def cleared_tasks():
return f"Finished tasks: {finished_tasks()}"
# This function will add a to-do to the list.
# It will only react to when input.add() is changed
# which happens when the button with id="add" is clicked
@reactive.Effect
@reactive.event(input.add)
def add():
# add todo
# and increment a count to keep track of them
count = todo_counter.get()
todo_counter.set(count+1)
# each todo is a dict with text and a button_key to know which one is which
todos.set(todos() + [dict(button_key=f"finish_{count}", text=input.todo_input_text())])
# reset input
ui.update_text("todo_input_text", value="")
# This function will remove a to-do item
# It will react to changes in todos() and button clicks
# in buttons with id = t["button_key"]
@reactive.Effect
@reactive.event(lambda: [input[t["button_key"]]() for t in todos()])
def finish():
items = todos()
key = None
# find button that has value above 0, i.e. it was clicked
for t in items:
v = input[t["button_key"]]()
if v > 0:
key = t["button_key"]
break
# if None, no button was clicked
if key is not None:
# remove the to-do that was clicked
todos.set([t for t in items if t["button_key"] != key])
# increment finished tasks
finished_tasks.set(finished_tasks() + 1)
# this function will update the interface when todos()
# is updated, i.e. an item is added or removed
@output
@render.ui
def tasks():
return ui.div(children=[
ui.row(
ui.column(2, ui.input_action_button(t["button_key"], "Finish")),
ui.column(5, ui.h5(t["text"]))
)
for t in todos()
# you can also add inline style to elements
], style="margin-top: 0.5em")
# Finally, to run everything create an App object and assign it to app
app = App(app_ui, server)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment