├── .envrc ├── .github └── workflows │ ├── build.yml │ └── update_shiny.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── _shinylive ├── _redirects └── favicon.ico ├── examples ├── index.json ├── python │ ├── altair │ │ ├── about.txt │ │ ├── app.py │ │ └── requirements.txt │ ├── app_with_plot │ │ ├── about.txt │ │ └── app.py │ ├── basic_app │ │ ├── about.txt │ │ └── app.py │ ├── brand │ │ ├── Monda-OFL.txt │ │ ├── Monda.ttf │ │ ├── _brand.yml │ │ ├── _colors.scss │ │ ├── about.txt │ │ ├── app.py │ │ └── requirements.txt │ ├── camera │ │ ├── about.txt │ │ └── app.py │ ├── cpuinfo │ │ ├── about.txt │ │ ├── app.py │ │ ├── fakepsutil.py │ │ └── helpers.py │ ├── extra_packages │ │ ├── about.txt │ │ ├── app.py │ │ └── requirements.txt │ ├── fetch │ │ ├── about.txt │ │ ├── app.py │ │ └── download.py │ ├── file_download │ │ ├── about.txt │ │ ├── app.py │ │ └── mtcars.csv │ ├── file_download_core │ │ ├── about.txt │ │ ├── app.py │ │ └── mtcars.csv │ ├── file_upload │ │ ├── about.txt │ │ └── app.py │ ├── hello_world │ │ ├── about.txt │ │ └── hello.py │ ├── input_checkbox │ │ ├── about.txt │ │ └── app.py │ ├── input_checkbox_group │ │ ├── about.txt │ │ └── app.py │ ├── input_date │ │ ├── about.txt │ │ └── app.py │ ├── input_date_range │ │ ├── about.txt │ │ └── app.py │ ├── input_numeric │ │ ├── about.txt │ │ └── app.py │ ├── input_password │ │ ├── about.txt │ │ └── app.py │ ├── input_radio │ │ ├── about.txt │ │ └── app.py │ ├── input_select │ │ ├── about.txt │ │ └── app.py │ ├── input_slider │ │ ├── about.txt │ │ └── app.py │ ├── input_switch │ │ ├── about.txt │ │ └── app.py │ ├── input_text │ │ ├── about.txt │ │ └── app.py │ ├── input_text_area │ │ ├── about.txt │ │ └── app.py │ ├── input_update │ │ ├── about.txt │ │ └── app.py │ ├── insert_ui │ │ ├── about.txt │ │ └── app.py │ ├── ipyleaflet │ │ ├── about.txt │ │ └── app.py │ ├── layout_sidebar │ │ ├── about.txt │ │ └── app.py │ ├── layout_two_column │ │ ├── about.txt │ │ └── app.py │ ├── modules │ │ ├── about.txt │ │ └── app.py │ ├── multiple_source_files │ │ ├── about.txt │ │ ├── app.py │ │ └── utils.py │ ├── orbit │ │ ├── about.txt │ │ ├── app.py │ │ ├── body.py │ │ ├── requirements.txt │ │ ├── simulation.py │ │ └── www │ │ │ └── coords.png │ ├── output_code │ │ ├── about.txt │ │ └── app.py │ ├── output_data_frame_grid │ │ ├── about.txt │ │ └── app.py │ ├── output_plot │ │ ├── about.txt │ │ └── app.py │ ├── output_table │ │ ├── about.txt │ │ └── app.py │ ├── output_text │ │ ├── about.txt │ │ └── app.py │ ├── output_ui │ │ ├── about.txt │ │ └── app.py │ ├── plot_interact_basic │ │ ├── about.txt │ │ ├── app.py │ │ └── mtcars.csv │ ├── plot_interact_exclude │ │ ├── about.txt │ │ ├── app.py │ │ └── mtcars.csv │ ├── plot_interact_select │ │ ├── about.txt │ │ ├── app.py │ │ └── mtcars.csv │ ├── plotly │ │ ├── about.txt │ │ ├── app.py │ │ └── requirements.txt │ ├── reactive_calc │ │ ├── about.txt │ │ └── app.py │ ├── reactive_effect │ │ ├── about.txt │ │ └── app.py │ ├── reactive_event │ │ ├── about.txt │ │ └── app.py │ ├── reactive_value │ │ ├── about.txt │ │ └── app.py │ ├── read_local_csv_file │ │ ├── about.txt │ │ ├── app.py │ │ └── mtcars.csv │ ├── regularization │ │ ├── about.txt │ │ ├── app.py │ │ └── compare.py │ ├── shinyswatch │ │ ├── about.txt │ │ ├── app.py │ │ └── requirements.txt │ ├── static_content │ │ ├── about.txt │ │ ├── app.py │ │ └── www │ │ │ └── logo.png │ └── wordle │ │ ├── about.txt │ │ ├── app.py │ │ ├── style.css │ │ └── words.py └── r │ ├── 001-hello │ ├── about.txt │ └── app.R │ ├── 002-text │ ├── about.txt │ └── app.R │ ├── 003-reactivity │ ├── about.txt │ └── app.R │ ├── 004-mpg │ ├── about.txt │ └── app.R │ ├── 005-sliders │ ├── about.txt │ └── app.R │ ├── 006-tabsets │ ├── about.txt │ └── app.R │ ├── 007-widgets │ ├── about.txt │ └── app.R │ ├── 008-html │ ├── about.txt │ ├── app.R │ └── www │ │ └── index.html │ ├── 009-upload │ ├── about.txt │ └── app.R │ ├── 010-download │ ├── about.txt │ └── app.R │ └── 011-timer │ ├── about.txt │ └── app.R ├── export_template ├── edit │ └── index.html └── index.html ├── flake.lock ├── flake.nix ├── package-lock.json ├── package.json ├── playwright.config.ts ├── playwright ├── editor-cell-test │ └── app.py ├── editor-cell.spec.ts ├── examples-viewer.spec.ts ├── helpers.ts ├── load-from-url.spec.ts ├── shiny-static.spec.ts └── static-app-test │ └── app.py ├── pyrightconfig.json ├── requirements-dev.txt ├── scripts ├── build.ts ├── build_examples_json.ts ├── create_typeshed.py └── pyodide_packages.py ├── shinylive_lock.json ├── shinylive_requirements.json ├── site ├── app │ └── .gitignore ├── editor │ └── .gitignore ├── examples │ └── .gitignore ├── favicon.ico ├── shinylive └── shinylive-sw.js ├── site_template ├── app │ └── index.html ├── editor │ └── index.html └── examples │ └── index.html ├── src ├── Components │ ├── App.css │ ├── App.tsx │ ├── Editor.css │ ├── Editor.test.tsx │ ├── Editor.tsx │ ├── ExampleSelector.css │ ├── ExampleSelector.test.tsx │ ├── ExampleSelector.tsx │ ├── HeaderBar.css │ ├── HeaderBar.tsx │ ├── Icons.css │ ├── Icons.tsx │ ├── LoadingAnimation.css │ ├── LoadingAnimation.tsx │ ├── OutputCell.css │ ├── OutputCell.tsx │ ├── ResizableGrid │ │ ├── DragToResizeHelpers.ts │ │ ├── ResizableGrid.css │ │ ├── ResizableGrid.tsx │ │ └── useDragToResizeGrid.tsx │ ├── ShareModal.css │ ├── ShareModal.tsx │ ├── Terminal.css │ ├── Terminal.tsx │ ├── Viewer.css │ ├── Viewer.tsx │ ├── codeMirror │ │ ├── FileTabs.tsx │ │ ├── extensions.ts │ │ ├── language-server │ │ │ ├── autocompletion.ts │ │ │ ├── diagnostics.ts │ │ │ ├── documentation.ts │ │ │ ├── hover.ts │ │ │ ├── lsp-extension.ts │ │ │ ├── names.ts │ │ │ ├── positions.ts │ │ │ ├── regexp-util.ts │ │ │ └── signatureHelp.ts │ │ ├── useTabbedCodeMirror.tsx │ │ └── utils.ts │ ├── filecontent.ts │ ├── gist.ts │ ├── share.ts │ ├── skull.svg │ └── utils.ts ├── assets │ ├── shiny-logo.svg │ └── shinylive-inject-socket.txt ├── awaitable-queue.ts ├── examples.ts ├── fileio.ts ├── fonts │ └── SourceSansPro-Regular.otf.woff2 ├── hooks │ ├── useOnEscOrClickOutside.tsx │ ├── usePyodide.tsx │ ├── useRunOnceOnMount.tsx │ └── useWebR.tsx ├── language-server │ ├── client.ts │ ├── null-client.ts │ └── pyright-client.ts ├── load-shinylive-sw.ts ├── lzstring-worker.ts ├── messageporthttp.ts ├── messageportwebsocket-channel.ts ├── messageportwebsocket.ts ├── parse-codeblock.ts ├── postable-error.ts ├── pyodide-proxy.ts ├── pyodide-worker.ts ├── pyodide │ ├── ffi.d.ts │ ├── pyodide.d.ts │ └── pyodide.js ├── pyright │ ├── PYRIGHT_LICENSE.txt │ ├── README.md │ ├── pyright.worker.js │ └── pyright.worker.js.map ├── run-python-blocks.ts ├── scripts │ └── codeblock-to-json.ts ├── shinylive-inject-socket.ts ├── shinylive-sw.ts ├── style-resets.css ├── types │ └── custom.d.ts ├── utils.ts └── webr-proxy.ts └── tsconfig.json /.envrc: -------------------------------------------------------------------------------- 1 | use flake -------------------------------------------------------------------------------- /.github/workflows/update_shiny.yml: -------------------------------------------------------------------------------- 1 | name: Update py-shiny submodule 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | update-shiny: 8 | name: Update py-shiny submodule 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 3.11 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.11" 19 | 20 | - name: Upgrade pip 21 | run: python -m pip install --upgrade pip 22 | 23 | - name: Check out submodules 24 | run: | 25 | make submodules 26 | 27 | - name: Pull latest py-shiny 28 | run: | 29 | make submodules-pull-shiny 30 | 31 | - name: Build shinylive 32 | run: | 33 | make all 34 | 35 | - name: Set up git within GHA 36 | uses: actions4git/setup-git@v1 37 | 38 | - name: Commit and push changes 39 | run: | 40 | PY_SHINY_SHA=$(git -C packages/py-shiny rev-parse --short HEAD) 41 | git add packages/py-shiny shinylive_lock.json && \ 42 | git commit --message "Pull latest posit-dev/py-shiny@$PY_SHINY_SHA" && \ 43 | git push origin HEAD:main 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /venv/ 2 | **/.venv/ 3 | **/.DS_store/ 4 | /node_modules/ 5 | /packages/*.whl 6 | /dist/ 7 | /downloads/ 8 | /build/ 9 | __pycache__ 10 | /typings/ 11 | 12 | /test-results/ 13 | /playwright-report/ 14 | /playwright/.cache/ 15 | 16 | .vscode/*.log 17 | 18 | playwright/static-build/ 19 | 20 | /.luarc.json 21 | 22 | _shinylive/py 23 | _shinylive/r 24 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "packages/py-shiny"] 2 | path = packages/py-shiny 3 | url = https://github.com/posit-dev/py-shiny.git 4 | [submodule "packages/py-htmltools"] 5 | path = packages/py-htmltools 6 | url = https://github.com/posit-dev/py-htmltools.git 7 | [submodule "typeshed"] 8 | path = typeshed 9 | url = https://github.com/python/typeshed.git 10 | [submodule "packages/py-shinywidgets"] 11 | path = packages/py-shinywidgets 12 | url = https://github.com/posit-dev/py-shinywidgets.git 13 | [submodule "packages/py-faicons"] 14 | path = packages/py-faicons 15 | url = https://github.com/posit-dev/py-faicons.git 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": true, 3 | "files.insertFinalNewline": true, 4 | "[javascript]": { 5 | "editor.formatOnSave": true, 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "[typescript]": { 9 | "editor.formatOnSave": true, 10 | "editor.defaultFormatter": "esbenp.prettier-vscode" 11 | }, 12 | "[typescriptreact]": { 13 | "editor.formatOnSave": true, 14 | "editor.defaultFormatter": "esbenp.prettier-vscode" 15 | }, 16 | "[json]": { 17 | "editor.formatOnSave": true, 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "[html]": { 21 | "editor.formatOnSave": true, 22 | "editor.defaultFormatter": "esbenp.prettier-vscode" 23 | }, 24 | "[css]": { 25 | "editor.formatOnSave": true, 26 | "editor.defaultFormatter": "esbenp.prettier-vscode" 27 | }, 28 | "python.formatting.provider": "black", 29 | "[python]": { 30 | "editor.rulers": [88], 31 | "editor.formatOnSave": true, 32 | "editor.tabSize": 4, 33 | "editor.codeActionsOnSave": { 34 | "source.organizeImports": "explicit" 35 | }, 36 | }, 37 | "isort.args":["--profile", "black"], 38 | "flake8.args": [ 39 | "--ignore=E501,W503" 40 | ], 41 | "eslint.options": { 42 | "reportUnusedDisableDirectives": "error" 43 | }, 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 RStudio, PBC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_shinylive/_redirects: -------------------------------------------------------------------------------- 1 | / /py/examples/ 302 2 | /py /py/examples/ 302 3 | /py/ /py/examples/ 302 4 | /r /r/examples/ 302 5 | /r/ /r/examples/ 302 6 | -------------------------------------------------------------------------------- /_shinylive/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posit-dev/shinylive/8acb41b0d4090308551e2f30dccb68fa6c9071a9/_shinylive/favicon.ico -------------------------------------------------------------------------------- /examples/index.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "engine": "python", 4 | "examples": [ 5 | { 6 | "category": "Basic", 7 | "apps": ["basic_app", "app_with_plot"] 8 | }, 9 | { 10 | "category": "Featured", 11 | "apps": ["cpuinfo", "regularization", "plotly", "altair", "ipyleaflet"] 12 | }, 13 | { 14 | "category": "Intermediate", 15 | "apps": [ 16 | "multiple_source_files", 17 | "read_local_csv_file", 18 | "file_upload", 19 | "insert_ui", 20 | "input_update", 21 | "extra_packages", 22 | "fetch", 23 | "brand" 24 | ] 25 | }, 26 | { 27 | "category": "Reactivity", 28 | "apps": [ 29 | "reactive_event", 30 | "reactive_effect", 31 | "reactive_calc", 32 | "reactive_value" 33 | ] 34 | }, 35 | { 36 | "category": "Shiny Core", 37 | "apps": [ 38 | "file_download_core", 39 | "modules", 40 | "orbit", 41 | "wordle", 42 | "static_content" 43 | ] 44 | }, 45 | { 46 | "category": "Interactive plots", 47 | "apps": [ 48 | "plot_interact_basic", 49 | "plot_interact_select", 50 | "plot_interact_exclude" 51 | ] 52 | }, 53 | { 54 | "category": "Non-Apps", 55 | "apps": ["hello_world"] 56 | } 57 | ] 58 | }, 59 | { 60 | "engine": "r", 61 | "examples": [ 62 | { 63 | "category": "R examples", 64 | "apps": [ 65 | "001-hello", 66 | "002-text", 67 | "003-reactivity", 68 | "004-mpg", 69 | "005-sliders", 70 | "006-tabsets", 71 | "007-widgets", 72 | "008-html", 73 | "009-upload", 74 | "010-download", 75 | "011-timer" 76 | ] 77 | } 78 | ] 79 | } 80 | ] 81 | -------------------------------------------------------------------------------- /examples/python/altair/about.txt: -------------------------------------------------------------------------------- 1 | altair 2 | Interactive plot with altair 3 | -------------------------------------------------------------------------------- /examples/python/altair/app.py: -------------------------------------------------------------------------------- 1 | from shiny.express import input, ui 2 | from shinywidgets import render_altair 3 | 4 | ui.input_selectize("var", "Select variable", choices=["bill_length_mm", "body_mass_g"]) 5 | 6 | 7 | @render_altair 8 | def hist(): 9 | import altair as alt 10 | from palmerpenguins import load_penguins 11 | 12 | df = load_penguins() 13 | return ( 14 | alt.Chart(df) 15 | .mark_bar() 16 | .encode(x=alt.X(f"{input.var()}:Q", bin=True), y="count()") 17 | ) 18 | -------------------------------------------------------------------------------- /examples/python/altair/requirements.txt: -------------------------------------------------------------------------------- 1 | altair 2 | anywidget 3 | palmerpenguins 4 | jsonschema -------------------------------------------------------------------------------- /examples/python/app_with_plot/about.txt: -------------------------------------------------------------------------------- 1 | App with plot 2 | -------------------------------------------------------------------------------- /examples/python/app_with_plot/app.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from shiny.express import ui, input, render 4 | 5 | with ui.sidebar(): 6 | ui.input_slider("n", "N", 0, 100, 20) 7 | 8 | 9 | @render.plot(alt="A histogram") 10 | def histogram(): 11 | np.random.seed(19680801) 12 | x = 100 + 15 * np.random.randn(437) 13 | plt.hist(x, input.n(), density=True) 14 | -------------------------------------------------------------------------------- /examples/python/basic_app/about.txt: -------------------------------------------------------------------------------- 1 | Basic App 2 | -------------------------------------------------------------------------------- /examples/python/basic_app/app.py: -------------------------------------------------------------------------------- 1 | from shiny.express import input, render, ui 2 | 3 | ui.input_slider("n", "N", 0, 100, 20) 4 | 5 | 6 | @render.code 7 | def txt(): 8 | return f"n*2 is {input.n() * 2}" 9 | -------------------------------------------------------------------------------- /examples/python/brand/Monda.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posit-dev/shinylive/8acb41b0d4090308551e2f30dccb68fa6c9071a9/examples/python/brand/Monda.ttf -------------------------------------------------------------------------------- /examples/python/brand/_brand.yml: -------------------------------------------------------------------------------- 1 | # Learn more about brand.yml at https://posit-dev.github.io/brand-yml 2 | meta: 3 | name: 4 | full: "Retro Arcade Brand" 5 | short: "RetroArc" 6 | link: 7 | home: https://retroarc.example.com 8 | mastodon: https://mastodon.social/@retroarc 9 | github: https://github.com/retroarc 10 | linkedin: https://linkedin.com/company/retroarc 11 | twitter: https://twitter.com/retroarc 12 | facebook: https://facebook.com/retroarc 13 | 14 | # This example doesn't include a brand logo 15 | # See: https://posit-dev.github.io/brand-yml/brand/logo.html 16 | # logo: brand-yml.png 17 | 18 | color: 19 | palette: 20 | pink: "#E83E8C" 21 | blue: "#007BFF" 22 | cyan: "#17A2B8" 23 | teal: "#20C997" 24 | green: "#28A745" 25 | yellow: "#FFD700" 26 | orange: "#FF7F50" 27 | red: "#FF3333" 28 | purple: "#6F42C1" 29 | indigo: "#6610F2" 30 | black: "#1A1A1A" 31 | white: "#F8F8F8" 32 | foreground: black 33 | background: white 34 | primary: purple 35 | success: green 36 | info: cyan 37 | warning: yellow 38 | danger: orange 39 | light: white 40 | dark: black 41 | 42 | typography: 43 | fonts: 44 | - family: Quantico 45 | source: google 46 | weight: [700] 47 | style: [normal, italic] 48 | display: swap 49 | - family: Monda 50 | source: file 51 | files: 52 | - path: Monda.ttf 53 | weight: 400..700 54 | - family: Share Tech Mono 55 | source: bunny 56 | weight: 400 57 | style: normal 58 | display: swap 59 | base: 60 | family: Monda 61 | size: 17px 62 | weight: 400 63 | line-height: 1.5 64 | headings: 65 | family: Quantico 66 | weight: 400 67 | line-height: 1.2 68 | style: normal 69 | monospace: 70 | family: Share Tech Mono 71 | size: 0.9em 72 | weight: 400 73 | monospace-inline: 74 | family: Share Tech Mono 75 | # size: 0.9em 76 | weight: 400 77 | color: yellow 78 | background-color: "#1a1a1add" 79 | monospace-block: 80 | family: Share Tech Mono 81 | size: 1.1em 82 | weight: 400 83 | color: green 84 | background-color: black 85 | line-height: 1.4 86 | link: 87 | weight: 400 88 | background-color: purple 89 | color: white 90 | decoration: "underline" 91 | 92 | defaults: 93 | bootstrap: 94 | defaults: 95 | my-pink: "$brand-pink" 96 | shiny: 97 | theme: 98 | preset: shiny 99 | rules: | 100 | .navbar-brand { color: $my-pink } 101 | -------------------------------------------------------------------------------- /examples/python/brand/about.txt: -------------------------------------------------------------------------------- 1 | Branded Theming 2 | Using brand.yml 3 | -------------------------------------------------------------------------------- /examples/python/brand/requirements.txt: -------------------------------------------------------------------------------- 1 | shiny[theme] 2 | matplotlib 3 | numpy 4 | -------------------------------------------------------------------------------- /examples/python/camera/about.txt: -------------------------------------------------------------------------------- 1 | Camera 2 | Use a phone camera for input 3 | -------------------------------------------------------------------------------- /examples/python/camera/app.py: -------------------------------------------------------------------------------- 1 | # This app uses a phone or tablet's camera to take a picture and process it. It cannot 2 | # use a desktop computer's webcam. If opened on a desktop computer, it will open up an 3 | # ordinary file chooser dialog. 4 | # 5 | # This particular application uses some memory-intensive libraries, like skimage, and so 6 | # it may not work properly on all phones. However, the camera input part should still 7 | # work on most phones. 8 | 9 | import numpy as np 10 | import skimage 11 | from PIL import Image, ImageOps 12 | from shiny import App, render, ui 13 | from shiny.types import FileInfo, ImgData, SilentException 14 | 15 | app_ui = ui.page_fluid( 16 | ui.input_file( 17 | "file", 18 | None, 19 | button_label="Open camera", 20 | # This tells it to accept still photos only (not videos). 21 | accept="image/*", 22 | # This tells it to use the phone's rear camera. Use "user" for the front camera. 23 | capture="environment", 24 | ), 25 | ui.output_image("image"), 26 | ) 27 | 28 | 29 | def server(input, output, session): 30 | @render.image 31 | async def image() -> ImgData: 32 | file_infos: list[FileInfo] = input.file() 33 | if not file_infos: 34 | raise SilentException() 35 | 36 | file_info = file_infos[0] 37 | img = Image.open(file_info["datapath"]) 38 | 39 | # Resize to 1000 pixels wide 40 | basewidth = 1000 41 | wpercent = basewidth / float(img.size[0]) 42 | hsize = int((float(img.size[1]) * float(wpercent))) 43 | img = img.resize((basewidth, hsize), Image.ANTIALIAS) 44 | 45 | # Convert to grayscale 46 | img = ImageOps.grayscale(img) 47 | 48 | # Rotate image based on EXIF tag 49 | img = ImageOps.exif_transpose(img) 50 | 51 | # Convert to numpy array for skimage processing 52 | image_data = np.array(img) 53 | 54 | # Apply thresholding 55 | val = skimage.filters.threshold_otsu(image_data) 56 | mask = image_data < val 57 | 58 | # Save for render.image 59 | skimage.io.imsave("small.png", skimage.util.img_as_ubyte(mask)) 60 | return {"src": "small.png", "width": "100%"} 61 | 62 | 63 | app = App(app_ui, server) 64 | -------------------------------------------------------------------------------- /examples/python/cpuinfo/about.txt: -------------------------------------------------------------------------------- 1 | CPU info 2 | CPU load monitor 3 | -------------------------------------------------------------------------------- /examples/python/cpuinfo/fakepsutil.py: -------------------------------------------------------------------------------- 1 | """Generates synthetic data""" 2 | 3 | import numpy as np 4 | 5 | 6 | def cpu_count(logical: bool = True): 7 | return 8 if logical else 4 8 | 9 | 10 | last_sample = np.random.uniform(0, 100, size=cpu_count(True)) 11 | 12 | 13 | def cpu_percent(interval=None, percpu=False): 14 | global last_sample 15 | delta = np.random.normal(scale=10, size=len(last_sample)) 16 | last_sample = (last_sample + delta).clip(0, 100) 17 | if percpu: 18 | return last_sample.tolist() 19 | else: 20 | return last_sample.mean() 21 | -------------------------------------------------------------------------------- /examples/python/cpuinfo/helpers.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import pandas as pd 6 | 7 | 8 | def hide_ticks(axis): 9 | for ticks in [axis.get_major_ticks(), axis.get_minor_ticks()]: 10 | for tick in ticks: 11 | tick.tick1line.set_visible(False) 12 | tick.tick2line.set_visible(False) 13 | tick.label1.set_visible(False) 14 | tick.label2.set_visible(False) 15 | 16 | 17 | def plot_cpu(history, nsamples, ncpu, cmap): 18 | if history is None: 19 | history = np.array([]) 20 | history.shape = (ncpu, 0) 21 | 22 | # Throw away samples too old to fit on the plot 23 | if history.shape[1] > nsamples: 24 | history = history[:, -nsamples:] 25 | 26 | ncols = 2 27 | nrows = int(ceil(ncpu / ncols)) 28 | fig, axeses = plt.subplots( 29 | nrows=nrows, 30 | ncols=ncols, 31 | squeeze=False, 32 | ) 33 | for i in range(0, ncols * nrows): 34 | row = i // ncols 35 | col = i % ncols 36 | axes = axeses[row, col] 37 | if i >= len(history): 38 | axes.set_visible(False) 39 | continue 40 | data = history[i] 41 | axes.yaxis.set_label_position("right") 42 | axes.yaxis.tick_right() 43 | axes.set_xlim(-(nsamples - 1), 0) 44 | axes.set_ylim(0, 100) 45 | 46 | assert len(data) <= nsamples 47 | 48 | # Set up an array of x-values that will right-align the data relative to the 49 | # plotting area 50 | x = np.arange(0, len(data)) 51 | x = np.flip(-x) 52 | 53 | # Color bars by cmap 54 | color = plt.get_cmap(cmap)(data / 100) 55 | axes.bar(x, data, color=color, linewidth=0, width=1.0) 56 | 57 | axes.set_yticks([25, 50, 75]) 58 | for ytl in axes.get_yticklabels(): 59 | if col == ncols - 1 or i == ncpu - 1 or True: 60 | ytl.set_fontsize(7) 61 | else: 62 | ytl.set_visible(False) 63 | hide_ticks(axes.yaxis) 64 | for xtl in axes.get_xticklabels(): 65 | xtl.set_visible(False) 66 | hide_ticks(axes.xaxis) 67 | axes.grid(True, linewidth=0.25) 68 | 69 | return fig 70 | -------------------------------------------------------------------------------- /examples/python/extra_packages/about.txt: -------------------------------------------------------------------------------- 1 | Extra packages 2 | Install extra packages from requirements.txt 3 | -------------------------------------------------------------------------------- /examples/python/extra_packages/app.py: -------------------------------------------------------------------------------- 1 | import attrs 2 | import isodate 3 | import tabulate 4 | from shiny.express import ui 5 | 6 | ui.markdown( 7 | """ 8 | This application doesn't actually do anything -- it simply demonstrates how to 9 | import extra packages from PyPI, by using a `requirements.txt` file. 10 | 11 | Packages listed in `requirements.txt` will be installed by micropip just before 12 | the app is started. This means that each time someone visits the app, the 13 | packages will be downloaded and installed into the browser's Pyodide 14 | environment. 15 | 16 | If you want test whether a package can be installed this way, either edit 17 | `requirements.txt` and reload this app, or try running this in the terminal: 18 | 19 | ``` 20 | import micropip 21 | await micropip.install("mypackage") 22 | import mypackage 23 | """ 24 | ) 25 | -------------------------------------------------------------------------------- /examples/python/extra_packages/requirements.txt: -------------------------------------------------------------------------------- 1 | # Packages listed in this file will be installed by micropip just before the app 2 | # is started. This means that each time someone visits the app, the packages 3 | # will be downloaded and installed into the browser's Pyodide environment. 4 | # 5 | # Each line must contain either a requirements specification or the URL for a 6 | # wheel file. 7 | isodate 8 | attrs==21.4.0 9 | httpx [socks,http2] 10 | https://files.pythonhosted.org/packages/ca/80/7c0cad11bd99985cfe7c09427ee0b4f9bd6b048bd13d4ffb32c6db237dfb/tabulate-0.8.9-py3-none-any.whl 11 | -------------------------------------------------------------------------------- /examples/python/fetch/about.txt: -------------------------------------------------------------------------------- 1 | Fetch data from a web API 2 | -------------------------------------------------------------------------------- /examples/python/fetch/app.py: -------------------------------------------------------------------------------- 1 | # Normally in Python, you would use urllib.request.urlopen() to fetch data from a web 2 | # API. However, it won't work in Pyodide because sockets are not available. 3 | # 4 | # Instead, you can pyodide.http.pyfetch(), which is a wrapper for the JavaScript fetch() 5 | # function. Note that when running shinylive, the endpoint MUST use https. This is 6 | # because shinylive must be served over https (unless you are running on localhost), 7 | # and browsers will not allow a https page to fetch data with http. 8 | # 9 | # If you are fetching data from a different origin, that server will also need to allow 10 | # cross-origin requests by setting the Access-Control-Allow-Origin header. See 11 | # https://en.wikipedia.org/wiki/Cross-origin_resource_sharing for more information. 12 | # 13 | # If you see "Failed to fetch" in the Python console, it is likely because the endpoint 14 | # is http and not https, or because the server does not allow cross-origin requests. 15 | # 16 | # One important difference between urllib.request.urlopen() and pyodide.http.pyfetch() 17 | # is that the latter is asynchronous. In a Shiny app, this just means that the 18 | # reactive.calc's and outputs must have `async` in front of the function definitions, 19 | # and when they're called, they must have `await` in front of them. 20 | # 21 | # If you want to write code that works in both regular Python and Pyodide, see the 22 | # download.py file for a wrapper function that can be used to make requests in both 23 | # regular Python and Pyodide. (Note that the function isn't actually used in this app.) 24 | 25 | from pprint import pformat 26 | 27 | import pyodide.http 28 | from shiny import reactive 29 | from shiny.express import input, render, ui 30 | 31 | 32 | @reactive.calc 33 | def url(): 34 | return f"https://goweather.herokuapp.com/weather/{input.city()}" 35 | 36 | 37 | @reactive.calc 38 | async def weather_data(): 39 | if input.city() == "": 40 | return 41 | 42 | response = await pyodide.http.pyfetch(url()) 43 | if response.status != 200: 44 | raise Exception(f"Error fetching {url()}: {response.status}") 45 | 46 | if input.data_type() == "json": 47 | # .json() parses the response as JSON and converts to dictionary. 48 | data = await response.json() 49 | elif input.data_type() == "string": 50 | # .string() returns the response as a string. 51 | data = await response.string() 52 | else: 53 | # .bytes() returns the response as a byte object. 54 | data = await response.bytes() 55 | 56 | return data 57 | 58 | 59 | ui.input_selectize( 60 | "city", 61 | "Select a city:", 62 | [ 63 | "", 64 | "Berlin", 65 | "Cairo", 66 | "Chicago", 67 | "Kyiv", 68 | "London", 69 | "Lima", 70 | "Los Angeles", 71 | "Mexico City", 72 | "Mumbai", 73 | "New York", 74 | "Paris", 75 | "São Paulo", 76 | "Seoul", 77 | "Shanghai", 78 | "Taipei", 79 | "Tokyo", 80 | ], 81 | ) 82 | ui.input_radio_buttons( 83 | "data_type", 84 | "Data conversion type", 85 | { 86 | "json": "Parse JSON and return dict/list", 87 | "string": "String", 88 | "bytes": "Byte object", 89 | }, 90 | ) 91 | 92 | 93 | @render.code 94 | async def info(): 95 | if input.city() == "": 96 | return "" 97 | 98 | data = await weather_data() 99 | if isinstance(data, (str, bytes)): 100 | data_str = data 101 | else: 102 | data_str = pformat(data) 103 | return f"Request URL: {url()}\nResult type: {type(data)}\n{data_str}" 104 | -------------------------------------------------------------------------------- /examples/python/fetch/download.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Any, Literal 3 | 4 | 5 | class HttpResponse: 6 | def __init__(self, status: int, data: Any): 7 | self.status = status 8 | self.data = data 9 | 10 | 11 | async def get_url( 12 | url: str, type: Literal["string", "bytes", "json"] = "string" 13 | ) -> HttpResponse: 14 | """ 15 | An async wrapper function for http requests that works in both regular Python and 16 | Pyodide. 17 | 18 | In Pyodide, it uses the pyodide.http.pyfetch() function, which is a wrapper for the 19 | JavaScript fetch() function. pyfetch() is asynchronous, so this whole function must 20 | also be async. 21 | 22 | In regular Python, it uses the urllib.request.urlopen() function. 23 | 24 | Args: 25 | url: The URL to download. 26 | 27 | type: How to parse the content. If "string", it returns the response as a 28 | string. If "bytes", it returns the response as a bytes object. If "json", it 29 | parses the reponse as JSON, then converts it to a Python object, usually a 30 | dictionary or list. 31 | 32 | Returns: 33 | A HttpResponse object 34 | """ 35 | import sys 36 | 37 | if "pyodide" in sys.modules: 38 | import pyodide.http 39 | 40 | response = await pyodide.http.pyfetch(url) 41 | 42 | if type == "json": 43 | # .json() parses the response as JSON and converts to dictionary. 44 | data = await response.json() 45 | elif type == "string": 46 | # .string() returns the response as a string. 47 | data = await response.string() 48 | elif type == "bytes": 49 | # .bytes() returns the response as a byte object. 50 | data = await response.bytes() 51 | 52 | return HttpResponse(response.status, data) 53 | 54 | else: 55 | import urllib.request 56 | 57 | response = urllib.request.urlopen(url) 58 | if type == "json": 59 | data = json.loads(response.read().decode("utf-8")) 60 | elif type == "string": 61 | data = response.read().decode("utf-8") 62 | elif type == "bytes": 63 | data = response.read() 64 | 65 | return HttpResponse(response.status, data) 66 | -------------------------------------------------------------------------------- /examples/python/file_download/about.txt: -------------------------------------------------------------------------------- 1 | File download 2 | -------------------------------------------------------------------------------- /examples/python/file_download/app.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import io 3 | from datetime import date 4 | from pathlib import Path 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from shiny import App, render, ui 9 | 10 | 11 | # A card component wrapper. 12 | def ui_card(title, *args): 13 | return ( 14 | ui.div( 15 | {"class": "card mb-4"}, 16 | ui.div(title, class_="card-header"), 17 | ui.div({"class": "card-body"}, *args), 18 | ), 19 | ) 20 | 21 | 22 | app_ui = ui.page_fluid( 23 | ui_card( 24 | "Download a pre-existing file, using its existing name on disk.", 25 | ui.download_button("download1", "Download CSV"), 26 | ), 27 | ui_card( 28 | "Download a PNG that is generated dynamically.", 29 | ui.input_text("title", "Plot title", "Random scatter plot"), 30 | ui.input_slider("num_points", "Number of data points", 1, 100, 50), 31 | ui.download_button("download2", "Download PNG"), 32 | ), 33 | ui_card( 34 | "Download a file with name that is generated dynamically.", 35 | ui.download_button("download3", "Download CSV"), 36 | ), 37 | ) 38 | 39 | 40 | def server(input, output, session): 41 | @render.download() 42 | def download1(): 43 | # This is the simplest case. The implementation simply returns the path to a 44 | # file on disk. 45 | path = Path(__file__).parent / "mtcars.csv" 46 | return str(path) 47 | 48 | @render.download(filename="image.png") 49 | def download2(): 50 | # Another way to implement a file download is by yielding bytes; either all at 51 | # once, like in this case, or by yielding multiple times. When using this 52 | # approach, you should pass a filename argument to @render.download, which 53 | # determines what the browser will name the downloaded file. 54 | x = np.random.uniform(size=input.num_points()) 55 | y = np.random.uniform(size=input.num_points()) 56 | plt.figure() 57 | plt.scatter(x, y) 58 | plt.title(input.title()) 59 | with io.BytesIO() as buf: 60 | plt.savefig(buf, format="png") 61 | yield buf.getvalue() 62 | 63 | @render.download( 64 | filename=lambda: f"data-{date.today().isoformat()}-{np.random.randint(100,999)}.csv" 65 | ) 66 | async def download3(): 67 | # This version uses a function to generate the filename. It also yields data 68 | # multiple times. 69 | await asyncio.sleep(0.25) 70 | yield "one,two,three\n" 71 | yield "新,1,2\n" 72 | yield "型,4,5\n" 73 | 74 | 75 | app = App(app_ui, server) 76 | -------------------------------------------------------------------------------- /examples/python/file_download/mtcars.csv: -------------------------------------------------------------------------------- 1 | "mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" 2 | 21,6,160,110,3.9,2.62,16.46,0,1,4,4 3 | 21,6,160,110,3.9,2.875,17.02,0,1,4,4 4 | 22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 5 | 21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 6 | 18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 7 | 18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 8 | 14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 9 | 24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 10 | 22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 11 | 19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 12 | 17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 13 | 16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 14 | 17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15 | 15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 16 | 10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 17 | 10.4,8,460,215,3,5.424,17.82,0,0,3,4 18 | 14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 19 | 32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 20 | 30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 21 | 33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 22 | 21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 23 | 15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 24 | 15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 25 | 13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 26 | 19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27 | 27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 28 | 26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 29 | 30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 30 | 15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 31 | 19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 32 | 15,8,301,335,3.54,3.57,14.6,0,1,5,8 33 | 21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 34 | -------------------------------------------------------------------------------- /examples/python/file_download_core/about.txt: -------------------------------------------------------------------------------- 1 | File download 2 | -------------------------------------------------------------------------------- /examples/python/file_download_core/app.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import io 3 | from datetime import date 4 | from pathlib import Path 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from shiny import App, render, ui 9 | 10 | 11 | # A card component wrapper. 12 | def ui_card(title, *args): 13 | return ( 14 | ui.div( 15 | {"class": "card mb-4"}, 16 | ui.div(title, class_="card-header"), 17 | ui.div({"class": "card-body"}, *args), 18 | ), 19 | ) 20 | 21 | 22 | app_ui = ui.page_fluid( 23 | ui_card( 24 | "Download a pre-existing file, using its existing name on disk.", 25 | ui.download_button("download1", "Download CSV"), 26 | ), 27 | ui_card( 28 | "Download a PNG that is generated dynamically.", 29 | ui.input_text("title", "Plot title", "Random scatter plot"), 30 | ui.input_slider("num_points", "Number of data points", 1, 100, 50), 31 | ui.download_button("download2", "Download PNG"), 32 | ), 33 | ui_card( 34 | "Download a file with name that is generated dynamically.", 35 | ui.download_button("download3", "Download CSV"), 36 | ), 37 | ) 38 | 39 | 40 | def server(input, output, session): 41 | @render.download() 42 | def download1(): 43 | # This is the simplest case. The implementation simply returns the path to a 44 | # file on disk. 45 | path = Path(__file__).parent / "mtcars.csv" 46 | return str(path) 47 | 48 | @render.download(filename="image.png") 49 | def download2(): 50 | # Another way to implement a file download is by yielding bytes; either all at 51 | # once, like in this case, or by yielding multiple times. When using this 52 | # approach, you should pass a filename argument to @render.download, which 53 | # determines what the browser will name the downloaded file. 54 | x = np.random.uniform(size=input.num_points()) 55 | y = np.random.uniform(size=input.num_points()) 56 | plt.figure() 57 | plt.scatter(x, y) 58 | plt.title(input.title()) 59 | with io.BytesIO() as buf: 60 | plt.savefig(buf, format="png") 61 | yield buf.getvalue() 62 | 63 | @render.download( 64 | filename=lambda: f"data-{date.today().isoformat()}-{np.random.randint(100,999)}.csv" 65 | ) 66 | async def download3(): 67 | # This version uses a function to generate the filename. It also yields data 68 | # multiple times. 69 | await asyncio.sleep(0.25) 70 | yield "one,two,three\n" 71 | yield "新,1,2\n" 72 | yield "型,4,5\n" 73 | 74 | 75 | app = App(app_ui, server) 76 | -------------------------------------------------------------------------------- /examples/python/file_download_core/mtcars.csv: -------------------------------------------------------------------------------- 1 | "mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" 2 | 21,6,160,110,3.9,2.62,16.46,0,1,4,4 3 | 21,6,160,110,3.9,2.875,17.02,0,1,4,4 4 | 22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 5 | 21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 6 | 18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 7 | 18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 8 | 14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 9 | 24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 10 | 22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 11 | 19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 12 | 17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 13 | 16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 14 | 17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15 | 15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 16 | 10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 17 | 10.4,8,460,215,3,5.424,17.82,0,0,3,4 18 | 14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 19 | 32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 20 | 30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 21 | 33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 22 | 21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 23 | 15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 24 | 15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 25 | 13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 26 | 19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27 | 27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 28 | 26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 29 | 30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 30 | 15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 31 | 19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 32 | 15,8,301,335,3.54,3.57,14.6,0,1,5,8 33 | 21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 34 | -------------------------------------------------------------------------------- /examples/python/file_upload/about.txt: -------------------------------------------------------------------------------- 1 | File upload 2 | -------------------------------------------------------------------------------- /examples/python/file_upload/app.py: -------------------------------------------------------------------------------- 1 | import mimetypes 2 | from math import ceil 3 | from typing import List 4 | 5 | from shiny.express import input, render, ui 6 | 7 | MAX_SIZE = 50000 8 | ui.input_file("file1", "Choose a file to upload:", multiple=True) 9 | ui.input_radio_buttons("type", "Type:", ["Text", "Binary"]) 10 | 11 | 12 | def format_hexdump(data: bytes) -> str: 13 | hex_vals = ["{:02x}".format(b) for b in data] 14 | hex_vals = group_into_blocks(hex_vals, 16) 15 | hex_vals = [" ".join(row) for row in hex_vals] 16 | hex_vals = "\n".join(hex_vals) 17 | return hex_vals 18 | 19 | 20 | def group_into_blocks(x: List[str], blocksize: int): 21 | """ 22 | Given a list, return a list of lists, where the inner lists each have `blocksize` 23 | elements. 24 | """ 25 | return [ 26 | x[i * blocksize : (i + 1) * blocksize] for i in range(ceil(len(x) / blocksize)) 27 | ] 28 | 29 | 30 | @render.code 31 | def file_content(): 32 | file_infos = input.file1() 33 | if not file_infos: 34 | return 35 | 36 | # file_infos is a list of dicts; each dict represents one file. Example: 37 | # [ 38 | # { 39 | # 'name': 'data.csv', 40 | # 'size': 2601, 41 | # 'type': 'text/csv', 42 | # 'datapath': '/tmp/fileupload-1wnx_7c2/tmpga4x9mps/0.csv' 43 | # } 44 | # ] 45 | out_str = "" 46 | for file_info in file_infos: 47 | out_str += ( 48 | "=" * 47 49 | + "\n" 50 | + file_info["name"] 51 | + "\nMIME type: " 52 | + str(mimetypes.guess_type(file_info["name"])[0]) 53 | ) 54 | if file_info["size"] > MAX_SIZE: 55 | out_str += f"\nTruncating at {MAX_SIZE} bytes." 56 | 57 | out_str += "\n" + "=" * 47 + "\n" 58 | 59 | if input.type() == "Text": 60 | with open(file_info["datapath"], "r") as f: 61 | out_str += f.read(MAX_SIZE) 62 | else: 63 | with open(file_info["datapath"], "rb") as f: 64 | data = f.read(MAX_SIZE) 65 | out_str += format_hexdump(data) 66 | 67 | return out_str 68 | -------------------------------------------------------------------------------- /examples/python/hello_world/about.txt: -------------------------------------------------------------------------------- 1 | Hello, World 2 | A non-application example 3 | -------------------------------------------------------------------------------- /examples/python/hello_world/hello.py: -------------------------------------------------------------------------------- 1 | # Python code that is not part of a Shiny app can also be used for examples 2 | # in Shinylive. 3 | # You can run this line-by-line by pressing cmd- or ctrl-Enter, or you can 4 | # select a block of text and run it with cmd-/ctrl-Enter. 5 | def hello(): 6 | print("Hello, world!") 7 | 8 | 9 | hello() 10 | -------------------------------------------------------------------------------- /examples/python/input_checkbox/about.txt: -------------------------------------------------------------------------------- 1 | Checkbox input 2 | -------------------------------------------------------------------------------- /examples/python/input_checkbox/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_checkbox("x", "Checkbox input"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f"x: {input.x()}" 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_checkbox_group/about.txt: -------------------------------------------------------------------------------- 1 | Checkbox group input 2 | -------------------------------------------------------------------------------- /examples/python/input_checkbox_group/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_checkbox_group( 5 | "x", "Checkbox group input", {"a": "Choice A", "b": "Choice B"} 6 | ), 7 | ui.output_code("txt"), 8 | ) 9 | 10 | 11 | def server(input, output, session): 12 | @render.code 13 | def txt(): 14 | return f"x: {input.x()}" 15 | 16 | 17 | app = App(app_ui, server, debug=True) 18 | -------------------------------------------------------------------------------- /examples/python/input_date/about.txt: -------------------------------------------------------------------------------- 1 | Date input 2 | -------------------------------------------------------------------------------- /examples/python/input_date/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_date("x", "Date input"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f"x: {input.x()}" 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_date_range/about.txt: -------------------------------------------------------------------------------- 1 | Date range input 2 | -------------------------------------------------------------------------------- /examples/python/input_date_range/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_date_range("x", "Date range input"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f"x: {input.x()}" 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_numeric/about.txt: -------------------------------------------------------------------------------- 1 | Numeric input 2 | -------------------------------------------------------------------------------- /examples/python/input_numeric/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_numeric("x", "Number input", value=100), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f"x: {input.x()}" 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_password/about.txt: -------------------------------------------------------------------------------- 1 | Password input 2 | -------------------------------------------------------------------------------- /examples/python/input_password/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_password("x", "Password input", placeholder="Enter password"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f'x: "{input.x()}"' 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_radio/about.txt: -------------------------------------------------------------------------------- 1 | Radio buttons input 2 | -------------------------------------------------------------------------------- /examples/python/input_radio/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_radio_buttons( 5 | "x", "Radio buttons input", {"a": "Choice A", "b": "Choice B"} 6 | ), 7 | ui.output_code("txt"), 8 | ) 9 | 10 | 11 | def server(input, output, session): 12 | @render.code 13 | def txt(): 14 | return f'x: "{input.x()}"' 15 | 16 | 17 | app = App(app_ui, server, debug=True) 18 | -------------------------------------------------------------------------------- /examples/python/input_select/about.txt: -------------------------------------------------------------------------------- 1 | Select input 2 | -------------------------------------------------------------------------------- /examples/python/input_select/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_select("x", "Select input", {"a": "Choice A", "b": "Choice B"}), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f'x: "{input.x()}"' 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_slider/about.txt: -------------------------------------------------------------------------------- 1 | Slider input 2 | -------------------------------------------------------------------------------- /examples/python/input_slider/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_slider("x", "Slider input", min=0, max=20, value=10), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f"x: {input.x()}" 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_switch/about.txt: -------------------------------------------------------------------------------- 1 | Switch input 2 | -------------------------------------------------------------------------------- /examples/python/input_switch/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_switch("x", "Switch input"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f"x: {input.x()}" 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_text/about.txt: -------------------------------------------------------------------------------- 1 | Text input 2 | -------------------------------------------------------------------------------- /examples/python/input_text/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_text("x", "Text input", placeholder="Enter text"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f'x: "{input.x()}"' 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_text_area/about.txt: -------------------------------------------------------------------------------- 1 | Text area input 2 | -------------------------------------------------------------------------------- /examples/python/input_text_area/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_text_area("x", "Text area input", placeholder="Enter text"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f'x: "{input.x()}"' 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/input_update/about.txt: -------------------------------------------------------------------------------- 1 | Dynamically updating inputs 2 | -------------------------------------------------------------------------------- /examples/python/input_update/app.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | from shiny import reactive 4 | from shiny.express import input, ui 5 | 6 | ui.h1("Updating inputs") 7 | 8 | ui.markdown( 9 | """ 10 | Each Shiny input has an `update_*` function which can be used to update that input. 11 | Most options can be changed including the value, style, and input label, please see 12 | [the docs](https://shiny.posit.co/py/api/ui.update_sidebar.html) for more examples. 13 | """ 14 | ) 15 | 16 | ui.input_slider("slider", "Slider", 0, 100, 50, width="50%") 17 | ui.input_action_button( 18 | "to_20", "Set slider to 20", class_="btn btn-primary", width="25%" 19 | ) 20 | ui.input_action_button( 21 | "to_60", "Set slider to 60", class_="btn btn-primary", width="25%" 22 | ) 23 | 24 | 25 | @reactive.effect 26 | @reactive.event(input.to_20) 27 | def set_to_20(): 28 | ui.update_slider("slider", value=20) 29 | 30 | 31 | @reactive.effect 32 | @reactive.event(input.to_60) 33 | def set_to_60(): 34 | ui.update_slider("slider", value=60) 35 | -------------------------------------------------------------------------------- /examples/python/insert_ui/about.txt: -------------------------------------------------------------------------------- 1 | Dynamically inserting UI 2 | -------------------------------------------------------------------------------- /examples/python/insert_ui/app.py: -------------------------------------------------------------------------------- 1 | from shiny import reactive 2 | from shiny.express import input, render, ui 3 | 4 | ui.h2("Dynamic UI") 5 | with ui.div(id="main-content"): 6 | ui.input_action_button("btn", "Trigger insert/remove ui") 7 | 8 | @render.ui 9 | def dyn_ui(): 10 | return ui.input_slider( 11 | "n1", "This slider is rendered via @render.ui", 0, 100, 20 12 | ) 13 | 14 | # Another way of adding dynamic content is with ui.insert_ui() and ui.remove_ui(). 15 | # The insertion is imperative, so, compared to @render.ui, more care is needed to 16 | # make sure you don't add multiple copies of the content. 17 | @reactive.effect 18 | def _(): 19 | btn = input.btn() 20 | if btn % 2 == 1: 21 | slider = ui.input_slider( 22 | "n2", "This slider is inserted with ui.insert_ui()", 0, 100, 20 23 | ) 24 | ui.insert_ui( 25 | ui.div({"id": "inserted-slider"}, slider), 26 | selector="#main-content", 27 | where="beforeEnd", 28 | ) 29 | elif btn > 0: 30 | ui.remove_ui("#inserted-slider") 31 | -------------------------------------------------------------------------------- /examples/python/ipyleaflet/about.txt: -------------------------------------------------------------------------------- 1 | Map 2 | Interactive map with ipyleaflet 3 | -------------------------------------------------------------------------------- /examples/python/ipyleaflet/app.py: -------------------------------------------------------------------------------- 1 | from shiny import reactive 2 | from shiny.express import input, ui 3 | from shinywidgets import render_widget 4 | import ipyleaflet as ipyl 5 | 6 | city_centers = { 7 | "London": (51.5074, 0.1278), 8 | "Paris": (48.8566, 2.3522), 9 | "New York": (40.7128, -74.0060), 10 | } 11 | ui.input_select("center", "Center", choices=list(city_centers.keys())) 12 | 13 | 14 | @render_widget 15 | def map(): 16 | return ipyl.Map(zoom=4) 17 | 18 | 19 | @reactive.effect 20 | def _(): 21 | map.widget.center = city_centers[input.center()] 22 | -------------------------------------------------------------------------------- /examples/python/layout_sidebar/about.txt: -------------------------------------------------------------------------------- 1 | Sidebar 2 | -------------------------------------------------------------------------------- /examples/python/layout_sidebar/app.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from shiny import App, render, ui 4 | 5 | # Note that if the window is narrow, then the sidebar will be shown above the 6 | # main content, instead of being on the left. 7 | 8 | app_ui = ui.page_fluid( 9 | ui.layout_sidebar( 10 | ui.panel_sidebar(ui.input_slider("n", "N", 0, 100, 20)), 11 | ui.panel_main(ui.output_plot("plot")), 12 | ), 13 | ) 14 | 15 | 16 | def server(input, output, session): 17 | @render.plot(alt="A histogram") 18 | def plot() -> object: 19 | np.random.seed(19680801) 20 | x = 100 + 15 * np.random.randn(437) 21 | 22 | fig, ax = plt.subplots() 23 | ax.hist(x, input.n(), density=True) 24 | return fig 25 | 26 | 27 | app = App(app_ui, server) 28 | -------------------------------------------------------------------------------- /examples/python/layout_two_column/about.txt: -------------------------------------------------------------------------------- 1 | Two columns 2 | -------------------------------------------------------------------------------- /examples/python/layout_two_column/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.tags.style( 5 | """ 6 | .app-col { 7 | border: 1px solid black; 8 | border-radius: 5px; 9 | background-color: #eee; 10 | padding: 8px; 11 | margin-top: 5px; 12 | margin-bottom: 5px; 13 | } 14 | """ 15 | ), 16 | ui.h2({"style": "text-align: center;"}, "App Title"), 17 | ui.row( 18 | ui.column( 19 | 12, 20 | ui.div( 21 | {"class": "app-col"}, 22 | ui.p( 23 | """ 24 | This is a column that spans the entire width of the page. 25 | """, 26 | ), 27 | ui.p( 28 | """ 29 | Here's some more text in another paragraph. 30 | """, 31 | ), 32 | ), 33 | ) 34 | ), 35 | ui.row( 36 | ui.column( 37 | 6, 38 | ui.div( 39 | {"class": "app-col"}, 40 | """ 41 | Here's some text in a column. Note that if the page is very 42 | narrow, the two columns will be be stacked on top of each other 43 | instead of being side-by-side. 44 | """, 45 | ), 46 | ), 47 | ui.column( 48 | 6, 49 | ui.div( 50 | {"class": "app-col"}, 51 | """ 52 | Here's some text in another column. 53 | """, 54 | ), 55 | ), 56 | ), 57 | ) 58 | 59 | 60 | def server(input, output, session): 61 | pass 62 | 63 | 64 | app = App(app_ui, server) 65 | -------------------------------------------------------------------------------- /examples/python/modules/about.txt: -------------------------------------------------------------------------------- 1 | Modules 2 | Reusable Shiny components 3 | -------------------------------------------------------------------------------- /examples/python/modules/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, module, reactive, render, ui 2 | 3 | 4 | # ============================================================ 5 | # Counter module 6 | # ============================================================ 7 | @module.ui 8 | def counter_ui(label: str = "Increment counter") -> ui.TagChild: 9 | return ui.card( 10 | ui.h2("This is " + label), 11 | ui.input_action_button(id="button", label=label), 12 | ui.output_code(id="out"), 13 | ) 14 | 15 | 16 | @module.server 17 | def counter_server(input, output, session, starting_value: int = 0): 18 | count: reactive.value[int] = reactive.value(starting_value) 19 | 20 | @reactive.effect 21 | @reactive.event(input.button) 22 | def _(): 23 | count.set(count() + 1) 24 | 25 | @render.code 26 | def out() -> str: 27 | return f"Click count is {count()}" 28 | 29 | 30 | # ============================================================================= 31 | # App that uses module 32 | # ============================================================================= 33 | app_ui = ui.page_fluid( 34 | counter_ui("counter1", "Counter 1"), 35 | counter_ui("counter2", "Counter 2"), 36 | ) 37 | 38 | 39 | def server(input, output, session): 40 | counter_server("counter1") 41 | counter_server("counter2") 42 | 43 | 44 | app = App(app_ui, server) 45 | -------------------------------------------------------------------------------- /examples/python/multiple_source_files/about.txt: -------------------------------------------------------------------------------- 1 | Multiple source files 2 | -------------------------------------------------------------------------------- /examples/python/multiple_source_files/app.py: -------------------------------------------------------------------------------- 1 | from shiny.express import input, render, ui 2 | from utils import square 3 | 4 | ui.input_slider("n", "N", 0, 100, 20) 5 | 6 | 7 | @render.code 8 | def txt(): 9 | val = square(input.n()) 10 | return f"{input.n()} squared is {val}" 11 | -------------------------------------------------------------------------------- /examples/python/multiple_source_files/utils.py: -------------------------------------------------------------------------------- 1 | def square(n): 2 | return n * n 3 | -------------------------------------------------------------------------------- /examples/python/orbit/about.txt: -------------------------------------------------------------------------------- 1 | Orbit simulation 2 | Earth-Moon gravity 3 | -------------------------------------------------------------------------------- /examples/python/orbit/body.py: -------------------------------------------------------------------------------- 1 | """Body Shiny module 2 | 3 | A Shiny module that represents a body (i.e. planet/moon) in the simulation. This allows 4 | us to have multiple bodies in the simulation, each sharing similar UI and server logic, 5 | without having to repeat the code. 6 | 7 | Learn more about Shiny modules at: https://shiny.posit.co/py/docs/workflow-modules.html 8 | """ 9 | 10 | import astropy.units as u 11 | import numpy as np 12 | from shiny import module, reactive, ui 13 | from simulation import Body, spherical_to_cartesian 14 | 15 | 16 | @module.ui 17 | def body_ui(enable, mass, speed, theta, phi): 18 | return ui.TagList( 19 | ui.input_checkbox("enable", "Enable", enable), 20 | ui.panel_conditional( 21 | "input.enable", 22 | ui.input_numeric( 23 | "mass", 24 | "Mass (10^22 kg)", 25 | mass, 26 | ), 27 | ui.input_slider( 28 | "speed", 29 | "Speed (km/s)", 30 | 0, 31 | 1, 32 | value=speed, 33 | step=0.001, 34 | ), 35 | ui.input_slider("theta", "Angle (𝜃)", 0, 360, value=theta), 36 | ui.input_slider("phi", "𝜙", 0, 180, value=phi), 37 | ), 38 | ) 39 | 40 | 41 | @module.server 42 | def body_server(input, output, session, label, start_vec): 43 | @reactive.calc 44 | def body_result(): 45 | if not input.enable(): 46 | return None 47 | 48 | v = spherical_to_cartesian(input.theta(), input.phi(), input.speed()) 49 | 50 | return Body( 51 | mass=input.mass() * 1e22 * u.kg, 52 | x_vec=np.array(start_vec) * u.km, 53 | v_vec=np.array(v) * u.km / u.s, 54 | name=label, 55 | ) 56 | 57 | return body_result 58 | -------------------------------------------------------------------------------- /examples/python/orbit/requirements.txt: -------------------------------------------------------------------------------- 1 | astropy 2 | -------------------------------------------------------------------------------- /examples/python/orbit/www/coords.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posit-dev/shinylive/8acb41b0d4090308551e2f30dccb68fa6c9071a9/examples/python/orbit/www/coords.png -------------------------------------------------------------------------------- /examples/python/output_code/about.txt: -------------------------------------------------------------------------------- 1 | Code output 2 | -------------------------------------------------------------------------------- /examples/python/output_code/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_text("x", "Text input", placeholder="Enter text"), 5 | ui.output_code("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.code 11 | def txt(): 12 | return f'x: "{input.x()}"' 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/output_data_frame_grid/about.txt: -------------------------------------------------------------------------------- 1 | Data Frame/Grid 2 | -------------------------------------------------------------------------------- /examples/python/output_data_frame_grid/app.py: -------------------------------------------------------------------------------- 1 | import palmerpenguins 2 | from shiny import App, render, ui 3 | 4 | penguins = palmerpenguins.load_penguins() 5 | # Slim down the data frame to a few representative columns 6 | penguins = penguins.loc[ 7 | penguins["body_mass_g"].notnull(), 8 | ["species", "island", "body_mass_g", "year"], 9 | ] 10 | 11 | app_ui = ui.page_fluid( 12 | ui.input_select( 13 | "selection_mode", 14 | "Selection mode", 15 | {"none": "(None)", "single": "Single", "multiple": "Multiple"}, 16 | selected="multiple", 17 | ), 18 | ui.input_switch("gridstyle", "Grid", True), 19 | ui.input_switch("fullwidth", "Take full width", True), 20 | ui.input_switch("fixedheight", "Fixed height", True), 21 | ui.input_switch("filters", "Filters", True), 22 | ui.output_data_frame("grid"), 23 | ui.panel_fixed( 24 | ui.output_code("detail"), 25 | right="10px", 26 | bottom="10px", 27 | ), 28 | class_="p-3", 29 | ) 30 | 31 | 32 | def server(input, output, session): 33 | @render.data_frame 34 | def grid(): 35 | height = 350 if input.fixedheight() else None 36 | width = "100%" if input.fullwidth() else "fit-content" 37 | if input.gridstyle(): 38 | return render.DataGrid( 39 | penguins, 40 | row_selection_mode=input.selection_mode(), 41 | height=height, 42 | width=width, 43 | filters=input.filters(), 44 | ) 45 | else: 46 | return render.DataTable( 47 | penguins, 48 | row_selection_mode=input.selection_mode(), 49 | height=height, 50 | width=width, 51 | filters=input.filters(), 52 | ) 53 | 54 | @render.code 55 | def detail(): 56 | if ( 57 | input.grid_selected_rows() is not None 58 | and len(input.grid_selected_rows()) > 0 59 | ): 60 | # "split", "records", "index", "columns", "values", "table" 61 | return penguins.iloc[list(input.grid_selected_rows())] 62 | 63 | 64 | app = App(app_ui, server) 65 | -------------------------------------------------------------------------------- /examples/python/output_plot/about.txt: -------------------------------------------------------------------------------- 1 | Plot output 2 | -------------------------------------------------------------------------------- /examples/python/output_plot/app.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from shiny import App, render, ui 4 | 5 | app_ui = ui.page_fluid( 6 | ui.input_slider("n", "Number of bins", 0, 100, 20), 7 | ui.output_plot("plot"), 8 | ) 9 | 10 | 11 | def server(input, output, session): 12 | @render.plot(alt="A histogram") 13 | def plot(): 14 | np.random.seed(19680801) 15 | x = 100 + 15 * np.random.randn(437) 16 | 17 | fig, ax = plt.subplots() 18 | ax.hist(x, input.n(), density=True) 19 | return fig 20 | 21 | 22 | app = App(app_ui, server, debug=True) 23 | -------------------------------------------------------------------------------- /examples/python/output_table/about.txt: -------------------------------------------------------------------------------- 1 | Table output 2 | -------------------------------------------------------------------------------- /examples/python/output_table/app.py: -------------------------------------------------------------------------------- 1 | import palmerpenguins 2 | from shiny import App, render, ui 3 | 4 | penguins = palmerpenguins.load_penguins() 5 | 6 | numeric_cols = [ 7 | "bill_length_mm", 8 | "bill_depth_mm", 9 | "flipper_length_mm", 10 | "body_mass_g", 11 | ] 12 | 13 | app_ui = ui.page_fluid( 14 | ui.input_checkbox("highlight", "Highlight min/max values"), 15 | ui.output_table("result"), 16 | # Legend 17 | ui.panel_conditional( 18 | "input.highlight", 19 | ui.panel_absolute( 20 | ui.span("minimum", style="background-color: silver;"), 21 | ui.span("maximum", style="background-color: yellow;"), 22 | top="6px", 23 | right="6px", 24 | class_="p-1 bg-light border", 25 | ), 26 | ), 27 | class_="p-3", 28 | ) 29 | 30 | 31 | def server(input, output, session): 32 | @render.table 33 | def result(): 34 | if not input.highlight(): 35 | # If we're not highlighting values, we can simply 36 | # return the pandas data frame as-is; @render.table 37 | # will call .to_html() on it. 38 | return penguins 39 | else: 40 | # We need to use the pandas Styler API. The default 41 | # formatting options for Styler are not the same as 42 | # DataFrame.to_html(), so we set a few options to 43 | # make them match. 44 | return ( 45 | penguins.style.set_table_attributes( 46 | 'class="dataframe shiny-table table w-auto"' 47 | ) 48 | .hide(axis="index") 49 | .format( 50 | { 51 | "bill_length_mm": "{0:0.1f}", 52 | "bill_depth_mm": "{0:0.1f}", 53 | "flipper_length_mm": "{0:0.0f}", 54 | "body_mass_g": "{0:0.0f}", 55 | } 56 | ) 57 | .set_table_styles( 58 | [ 59 | dict(selector="th", props=[("text-align", "right")]), 60 | dict( 61 | selector="tr>td", 62 | props=[ 63 | ("padding-top", "0.1rem"), 64 | ("padding-bottom", "0.1rem"), 65 | ], 66 | ), 67 | ] 68 | ) 69 | .highlight_min(color="silver", subset=numeric_cols) 70 | .highlight_max(color="yellow", subset=numeric_cols) 71 | ) 72 | 73 | 74 | app = App(app_ui, server) 75 | -------------------------------------------------------------------------------- /examples/python/output_text/about.txt: -------------------------------------------------------------------------------- 1 | Text output 2 | -------------------------------------------------------------------------------- /examples/python/output_text/app.py: -------------------------------------------------------------------------------- 1 | from shiny import App, render, ui 2 | 3 | app_ui = ui.page_fluid( 4 | ui.input_text("x", "Text input", placeholder="Enter text"), 5 | ui.output_text("txt"), 6 | ) 7 | 8 | 9 | def server(input, output, session): 10 | @render.text 11 | def txt(): 12 | return f'x: "{input.x()}"' 13 | 14 | 15 | app = App(app_ui, server, debug=True) 16 | -------------------------------------------------------------------------------- /examples/python/output_ui/about.txt: -------------------------------------------------------------------------------- 1 | UI output 2 | -------------------------------------------------------------------------------- /examples/python/output_ui/app.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from shiny.express import expressify, input, output, render, ui 4 | 5 | ui.input_slider("card_n", "Number of cards", value=3, min=1, max=5) 6 | 7 | 8 | @expressify 9 | def custom_card(id): 10 | id = id + 1 11 | with ui.card(): 12 | f"Card {id}" 13 | 14 | # Specifying the ID like this lets us include a renderer in the iterator 15 | # without causing ID conflicts. 16 | @output(id=f"hist_{id }") 17 | @render.plot(alt="A histogram") 18 | def histogram(): 19 | np.random.seed(19680801) 20 | x = 100 + 15 * np.random.randn(437) 21 | plt.hist(x, 20, density=True) 22 | 23 | 24 | @render.express 25 | def cards(): 26 | with ui.layout_columns(): 27 | for i in range(input.card_n()): 28 | custom_card(i) 29 | -------------------------------------------------------------------------------- /examples/python/plot_interact_basic/about.txt: -------------------------------------------------------------------------------- 1 | Basic plot interaction 2 | -------------------------------------------------------------------------------- /examples/python/plot_interact_basic/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | import matplotlib.pyplot as plt 5 | import pandas as pd 6 | from shiny import App, render, ui 7 | 8 | mtcars = pd.read_csv(Path(__file__).parent / "mtcars.csv") 9 | mtcars.drop(["disp", "hp", "drat", "qsec", "vs", "gear", "carb"], axis=1, inplace=True) 10 | 11 | 12 | app_ui = ui.page_fluid( 13 | ui.head_content( 14 | ui.tags.style( 15 | """ 16 | /* Smaller font for preformatted text */ 17 | pre, table.table { 18 | font-size: smaller; 19 | } 20 | 21 | pre, table.table { 22 | font-size: smaller; 23 | } 24 | """ 25 | ) 26 | ), 27 | ui.row( 28 | ui.column( 29 | 4, 30 | ui.panel_well( 31 | ui.input_radio_buttons( 32 | "plot_type", "Plot type", ["matplotlib", "plotnine"] 33 | ) 34 | ), 35 | ), 36 | ui.column( 37 | 8, 38 | ui.output_plot("plot1", click=True, dblclick=True, hover=True, brush=True), 39 | ), 40 | ), 41 | ui.row( 42 | ui.column(3, ui.output_code("click_info")), 43 | ui.column(3, ui.output_code("dblclick_info")), 44 | ui.column(3, ui.output_code("hover_info")), 45 | ui.column(3, ui.output_code("brush_info")), 46 | ), 47 | ) 48 | 49 | 50 | def server(input, output, session): 51 | @render.plot(alt="A scatterplot") 52 | def plot1(): 53 | if input.plot_type() == "matplotlib": 54 | fig, ax = plt.subplots() 55 | plt.title("Good old mtcars") 56 | ax.scatter(mtcars["wt"], mtcars["mpg"]) 57 | return fig 58 | 59 | elif input.plot_type() == "plotnine": 60 | from plotnine import aes, geom_point, ggplot, ggtitle 61 | 62 | p = ( 63 | ggplot(mtcars, aes("wt", "mpg")) 64 | + geom_point() 65 | + ggtitle("Good old mtcars") 66 | ) 67 | 68 | return p 69 | 70 | @render.code() 71 | def click_info(): 72 | return "click:\n" + json.dumps(input.plot1_click(), indent=2) 73 | 74 | @render.code() 75 | def dblclick_info(): 76 | return "dblclick:\n" + json.dumps(input.plot1_dblclick(), indent=2) 77 | 78 | @render.code() 79 | def hover_info(): 80 | return "hover:\n" + json.dumps(input.plot1_hover(), indent=2) 81 | 82 | @render.code() 83 | def brush_info(): 84 | return "brush:\n" + json.dumps(input.plot1_brush(), indent=2) 85 | 86 | 87 | app = App(app_ui, server, debug=True) 88 | -------------------------------------------------------------------------------- /examples/python/plot_interact_basic/mtcars.csv: -------------------------------------------------------------------------------- 1 | "model","mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" 2 | "Mazda RX4",21,6,160,110,3.9,2.62,16.46,0,1,4,4 3 | "Mazda RX4 Wag",21,6,160,110,3.9,2.875,17.02,0,1,4,4 4 | "Datsun 710",22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 5 | "Hornet 4 Drive",21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 6 | "Hornet Sportabout",18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 7 | "Valiant",18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 8 | "Duster 360",14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 9 | "Merc 240D",24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 10 | "Merc 230",22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 11 | "Merc 280",19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 12 | "Merc 280C",17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 13 | "Merc 450SE",16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 14 | "Merc 450SL",17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15 | "Merc 450SLC",15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 16 | "Cadillac Fleetwood",10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 17 | "Lincoln Continental",10.4,8,460,215,3,5.424,17.82,0,0,3,4 18 | "Chrysler Imperial",14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 19 | "Fiat 128",32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 20 | "Honda Civic",30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 21 | "Toyota Corolla",33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 22 | "Toyota Corona",21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 23 | "Dodge Challenger",15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 24 | "AMC Javelin",15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 25 | "Camaro Z28",13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 26 | "Pontiac Firebird",19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27 | "Fiat X1-9",27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 28 | "Porsche 914-2",26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 29 | "Lotus Europa",30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 30 | "Ford Pantera L",15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 31 | "Ferrari Dino",19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 32 | "Maserati Bora",15,8,301,335,3.54,3.57,14.6,0,1,5,8 33 | "Volvo 142E",21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 34 | -------------------------------------------------------------------------------- /examples/python/plot_interact_exclude/about.txt: -------------------------------------------------------------------------------- 1 | Interactively excluding data 2 | -------------------------------------------------------------------------------- /examples/python/plot_interact_exclude/app.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import numpy as np 4 | import pandas as pd 5 | import statsmodels.api as sm 6 | from plotnine import aes, geom_point, geom_smooth, ggplot 7 | from shiny import App, reactive, render, ui 8 | from shiny.plotutils import brushed_points, near_points 9 | 10 | mtcars = pd.read_csv(Path(__file__).parent / "mtcars.csv") 11 | mtcars.drop(["disp", "hp", "drat", "qsec", "vs", "gear", "carb"], axis=1, inplace=True) 12 | 13 | 14 | app_ui = ui.page_fluid( 15 | ui.head_content( 16 | ui.tags.style( 17 | """ 18 | pre, table.table { 19 | font-size: smaller; 20 | } 21 | """ 22 | ) 23 | ), 24 | ui.row( 25 | ui.column(2), 26 | ui.column( 27 | 8, 28 | ui.output_plot("plot1", click=True, brush=True), 29 | ui.div( 30 | {"style": "text-align: center"}, 31 | ui.input_action_button("exclude_toggle", "Toggle brushed points"), 32 | ui.input_action_button("exclude_reset", "Reset"), 33 | ), 34 | ), 35 | ), 36 | ui.row( 37 | ui.column(12, {"style": "margin-top: 15px;"}, ui.output_code("model")), 38 | ), 39 | ) 40 | 41 | 42 | def server(input, output, session): 43 | keep_rows = reactive.value([True] * len(mtcars)) 44 | 45 | @reactive.calc 46 | def data_with_keep(): 47 | df = mtcars.copy() 48 | df["keep"] = keep_rows() 49 | return df 50 | 51 | @reactive.effect 52 | @reactive.event(input.plot1_click) 53 | def _(): 54 | res = near_points(mtcars, input.plot1_click(), all_rows=True, max_points=1) 55 | keep_rows.set(list(np.logical_xor(keep_rows(), res.selected_))) 56 | 57 | @reactive.effect 58 | @reactive.event(input.exclude_toggle) 59 | def _(): 60 | res = brushed_points(mtcars, input.plot1_brush(), all_rows=True) 61 | keep_rows.set(list(np.logical_xor(keep_rows(), res.selected_))) 62 | 63 | @reactive.effect 64 | @reactive.event(input.exclude_reset) 65 | def _(): 66 | keep_rows.set([True] * len(mtcars)) 67 | 68 | @render.plot() 69 | def plot1(): 70 | df = data_with_keep() 71 | df_keep = df[df["keep"]] 72 | df_exclude = df[~df["keep"]] 73 | 74 | return ( 75 | ggplot(df_keep, aes("wt", "mpg")) 76 | + geom_point() 77 | + geom_point(data=df_exclude, color="#666", fill="white") 78 | + geom_smooth(method="lm", fullrange=True) 79 | ) 80 | 81 | @render.code() 82 | def model(): 83 | df = data_with_keep() 84 | df_keep = df[df["keep"]] 85 | mod = sm.OLS(df_keep["wt"], df_keep["mpg"]) 86 | res = mod.fit() 87 | return res.summary() 88 | 89 | 90 | app = App(app_ui, server) 91 | -------------------------------------------------------------------------------- /examples/python/plot_interact_exclude/mtcars.csv: -------------------------------------------------------------------------------- 1 | "model","mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" 2 | "Mazda RX4",21,6,160,110,3.9,2.62,16.46,0,1,4,4 3 | "Mazda RX4 Wag",21,6,160,110,3.9,2.875,17.02,0,1,4,4 4 | "Datsun 710",22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 5 | "Hornet 4 Drive",21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 6 | "Hornet Sportabout",18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 7 | "Valiant",18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 8 | "Duster 360",14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 9 | "Merc 240D",24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 10 | "Merc 230",22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 11 | "Merc 280",19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 12 | "Merc 280C",17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 13 | "Merc 450SE",16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 14 | "Merc 450SL",17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15 | "Merc 450SLC",15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 16 | "Cadillac Fleetwood",10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 17 | "Lincoln Continental",10.4,8,460,215,3,5.424,17.82,0,0,3,4 18 | "Chrysler Imperial",14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 19 | "Fiat 128",32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 20 | "Honda Civic",30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 21 | "Toyota Corolla",33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 22 | "Toyota Corona",21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 23 | "Dodge Challenger",15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 24 | "AMC Javelin",15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 25 | "Camaro Z28",13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 26 | "Pontiac Firebird",19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27 | "Fiat X1-9",27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 28 | "Porsche 914-2",26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 29 | "Lotus Europa",30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 30 | "Ford Pantera L",15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 31 | "Ferrari Dino",19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 32 | "Maserati Bora",15,8,301,335,3.54,3.57,14.6,0,1,5,8 33 | "Volvo 142E",21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 34 | -------------------------------------------------------------------------------- /examples/python/plot_interact_select/about.txt: -------------------------------------------------------------------------------- 1 | Selecting data 2 | -------------------------------------------------------------------------------- /examples/python/plot_interact_select/mtcars.csv: -------------------------------------------------------------------------------- 1 | "model","mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" 2 | "Mazda RX4",21,6,160,110,3.9,2.62,16.46,0,1,4,4 3 | "Mazda RX4 Wag",21,6,160,110,3.9,2.875,17.02,0,1,4,4 4 | "Datsun 710",22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 5 | "Hornet 4 Drive",21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 6 | "Hornet Sportabout",18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 7 | "Valiant",18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 8 | "Duster 360",14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 9 | "Merc 240D",24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 10 | "Merc 230",22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 11 | "Merc 280",19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 12 | "Merc 280C",17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 13 | "Merc 450SE",16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 14 | "Merc 450SL",17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15 | "Merc 450SLC",15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 16 | "Cadillac Fleetwood",10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 17 | "Lincoln Continental",10.4,8,460,215,3,5.424,17.82,0,0,3,4 18 | "Chrysler Imperial",14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 19 | "Fiat 128",32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 20 | "Honda Civic",30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 21 | "Toyota Corolla",33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 22 | "Toyota Corona",21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 23 | "Dodge Challenger",15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 24 | "AMC Javelin",15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 25 | "Camaro Z28",13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 26 | "Pontiac Firebird",19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27 | "Fiat X1-9",27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 28 | "Porsche 914-2",26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 29 | "Lotus Europa",30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 30 | "Ford Pantera L",15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 31 | "Ferrari Dino",19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 32 | "Maserati Bora",15,8,301,335,3.54,3.57,14.6,0,1,5,8 33 | "Volvo 142E",21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 34 | -------------------------------------------------------------------------------- /examples/python/plotly/about.txt: -------------------------------------------------------------------------------- 1 | Plotly 2 | Interactive plot with plotly 3 | -------------------------------------------------------------------------------- /examples/python/plotly/app.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | from shiny.express import input, ui 3 | from shinywidgets import render_plotly 4 | 5 | ui.page_opts(title="Filling layout", fillable=True) 6 | with ui.layout_columns(): 7 | 8 | @render_plotly 9 | def plot1(): 10 | return px.histogram(px.data.tips(), y="tip") 11 | 12 | @render_plotly 13 | def plot2(): 14 | return px.histogram(px.data.tips(), y="total_bill") 15 | -------------------------------------------------------------------------------- /examples/python/plotly/requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | plotly 3 | -------------------------------------------------------------------------------- /examples/python/reactive_calc/about.txt: -------------------------------------------------------------------------------- 1 | Reactive calc 2 | -------------------------------------------------------------------------------- /examples/python/reactive_calc/app.py: -------------------------------------------------------------------------------- 1 | # A reactive Calc is used for its return value. It intelligently caches its value, and 2 | # only re-runs after it has been invalidated -- that is, when upstream reactive inputs 3 | # change. 4 | 5 | from shiny import reactive 6 | from shiny.express import input, render, ui 7 | 8 | ui.input_slider("x", "Choose a number", 1, 100, 50) 9 | 10 | 11 | @reactive.calc 12 | def x_times_2(): 13 | val = input.x() * 2 14 | print(f"Running x_times_2(). Result is {val}.") 15 | return val 16 | 17 | 18 | @render.code 19 | def txt1(): 20 | return f'x times 2 is: "{x_times_2()}"' 21 | 22 | 23 | @render.code 24 | def txt2(): 25 | return f'x times 2 is: "{x_times_2()}"' 26 | -------------------------------------------------------------------------------- /examples/python/reactive_effect/about.txt: -------------------------------------------------------------------------------- 1 | Reactive effect 2 | -------------------------------------------------------------------------------- /examples/python/reactive_effect/app.py: -------------------------------------------------------------------------------- 1 | # A reactive Effect is run for its side effects, not for its return value. These 2 | # side effects can include printing messages to the console, writing files to 3 | # disk, or sending messages to a server. 4 | 5 | from shiny import reactive 6 | from shiny.express import input, ui 7 | 8 | ui.input_text("x", "Text input", placeholder="Enter text") 9 | 10 | 11 | @reactive.effect 12 | def _(): 13 | print(f"x has changed to {input.x()}") 14 | -------------------------------------------------------------------------------- /examples/python/reactive_event/about.txt: -------------------------------------------------------------------------------- 1 | Event decorator 2 | -------------------------------------------------------------------------------- /examples/python/reactive_event/app.py: -------------------------------------------------------------------------------- 1 | # A reactive Effect is run for its side effects, not for its return value. These 2 | # side effects can include printing messages to the console, writing files to 3 | # disk, or sending messages to a server. 4 | 5 | from shiny import reactive 6 | from shiny.express import input, render, ui 7 | 8 | ui.input_slider("n", "N", 0, 20, 10) 9 | ui.input_action_button("btn", "Click me") 10 | ui.tags.br() 11 | "The value of the slider when the button was last clicked:" 12 | 13 | 14 | @reactive.effect 15 | @reactive.event(input.btn) 16 | def _(): 17 | print("You clicked the button!") 18 | # You can do other things here, like write data to disk. 19 | 20 | 21 | @render.code 22 | @reactive.event(input.btn) 23 | def txt(): 24 | return f"Last value: {input.n()}" 25 | -------------------------------------------------------------------------------- /examples/python/reactive_value/about.txt: -------------------------------------------------------------------------------- 1 | Reactive value 2 | -------------------------------------------------------------------------------- /examples/python/reactive_value/app.py: -------------------------------------------------------------------------------- 1 | # When a reactive Value's value changes, it will invalidate (and trigger re-execution) 2 | # of any reactive objects (Calcs, Effects, and outputs) that depend on it. Typically, a 3 | # reactive Value will be set with a reactive Effect. The Effect, in turn, may be driven 4 | # by a user input (like a button), or something else, like a timer. 5 | # 6 | # Reactive Values are often used for tracking or accumulating state over time. 7 | # 8 | # In this example, a button press triggers an Effect which adds the current timestamp to 9 | # a reactive Value containing an array of timestamps. Importantly, the Effect sets the 10 | # reactive Value's value, which causes all downstream reactive objects to be 11 | # invalidated. 12 | # 13 | # There is also an output which reads the reactive Value and returns a string. When the 14 | # reactive Value changes, it invalidates this output, causing it to re-execute and 15 | # return a new string. 16 | 17 | import textwrap 18 | from datetime import datetime 19 | 20 | from shiny import reactive 21 | from shiny.express import input, render, ui 22 | 23 | ui.h3("Press the button:") 24 | ui.input_action_button("btn", "Time") 25 | ui.h3("Time between button presses:") 26 | 27 | 28 | # A reactive.value with an array tracking timestamps of all button presses. 29 | all_times = reactive.value([datetime.now().timestamp()]) 30 | 31 | 32 | # This Effect is triggered by pressing the button. It makes a copy of all_times(), 33 | # because we don't want to modify the original, then appends the new timestamp, 34 | # then sets all_times() to the new, longer array. 35 | @reactive.effect 36 | @reactive.event(input.btn) 37 | def _(): 38 | x = all_times().copy() 39 | x.append(datetime.now().timestamp()) 40 | all_times.set(x) 41 | 42 | 43 | # This text output is invalidated when all_times() changes. It calculates the 44 | # differences between each timestamp and returns the array of differences as a 45 | # string. 46 | 47 | 48 | @render.code 49 | def txt(): 50 | x = all_times() 51 | x = [round(j - i, 2) for i, j in zip(x[:-1], x[1:])] 52 | return "\n".join(textwrap.wrap(str(x), width=45)) 53 | -------------------------------------------------------------------------------- /examples/python/read_local_csv_file/about.txt: -------------------------------------------------------------------------------- 1 | Read local CSV 2 | Load a CSV file and display as an HTML table 3 | -------------------------------------------------------------------------------- /examples/python/read_local_csv_file/app.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pandas 4 | 5 | from shiny import reactive 6 | from shiny.express import render, ui 7 | 8 | 9 | @reactive.calc 10 | def dat(): 11 | infile = Path(__file__).parent / "mtcars.csv" 12 | return pandas.read_csv(infile) 13 | 14 | 15 | with ui.navset_card_underline(): 16 | 17 | with ui.nav_panel("Data frame"): 18 | @render.data_frame 19 | def frame(): 20 | # Give dat() to render.DataGrid to customize the grid 21 | return dat() 22 | 23 | with ui.nav_panel("Table"): 24 | @render.table 25 | def table(): 26 | return dat() 27 | -------------------------------------------------------------------------------- /examples/python/read_local_csv_file/mtcars.csv: -------------------------------------------------------------------------------- 1 | "mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" 2 | 21,6,160,110,3.9,2.62,16.46,0,1,4,4 3 | 21,6,160,110,3.9,2.875,17.02,0,1,4,4 4 | 22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 5 | 21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 6 | 18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 7 | 18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 8 | 14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 9 | 24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 10 | 22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 11 | 19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 12 | 17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 13 | 16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 14 | 17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15 | 15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 16 | 10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 17 | 10.4,8,460,215,3,5.424,17.82,0,0,3,4 18 | 14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 19 | 32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 20 | 30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 21 | 33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 22 | 21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 23 | 15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 24 | 15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 25 | 13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 26 | 19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27 | 27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 28 | 26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 29 | 30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 30 | 15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 31 | 19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 32 | 15,8,301,335,3.54,3.57,14.6,0,1,5,8 33 | 21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 34 | -------------------------------------------------------------------------------- /examples/python/regularization/about.txt: -------------------------------------------------------------------------------- 1 | Regularization 2 | Regularization strength and coefficient estimates 3 | -------------------------------------------------------------------------------- /examples/python/regularization/compare.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet 4 | 5 | # define functions 6 | def sim_data(n=1000): 7 | # Real Variables 8 | A = np.random.normal(0, 1, n) 9 | E = np.random.normal(0, 1, n) 10 | I = np.random.normal(0, 1, n) 11 | O = np.random.normal(0, 1, n) 12 | U = np.random.normal(0, 1, n) 13 | Y = np.random.normal(0, 1, n) 14 | W = np.random.normal(0, 1, n) 15 | 16 | # Unrelated Variables 17 | B = np.random.normal(0, 1, n) 18 | C = np.random.normal(0, 1, n) 19 | D = np.random.normal(0, 1, n) 20 | G = np.random.normal(0, 1, n) 21 | H = np.random.normal(0, 1, n) 22 | J = np.random.normal(0, 1, n) 23 | K = np.random.normal(0, 1, n) 24 | 25 | # coefficients 26 | a = 12.34 27 | e = 8.23 28 | i = 7.83 29 | o = 5.12 30 | u = 3.48 31 | y = 2.97 32 | w = 1.38 33 | 34 | # Outcome 35 | X = ( 36 | 100 37 | + A * a 38 | + E * e 39 | + I * i 40 | + O * o 41 | + U * u 42 | + Y * y 43 | + W * w 44 | + np.random.normal(0, 15, n) 45 | ) 46 | 47 | X = (X - np.mean(X)) / np.std(X) # z-score X 48 | # the other variables already have a mean of 0 and sd of 1 49 | 50 | # Data Frame 51 | df = pd.DataFrame( 52 | { 53 | "A": A, 54 | "E": E, 55 | "I": I, 56 | "O": O, 57 | "U": U, 58 | "B": B, 59 | "C": C, 60 | "D": D, 61 | "G": G, 62 | "H": H, 63 | "J": J, 64 | "K": K, 65 | "Y": Y, 66 | "W": W, 67 | "X": X, 68 | } 69 | ) 70 | return df 71 | 72 | 73 | def compare(df, alpha=1): 74 | feat = ["A", "B", "C", "D", "E", "G", "H", "I", "O", "U", "J", "K", "Y", "W"] 75 | 76 | # linear 77 | lr = LinearRegression() 78 | lr.fit(df[feat], df["X"]) 79 | lr_co = lr.coef_ 80 | 81 | # lasso 82 | lasso = Lasso(alpha=alpha, fit_intercept=True, tol=0.0000001, max_iter=100000) 83 | lasso.fit(df[feat], df["X"]) 84 | lasso_co = lasso.coef_ 85 | 86 | # ridge 87 | ridge = Ridge( 88 | alpha=df.shape[0] * alpha, fit_intercept=True, tol=0.0000001, max_iter=100000 89 | ) 90 | ridge.fit(df[feat], df["X"]) 91 | ridge_co = ridge.coef_ 92 | 93 | conames = feat * 3 94 | coefs = np.concatenate([lr_co, lasso_co, ridge_co]) 95 | 96 | model = np.repeat( 97 | np.array(["Linear", "LASSO", "Ridge"]), 98 | [len(feat), len(feat), len(feat)], 99 | axis=0, 100 | ) 101 | 102 | df = pd.DataFrame({"conames": conames, "coefs": coefs, "model": model}) 103 | 104 | return df 105 | -------------------------------------------------------------------------------- /examples/python/shinyswatch/about.txt: -------------------------------------------------------------------------------- 1 | Shinyswatch 2 | Visual themes 3 | -------------------------------------------------------------------------------- /examples/python/shinyswatch/app.py: -------------------------------------------------------------------------------- 1 | # The shinyswatch package provides themes from https://bootswatch.com/ 2 | 3 | import shinyswatch 4 | from shiny import App, render, ui 5 | 6 | app_ui = ui.page_navbar( 7 | # Available themes: 8 | # cerulean, cosmo, cyborg, darkly, flatly, journal, litera, lumen, lux, 9 | # materia, minty, morph, pulse, quartz, sandstone, simplex, sketchy, slate, 10 | # solar, spacelab, superhero, united, vapor, yeti, zephyr 11 | shinyswatch.theme.superhero(), 12 | ui.nav( 13 | "Navbar 1", 14 | ui.layout_sidebar( 15 | ui.panel_sidebar( 16 | ui.input_file("file", "File input:"), 17 | ui.input_text("txt", "Text input:", "general"), 18 | ui.input_slider("slider", "Slider input:", 1, 100, 30), 19 | ui.tags.h5("Default actionButton:"), 20 | ui.input_action_button("action", "Search"), 21 | ui.tags.h5("actionButton with CSS class:"), 22 | ui.input_action_button( 23 | "action2", "Action button", class_="btn-primary" 24 | ), 25 | ), 26 | ui.panel_main( 27 | ui.navset_tab( 28 | ui.nav( 29 | "Tab 1", 30 | ui.tags.h4("Table"), 31 | ui.output_table("table"), 32 | ui.tags.h4("Verbatim text output"), 33 | ui.output_code("txtout"), 34 | ui.tags.h1("Header 1"), 35 | ui.tags.h2("Header 2"), 36 | ui.tags.h3("Header 3"), 37 | ui.tags.h4("Header 4"), 38 | ui.tags.h5("Header 5"), 39 | ), 40 | ui.nav("Tab 2"), 41 | ui.nav("Tab 3"), 42 | ) 43 | ), 44 | ), 45 | ), 46 | ui.nav("Plot"), 47 | ui.nav("Table"), 48 | title="Shinyswatch", 49 | ) 50 | 51 | 52 | def server(input, output, session): 53 | @output 54 | @render.code 55 | def txtout(): 56 | return f"{input.txt()}, {input.slider()}, {input.slider()}" 57 | 58 | @output 59 | @render.table 60 | def table(): 61 | import pandas as pd 62 | 63 | cars = pd.DataFrame( 64 | { 65 | "speed": [4, 4, 7, 7, 8, 9], 66 | "dist": [2, 10, 4, 22, 16, 10], 67 | } 68 | ) 69 | return cars.head(4) 70 | 71 | 72 | app = App(app_ui, server) 73 | -------------------------------------------------------------------------------- /examples/python/shinyswatch/requirements.txt: -------------------------------------------------------------------------------- 1 | shinyswatch 2 | -------------------------------------------------------------------------------- /examples/python/static_content/about.txt: -------------------------------------------------------------------------------- 1 | Static content 2 | Serve files from a subdirectory 3 | -------------------------------------------------------------------------------- /examples/python/static_content/app.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from shiny import App, render, ui 4 | 5 | app_ui = ui.page_fluid( 6 | ui.row( 7 | ui.column( 8 | 6, ui.input_slider("n", "Make a Shiny square:", min=0, max=6, value=2) 9 | ), 10 | ui.column( 11 | 6, 12 | ui.output_ui("images"), 13 | ), 14 | ) 15 | ) 16 | 17 | 18 | def square(x: ui.TagChild, n: int) -> ui.Tag: 19 | row = ui.div([x] * n) 20 | return ui.div([row] * n) 21 | 22 | 23 | def server(input, output, session): 24 | @output 25 | @render.ui 26 | def images() -> ui.Tag: 27 | img = ui.img(src="logo.png", style="width: 40px;") 28 | return square(img, input.n()) 29 | 30 | 31 | www_dir = Path(__file__).parent / "www" 32 | app = App(app_ui, server, static_assets=www_dir) 33 | -------------------------------------------------------------------------------- /examples/python/static_content/www/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posit-dev/shinylive/8acb41b0d4090308551e2f30dccb68fa6c9071a9/examples/python/static_content/www/logo.png -------------------------------------------------------------------------------- /examples/python/wordle/about.txt: -------------------------------------------------------------------------------- 1 | Wordle 2 | A clone of Wordle 3 | -------------------------------------------------------------------------------- /examples/python/wordle/style.css: -------------------------------------------------------------------------------- 1 | .container-fluid { 2 | text-align: center; 3 | height: calc(100vh - 30px); 4 | display: grid; 5 | grid-template-rows: 1fr auto; 6 | } 7 | .guesses { 8 | overflow-y: auto; 9 | height: 100%; 10 | } 11 | .guesses.finished { 12 | overflow-y: visible; 13 | } 14 | .guesses .word { 15 | margin: 5px; 16 | } 17 | .guesses .word > .letter { 18 | display: inline-block; 19 | width: 50px; 20 | height: 50px; 21 | text-align: center; 22 | vertical-align: middle; 23 | border-radius: 3px; 24 | line-height: 50px; 25 | font-size: 32px; 26 | font-weight: bold; 27 | vertical-align: middle; 28 | user-select: none; 29 | color: white; 30 | font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif; 31 | } 32 | .guesses .word > .correct { 33 | background-color: #6a5; 34 | } 35 | .guesses .word > .in-word { 36 | background-color: #db5; 37 | } 38 | .guesses .word > .not-in-word { 39 | background-color: #888; 40 | } 41 | .guesses .word > .guess { 42 | color: black; 43 | background-color: white; 44 | border: 1px solid black; 45 | } 46 | .keyboard { 47 | height: 240px; 48 | user-select: none; 49 | } 50 | .keyboard .keyboard-row { 51 | margin: 3px; 52 | } 53 | .keyboard .keyboard-row .key { 54 | display: inline-block; 55 | padding: 0; 56 | width: 30px; 57 | height: 50px; 58 | text-align: center; 59 | vertical-align: middle; 60 | border-radius: 3px; 61 | line-height: 50px; 62 | font-size: 18px; 63 | font-weight: bold; 64 | vertical-align: middle; 65 | color: black; 66 | font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif; 67 | background-color: #ddd; 68 | touch-action: none; 69 | } 70 | .keyboard .keyboard-row .key:focus { 71 | outline: none; 72 | } 73 | .keyboard .keyboard-row .key.wide-key { 74 | font-size: 15px; 75 | width: 50px; 76 | } 77 | .keyboard .keyboard-row .key.correct { 78 | background-color: #6a5; 79 | color: white; 80 | } 81 | .keyboard .keyboard-row .key.in-word { 82 | background-color: #db5; 83 | color: white; 84 | } 85 | .keyboard .keyboard-row .key.not-in-word { 86 | background-color: #888; 87 | color: white; 88 | } 89 | .endgame-content { 90 | font-family: Helvetica, Arial, sans-serif; 91 | display: inline-block; 92 | line-height: 1.4; 93 | letter-spacing: 0.2em; 94 | margin: 20px 8px; 95 | width: fit-content; 96 | padding: 20px; 97 | border-radius: 5px; 98 | box-shadow: 4px 4px 19px rgb(0 0 0 / 17%); 99 | } 100 | -------------------------------------------------------------------------------- /examples/r/001-hello/about.txt: -------------------------------------------------------------------------------- 1 | Hello Shiny! 2 | -------------------------------------------------------------------------------- /examples/r/001-hello/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | 4 | # Define UI for app that draws a histogram ---- 5 | ui <- page_sidebar( 6 | 7 | # App title ---- 8 | title = "Hello Shiny!", 9 | 10 | # Sidebar panel for inputs ---- 11 | sidebar = sidebar( 12 | 13 | # Input: Slider for the number of bins ---- 14 | sliderInput( 15 | inputId = "bins", 16 | label = "Number of bins:", 17 | min = 1, 18 | max = 50, 19 | value = 30 20 | ) 21 | ), 22 | 23 | # Output: Histogram ---- 24 | plotOutput(outputId = "distPlot") 25 | ) 26 | 27 | # Define server logic required to draw a histogram ---- 28 | server <- function(input, output) { 29 | 30 | # Histogram of the Old Faithful Geyser Data ---- 31 | # with requested number of bins 32 | # This expression that generates a histogram is wrapped in a call 33 | # to renderPlot to indicate that: 34 | # 35 | # 1. It is "reactive" and therefore should be automatically 36 | # re-executed when inputs (input$bins) change 37 | # 2. Its output type is a plot 38 | output$distPlot <- renderPlot({ 39 | x <- faithful$waiting 40 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 41 | 42 | hist( 43 | x, 44 | breaks = bins, 45 | col = "#75AADB", 46 | border = "white", 47 | xlab = "Waiting time to next eruption (in mins)", 48 | main = "Histogram of waiting times" 49 | ) 50 | }) 51 | } 52 | 53 | # Create Shiny app ---- 54 | shinyApp(ui = ui, server = server) 55 | -------------------------------------------------------------------------------- /examples/r/002-text/about.txt: -------------------------------------------------------------------------------- 1 | Shiny Text 2 | -------------------------------------------------------------------------------- /examples/r/002-text/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | 4 | # Define UI for dataset viewer app ---- 5 | ui <- page_sidebar( 6 | 7 | # App title ---- 8 | title = "Shiny Text", 9 | 10 | # Sidebar panel for inputs ---- 11 | sidebar = sidebar( 12 | 13 | # Input: Selector for choosing dataset ---- 14 | selectInput( 15 | inputId = "dataset", 16 | label = "Choose a dataset:", 17 | choices = c("rock", "pressure", "cars") 18 | ), 19 | 20 | # Input: Numeric entry for number of obs to view ---- 21 | numericInput( 22 | inputId = "obs", 23 | label = "Number of observations to view:", 24 | value = 10 25 | ) 26 | ), 27 | 28 | # Output: Verbatim text for data summary ---- 29 | verbatimTextOutput("summary"), 30 | 31 | # Output: HTML table with requested number of observations ---- 32 | tableOutput("view") 33 | ) 34 | 35 | # Define server logic to summarize and view selected dataset ---- 36 | server <- function(input, output) { 37 | 38 | # Return the requested dataset ---- 39 | datasetInput <- reactive({ 40 | switch( 41 | input$dataset, 42 | "rock" = rock, 43 | "pressure" = pressure, 44 | "cars" = cars 45 | ) 46 | }) 47 | 48 | # Generate a summary of the dataset ---- 49 | output$summary <- renderPrint({ 50 | dataset <- datasetInput() 51 | summary(dataset) 52 | }) 53 | 54 | # Show the first "n" observations ---- 55 | output$view <- renderTable({ 56 | head(datasetInput(), n = input$obs) 57 | }) 58 | } 59 | 60 | # Create Shiny app ---- 61 | shinyApp(ui = ui, server = server) 62 | -------------------------------------------------------------------------------- /examples/r/003-reactivity/about.txt: -------------------------------------------------------------------------------- 1 | Reactivity 2 | -------------------------------------------------------------------------------- /examples/r/003-reactivity/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | 4 | # Define UI for dataset viewer app ---- 5 | ui <- page_sidebar( 6 | 7 | # App title ---- 8 | title = "Reactivity", 9 | 10 | # Sidebar panel for inputs ---- 11 | sidebar = sidebar( 12 | 13 | # Input: Text for providing a caption ---- 14 | # Note: Changes made to the caption in the textInput control 15 | # are updated in the output area immediately as you type 16 | textInput( 17 | inputId = "caption_text", 18 | label = "Caption:", 19 | value = "Data Summary" 20 | ), 21 | 22 | # Input: Selector for choosing dataset ---- 23 | selectInput( 24 | inputId = "dataset", 25 | label = "Choose a dataset:", 26 | choices = c("rock", "pressure", "cars") 27 | ), 28 | 29 | # Input: Numeric entry for number of obs to view ---- 30 | numericInput( 31 | inputId = "obs", 32 | label = "Number of observations to view:", 33 | value = 10 34 | ) 35 | ), 36 | 37 | # Output: Formatted text for caption ---- 38 | h3(textOutput("caption", container = span)), 39 | 40 | # Output: Verbatim text for data summary ---- 41 | verbatimTextOutput("summary"), 42 | 43 | # Output: HTML table with requested number of observations ---- 44 | tableOutput("view") 45 | ) 46 | 47 | # Define server logic to summarize and view selected dataset ---- 48 | server <- function(input, output) { 49 | 50 | # Return the requested dataset ---- 51 | # By declaring datasetInput as a reactive expression we ensure 52 | # that: 53 | # 54 | # 1. It is only called when the inputs it depends on changes 55 | # 2. The computation and result are shared by all the callers, 56 | # i.e. it only executes a single time 57 | datasetInput <- reactive({ 58 | switch( 59 | input$dataset, 60 | "rock" = rock, 61 | "pressure" = pressure, 62 | "cars" = cars 63 | ) 64 | }) 65 | 66 | # Create caption ---- 67 | # The output$caption is computed based on a reactive expression 68 | # that returns input$caption. When the user changes the 69 | # "caption" field: 70 | # 71 | # 1. This function is automatically called to recompute the output 72 | # 2. New caption is pushed back to the browser for re-display 73 | # 74 | # Note that because the data-oriented reactive expressions 75 | # below don't depend on input$caption_text, those expressions are 76 | # NOT called when input$caption_text changes 77 | output$caption <- renderText({ 78 | input$caption_text 79 | }) 80 | 81 | # Generate a summary of the dataset ---- 82 | # The output$summary depends on the datasetInput reactive 83 | # expression, so will be re-executed whenever datasetInput is 84 | # invalidated, i.e. whenever the input$dataset changes 85 | output$summary <- renderPrint({ 86 | dataset <- datasetInput() 87 | summary(dataset) 88 | }) 89 | 90 | # Show the first "n" observations ---- 91 | # The output$view depends on both the databaseInput reactive 92 | # expression and input$obs, so it will be re-executed whenever 93 | # input$dataset or input$obs is changed 94 | output$view <- renderTable({ 95 | head(datasetInput(), n = input$obs) 96 | }) 97 | } 98 | 99 | # Create Shiny app ---- 100 | shinyApp(ui, server) 101 | -------------------------------------------------------------------------------- /examples/r/004-mpg/about.txt: -------------------------------------------------------------------------------- 1 | Miles Per Gallon 2 | -------------------------------------------------------------------------------- /examples/r/004-mpg/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | library(datasets) 4 | 5 | # Data pre-processing ---- 6 | # Tweak the "am" variable to have nicer factor labels -- since this 7 | # doesn't rely on any user inputs, we can do this once at startup 8 | # and then use the value throughout the lifetime of the app 9 | mpgData <- mtcars 10 | mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual")) 11 | 12 | 13 | # Define UI for miles per gallon app ---- 14 | ui <- page_sidebar( 15 | 16 | # App title ---- 17 | title = "Miles Per Gallon", 18 | 19 | # Sidebar panel for inputs ---- 20 | sidebar = sidebar( 21 | 22 | # Input: Selector for variable to plot against mpg ---- 23 | selectInput( 24 | "variable", 25 | "Variable:", 26 | c( 27 | "Cylinders" = "cyl", 28 | "Transmission" = "am", 29 | "Gears" = "gear" 30 | ) 31 | ), 32 | 33 | # Input: Checkbox for whether outliers should be included ---- 34 | checkboxInput("outliers", "Show outliers", TRUE) 35 | ), 36 | 37 | # Output: Formatted text for caption ---- 38 | h3(textOutput("caption")), 39 | 40 | # Output: Plot of the requested variable against mpg ---- 41 | plotOutput("mpgPlot") 42 | ) 43 | 44 | # Define server logic to plot various variables against mpg ---- 45 | server <- function(input, output) { 46 | 47 | # Compute the formula text ---- 48 | # This is in a reactive expression since it is shared by the 49 | # output$caption and output$mpgPlot functions 50 | formulaText <- reactive({ 51 | paste("mpg ~", input$variable) 52 | }) 53 | 54 | # Return the formula text for printing as a caption ---- 55 | output$caption <- renderText({ 56 | formulaText() 57 | }) 58 | 59 | # Generate a plot of the requested variable against mpg ---- 60 | # and only exclude outliers if requested 61 | output$mpgPlot <- renderPlot({ 62 | boxplot( 63 | as.formula(formulaText()), 64 | data = mpgData, 65 | outline = input$outliers, 66 | col = "#75AADB", 67 | pch = 19 68 | ) 69 | }) 70 | } 71 | 72 | # Create Shiny app ---- 73 | shinyApp(ui, server) 74 | -------------------------------------------------------------------------------- /examples/r/005-sliders/about.txt: -------------------------------------------------------------------------------- 1 | Sliders 2 | -------------------------------------------------------------------------------- /examples/r/005-sliders/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | 4 | # Define UI for slider demo app ---- 5 | ui <- page_sidebar( 6 | 7 | # App title ---- 8 | title = "Sliders", 9 | 10 | # Sidebar panel for inputs ---- 11 | sidebar = sidebar( 12 | 13 | # Input: Simple integer interval ---- 14 | sliderInput( 15 | "integer", 16 | "Integer:", 17 | min = 0, 18 | max = 1000, 19 | value = 500 20 | ), 21 | 22 | # Input: Decimal interval with step value ---- 23 | sliderInput( 24 | "decimal", 25 | "Decimal:", 26 | min = 0, 27 | max = 1, 28 | value = 0.5, 29 | step = 0.1 30 | ), 31 | 32 | # Input: Specification of range within an interval ---- 33 | sliderInput( 34 | "range", 35 | "Range:", 36 | min = 1, 37 | max = 1000, 38 | value = c(200, 500) 39 | ), 40 | 41 | # Input: Custom currency format for with basic animation ---- 42 | sliderInput( 43 | "format", 44 | "Custom Format:", 45 | min = 0, 46 | max = 10000, 47 | value = 0, 48 | step = 2500, 49 | pre = "$", 50 | sep = ",", 51 | animate = TRUE 52 | ), 53 | 54 | # Input: Animation with custom interval (in ms) ---- 55 | # to control speed, plus looping 56 | sliderInput( 57 | "animation", 58 | "Looping Animation:", 59 | min = 1, 60 | max = 2000, 61 | value = 1, 62 | step = 10, 63 | animate = 64 | animationOptions(interval = 300, loop = TRUE) 65 | ) 66 | ), 67 | 68 | # Output: Table summarizing the values entered ---- 69 | tableOutput("values") 70 | ) 71 | 72 | # Define server logic for slider examples ---- 73 | server <- function(input, output) { 74 | 75 | # Reactive expression to create data frame of all input values ---- 76 | sliderValues <- reactive({ 77 | data.frame( 78 | Name = c( 79 | "Integer", 80 | "Decimal", 81 | "Range", 82 | "Custom Format", 83 | "Animation" 84 | ), 85 | Value = as.character(c( 86 | input$integer, 87 | input$decimal, 88 | paste(input$range, collapse = " "), 89 | input$format, 90 | input$animation 91 | )), 92 | stringsAsFactors = FALSE 93 | ) 94 | }) 95 | 96 | # Show the values in an HTML table ---- 97 | output$values <- renderTable({ 98 | sliderValues() 99 | }) 100 | } 101 | 102 | # Create Shiny app ---- 103 | shinyApp(ui, server) 104 | -------------------------------------------------------------------------------- /examples/r/006-tabsets/about.txt: -------------------------------------------------------------------------------- 1 | Tabsets 2 | -------------------------------------------------------------------------------- /examples/r/006-tabsets/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | 4 | # Define UI for random distribution app ---- 5 | # Sidebar layout with input and output definitions ---- 6 | ui <- page_sidebar( 7 | 8 | # App title ---- 9 | title = "Tabsets", 10 | 11 | # Sidebar panel for inputs ---- 12 | sidebar = sidebar( 13 | 14 | # Input: Select the random distribution type ---- 15 | radioButtons( 16 | "dist", 17 | "Distribution type:", 18 | c( 19 | "Normal" = "norm", 20 | "Uniform" = "unif", 21 | "Log-normal" = "lnorm", 22 | "Exponential" = "exp" 23 | ) 24 | ), 25 | # br() element to introduce extra vertical spacing ---- 26 | br(), 27 | # Input: Slider for the number of observations to generate ---- 28 | sliderInput( 29 | "n", 30 | "Number of observations:", 31 | value = 500, 32 | min = 1, 33 | max = 1000 34 | ) 35 | ), 36 | 37 | # Main panel for displaying outputs ---- 38 | # Output: A tabset that combines three panels ---- 39 | navset_card_underline( 40 | # Panel with plot ---- 41 | nav_panel("Plot", plotOutput("plot")), 42 | 43 | # Panel with summary ---- 44 | nav_panel("Summary", verbatimTextOutput("summary")), 45 | 46 | # Panel with table ---- 47 | nav_panel("Table", tableOutput("table")) 48 | ) 49 | ) 50 | 51 | # Define server logic for random distribution app ---- 52 | server <- function(input, output) { 53 | 54 | # Reactive expression to generate the requested distribution ---- 55 | # This is called whenever the inputs change. The output functions 56 | # defined below then use the value computed from this expression 57 | d <- reactive({ 58 | dist <- switch( 59 | input$dist, 60 | norm = rnorm, 61 | unif = runif, 62 | lnorm = rlnorm, 63 | exp = rexp, 64 | rnorm 65 | ) 66 | 67 | dist(input$n) 68 | }) 69 | 70 | # Generate a plot of the data ---- 71 | # Also uses the inputs to build the plot label. Note that the 72 | # dependencies on the inputs and the data reactive expression are 73 | # both tracked, and all expressions are called in the sequence 74 | # implied by the dependency graph. 75 | output$plot <- renderPlot({ 76 | dist <- input$dist 77 | n <- input$n 78 | 79 | hist( 80 | d(), 81 | main = paste("r", dist, "(", n, ")", sep = ""), 82 | col = "#75AADB", 83 | border = "white" 84 | ) 85 | }) 86 | 87 | # Generate a summary of the data ---- 88 | output$summary <- renderPrint({ 89 | summary(d()) 90 | }) 91 | 92 | # Generate an HTML table view of the data ---- 93 | output$table <- renderTable({ 94 | d() 95 | }) 96 | } 97 | 98 | # Create Shiny app ---- 99 | shinyApp(ui, server) 100 | -------------------------------------------------------------------------------- /examples/r/007-widgets/about.txt: -------------------------------------------------------------------------------- 1 | Widgets 2 | -------------------------------------------------------------------------------- /examples/r/007-widgets/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(bslib) 3 | 4 | # Define UI for slider demo app ---- 5 | ui <- page_sidebar( 6 | 7 | # App title ---- 8 | title = "More Widgets", 9 | 10 | # Sidebar panel for inputs ---- 11 | sidebar = sidebar( 12 | 13 | # Input: Select a dataset ---- 14 | selectInput( 15 | "dataset", 16 | "Choose a dataset:", 17 | choices = c("rock", "pressure", "cars") 18 | ), 19 | 20 | # Input: Specify the number of observations to view ---- 21 | numericInput("obs", "Number of observations to view:", 10), 22 | 23 | # Include clarifying text ---- 24 | helpText( 25 | "Note: while the data view will show only the specified", 26 | "number of observations, the summary will still be based", 27 | "on the full dataset." 28 | ), 29 | 30 | # Input: actionButton() to defer the rendering of output ---- 31 | # until the user explicitly clicks the button (rather than 32 | # doing it immediately when inputs change). This is useful if 33 | # the computations required to render output are inordinately 34 | # time-consuming. 35 | actionButton("update", "Update View") 36 | ), 37 | 38 | # Output: Header + summary of distribution ---- 39 | h4("Summary"), 40 | verbatimTextOutput("summary"), 41 | 42 | # Output: Header + table of distribution ---- 43 | h4("Observations"), 44 | tableOutput("view") 45 | ) 46 | 47 | # Define server logic to summarize and view selected dataset ---- 48 | server <- function(input, output) { 49 | 50 | # Return the requested dataset ---- 51 | # Note that we use eventReactive() here, which depends on 52 | # input$update (the action button), so that the output is only 53 | # updated when the user clicks the button 54 | datasetInput <- eventReactive( 55 | input$update, 56 | { 57 | switch( 58 | input$dataset, 59 | "rock" = rock, 60 | "pressure" = pressure, 61 | "cars" = cars 62 | ) 63 | }, 64 | ignoreNULL = FALSE 65 | ) 66 | 67 | # Generate a summary of the dataset ---- 68 | output$summary <- renderPrint({ 69 | dataset <- datasetInput() 70 | summary(dataset) 71 | }) 72 | 73 | # Show the first "n" observations ---- 74 | # The use of isolate() is necessary because we don't want the table 75 | # to update whenever input$obs changes (only when the user clicks 76 | # the action button) 77 | output$view <- renderTable({ 78 | head(datasetInput(), n = isolate(input$obs)) 79 | }) 80 | } 81 | 82 | # Create Shiny app ---- 83 | shinyApp(ui, server) 84 | -------------------------------------------------------------------------------- /examples/r/008-html/about.txt: -------------------------------------------------------------------------------- 1 | Custom HTML UI 2 | -------------------------------------------------------------------------------- /examples/r/008-html/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | 3 | # Define server logic for random distribution app ---- 4 | server <- function(input, output) { 5 | 6 | # Reactive expression to generate the requested distribution ---- 7 | # This is called whenever the inputs change. The output functions 8 | # defined below then use the value computed from this expression 9 | d <- reactive({ 10 | dist <- switch( 11 | input$dist, 12 | norm = rnorm, 13 | unif = runif, 14 | lnorm = rlnorm, 15 | exp = rexp, 16 | rnorm 17 | ) 18 | 19 | dist(input$n) 20 | }) 21 | 22 | # Generate a plot of the data ---- 23 | # Also uses the inputs to build the plot label. Note that the 24 | # dependencies on the inputs and the data reactive expression are 25 | # both tracked, and all expressions are called in the sequence 26 | # implied by the dependency graph. 27 | output$plot <- renderPlot({ 28 | dist <- input$dist 29 | n <- input$n 30 | 31 | hist( 32 | d(), 33 | main = paste("r", dist, "(", n, ")", sep = ""), 34 | col = "#75AADB", 35 | border = "white" 36 | ) 37 | }) 38 | 39 | # Generate a summary of the data ---- 40 | output$summary <- renderPrint({ 41 | summary(d()) 42 | }) 43 | 44 | # Generate an HTML table view of the head of the data ---- 45 | output$table <- renderTable({ 46 | head(data.frame(x = d())) 47 | }) 48 | } 49 | 50 | # Create Shiny app ---- 51 | shinyApp(ui = htmlTemplate("www/index.html"), server) 52 | -------------------------------------------------------------------------------- /examples/r/008-html/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
14 |
15 |
21 |
24 |
25 |
26 |
27 |
28 |
86 | {content.value}
87 |
88 | )}
89 |