0%

shiny学习笔记

项目要求做一个可以交互的网页,因为R老师已经使用shiny帮我做了一个demo了,所以接下来我需要进一步完善整个网页。

我现在有两周完成产品,时间应该还比较充裕,可以从头学学shiny。并且网络上关于shiny for python的资料真的很少,所以这一次我也想尝试一边学习一边做一些笔记。

因为shiny对vscode的支持很好,所以我没有用jupyter,而是又尝试了一下在vscode上配置python。不过烦人的是,我几乎花了一晚上时间尝试在vscode上激活conda虚拟环境,主要是因为我的机器学习库都在Miku环境中。尝试了一系列操作,主要是修改setting.json或者lauch.json还有修改什么powershell,虽然一直在使用vscode,但是我对其的认识还只停留在使用上,如此简单但是功能强大的软件,其实值得去学学其原理。

记录一下学习代码的位置:E:\学习\python\py_codbase\shiny_notes
官方文档链接:https://shiny.rstudio.com/py/docs/overview.html

现在也遇到些问题

  • 在我瞎几把设置vscode的编译器后,在每次开始调试后,终端termail的命令前都会多一个地址。
  • 所以说需要修改命令才可以开始调试

在安装时注意到:

您有时可能需要强制安装我们软件包的更新版本,因为它们正在开发中。这可以通过以下方式完成:

1
pip install --upgrade shiny htmltools

Running

1
shiny create . 

创建一个app.py作为静态网页接口,也是编写代码的文件

1
shiny run --reload

在命令行中加载网页

理想的编辑环境:


Shiny applications consist of two parts:

  • the user interface (or UI),
  • and the server function.

These are combined using a shiny.App object.

网页逻辑反应流

shiny网页前端总体分两个部分。app_ui是作为控制网页布局样式的。这里ui.input_slider是设置了一个slider作为数据输入,用户通过移动slider来控制输入数值”n“。而n作为输入,被传入到server函数中,进行逻辑运算。再server中,通过@output@render.text,来修饰txt函数。前者表示函数结果应该被显示在网页上,后者表示结果是文本(e.g.其肯定不是图像)。而txt的输出被传到app_ui中,并在ui.output_text_verbatim中进行展示。

输入函数

数值类型输入
ui.input_slider
ui.input_numeric

选择输入
当multiple = True时,可以选择多个文本

1
2
3
4
5
6
7
8
9
10
11
12
13

from shiny import ui, App
import re

# A list of Python's built-in functions
choices = list(filter(lambda x: re.match(r'[a-z].*', x), dir(__builtins__)))

app_ui = ui.page_fluid(
ui.input_selectize("x1", "Selectize (single)", choices),
ui.input_selectize("x2", "Selectize (multiple)", choices, multiple = True),
)

app = App(app_ui, None)

ui.input_checkbox_group(“x2”, “Checkbox group”, choices) 接口支持多输入

输出控制

Outputs create a spot on the webpage to put results from the server.

输出支持静态图像,文字,甚至是地图。
找到了一个地图可视化库ipyleaflet,支持很多可交互式操作


sever function

When you define an output function, Shiny makes it reactive, and so it can be used to access input values.

在逻辑函数中,需要先定义函数才能获取input。


响应式编程 reactive programming

At the heart of Shiny is a reactive programming engine.

1
2
3
4
5
6
7
8
9
@reactive.Calc
def double_x():
return input.x() * 2
# Then we can use the reactive calculation in an output:

@output
@render.text
def txt():
return f"x * 2 is {double_x()}"

一个”reactive.Calc”与一个普通函数的主要区别在于它尝试通过使用缓存来最小化执行的工作量。当”reactive.Calc”运行时,它会缓存其最近的计算结果,并简单地返回该缓存值,直到”Calc”被标记为失效。

什么是”失效(invalidated)”?像”reactive.Calc”、”reactive.Effect”(还有”@output”函数)这样的响应式对象,在其所依赖的响应式输入发生变化时会被标记为失效。例如,如果”double_x()”函数使用了”input.x()”,那么如果用户改变了”input.x()”的值,”double_x()”就会被标记为失效。然后,”double_x()”会使其依赖的任何其他响应式对象失效。在我们的示例中,它会使输出”txt”失效。

当输出”txt”被标记为失效后,它会重新执行,重新执行时会调用”double_x()”。由于”double_x()”被标记为失效,它会重新执行(而不是简单地返回缓存的值),然后它会读取”input.x()”的新值。

控制响应函数 Controlling reactivity with isolate() and @reactive.event()

通常情况下,当响应式函数的任何响应式输入发生变化时,它都会被标记为失效(并重新执行)。所谓响应式输入,不仅指来自用户的输入,比如”input.x()”,还包括来自”reactive.Calc”对象的值。

有时这些依赖关系会导致函数被过于频繁地重新执行。例如,假设我们有一个输出,它依赖于滑块的值,但是计算成本很高。我们可能希望它只在用户按下按钮时重新执行。

有两种方法可以实现这一点:一种是使用”isolate()”函数,另一种是”@reactive.event()”注解。

使用”reactive.isolate()”函数时,代码块在响应式函数内部运行,但不会对代码块内部的响应式输入产生响应式依赖。这意味着该块中的任何响应式输入都不会导致函数重新执行。在下面的示例中,输出依赖于”input.button()”,但不依赖于”input.x()”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from shiny import App, reactive, render, ui
import asyncio

app_ui = ui.page_fluid(
ui.input_slider("n", "N", min=1, max=100, value=1),
ui.input_action_button("compute", "Compute!"),
ui.output_text_verbatim("result", placeholder=True),
)

def server(input, output, session):

@output
@render.text
async def result(): # 异步编程语法async await ,await asyncio.sleep(2)以模拟长时间的计算过程
input.compute() # 这里关联上了ui.input_action_button,此变量改变会使函数失效并且重新运行
await asyncio.sleep(2) # Wait 2 seconds (to simulate a long computation)

with reactive.isolate(): # 该块中的任何输入的改变都不会导致函数重新执行。只有input.compute()改变时才会重新运行
# Inside this block, we can use input.n() without taking a
# dependency on it.
return f"Result: {input.n()}"

app = App(app_ui, server)

变量不止可以放到input中,还可以自己定义响应变量。响应式变量在需要进行更复杂的响应式操作时非常有用。

1
2
# Create a reactive value with a starting value of "Hello"
x = reactive.Value("Hello")