反应性地检查文件是否可用
Reactively check if a file is available
我正在开发一个闪亮的应用程序,让您可以导入一些数据、操作和绘制它们。
当您对分析感到满意时,您可以使用 python 脚本生成一个 .xlsx 文件(我正在使用 reticulate 使其在闪亮的应用程序中运行):
server <- function(input, output){
observeEvent(input$run_py, {
# pass variable from R to Py
py$df <- avg.spread()
py$var <- var.spread()
py_run_file('py_export_excel.py', local = FALSE, convert = FALSE)
})
}
.xlsx 文件没有立即准备好,所以我需要一个不同的按钮来让您下载文件。
server <- function(input, output){
output$downloadData <- downloadHandler(
filename = "file.xlsx",
content <- function(file) {
file.copy("test.xlsx", file)
}
)}
到目前为止一切正常,但我不喜欢必须按下两个不同的按钮才能只做一件事的想法。
我想要一个反应式处理程序,它会不断检查文件是否存在并自动提示您下载它。有没有办法使以下代码具有反应性?
if(file.exists("test.xlsx")) {
# download code
...
}
您可以使用 invalidateLater
重复(在延迟后)反应块。试试这个:
library(shiny)
library(shinyjs)
.log <- function(...) message(format(Sys.time(), format = "[ %H:%M:%S ]"), " ", ...)
.maxtime <- 60
shinyApp(
ui = fluidPage(
useShinyjs(),
actionButton("doit", "Long process ..."),
downloadButton("dnld", "Download!")
),
server = function(input, output, session) {
.log("hello world")
started <- reactiveVal(Sys.time()[NA])
shinyjs::disable("dnld")
# just for this answer
if (file.exists("61240116.txt")) warning("file exists: '61240116.txt'", call. = FALSE)
# this is your "long process", here I just delay and touch the
# file; the important parts to preserve are 'disable' and
# 'started' (or similar)
observeEvent(input$doit, {
shinyjs::disable("dnld")
.log("starting a long process ...")
started(Sys.time())
system("sh -c '{ sleep 5 ; touch 61240116.txt; }'", wait = FALSE)
})
# this is the work-horse here: it starts whenever 'started' is
# triggered, and keeps working as long as it has not been too long
observe({
req(started())
d <- difftime(Sys.time(), started(), units = "secs")
if (file.exists("61240116.txt")) {
.log("... done!")
started(Sys.time()[NA])
shinyjs::enable("dnld")
} else if (d > .maxtime) {
.log("timed out")
# might need to do some damage-control here
} else {
.log("... cycling, ", round(d, 1))
invalidateLater(1000, session)
}
})
# simple/naive download handler
output$dnld <- downloadHandler(
filename = function() "61240116.txt",
content = function(file) file.copy("61240116.txt", file)
)
}
)
# Listening on http://127.0.0.1:5020
# [ 16:56:08 ] hello world
# [ 16:56:17 ] starting a long process ...
# [ 16:56:18 ] ... cycling, 0
# [ 16:56:19 ] ... cycling, 1
# [ 16:56:20 ] ... cycling, 2
# [ 16:56:21 ] ... cycling, 3
# [ 16:56:22 ] ... cycling, 4
# [ 16:56:23 ] ... cycling, 5
# [ 16:56:24 ] ... done!
- 启动应用,注意
Download!
被禁用(第一个shinyjs::disable
);
- 单击
Long process ...
按钮,注意在控制台上您应该会看到一些状态消息。
- 大约 5 秒后,下载按钮应该会启用,您现在可以下载文件了。
本小程序的"engineering":
started
有两个方面:开始 1000 毫秒循环失效,以及永远保持此检查来自 运行。 (当然有可能(如果你 "set" started
在多个地方)打破这个结构。)使用 "time" 可能不适合你的情况,也不是完全万无一失的(例如,负载很重的系统可能需要更多时间)。
- 我出于三个目的将
started
设置并重置为 Sys.time()[NA]
:首先,它需要始终是 POSIXt
而不是 "recent";其次,当 difftime
只是一个数字时(例如 0
),它会变得不安,因为这需要一个时间起点;第三,req
将导致 observe
块提前终止,如果它是某种形式的 NA
(或其他不真实的东西......见 ?req
)。我们依赖于这种行为。
shinyjs
用于禁用下载按钮,以便 我们 控制何时可以下载某些内容。
我正在开发一个闪亮的应用程序,让您可以导入一些数据、操作和绘制它们。 当您对分析感到满意时,您可以使用 python 脚本生成一个 .xlsx 文件(我正在使用 reticulate 使其在闪亮的应用程序中运行):
server <- function(input, output){
observeEvent(input$run_py, {
# pass variable from R to Py
py$df <- avg.spread()
py$var <- var.spread()
py_run_file('py_export_excel.py', local = FALSE, convert = FALSE)
})
}
.xlsx 文件没有立即准备好,所以我需要一个不同的按钮来让您下载文件。
server <- function(input, output){
output$downloadData <- downloadHandler(
filename = "file.xlsx",
content <- function(file) {
file.copy("test.xlsx", file)
}
)}
到目前为止一切正常,但我不喜欢必须按下两个不同的按钮才能只做一件事的想法。 我想要一个反应式处理程序,它会不断检查文件是否存在并自动提示您下载它。有没有办法使以下代码具有反应性?
if(file.exists("test.xlsx")) {
# download code
...
}
您可以使用 invalidateLater
重复(在延迟后)反应块。试试这个:
library(shiny)
library(shinyjs)
.log <- function(...) message(format(Sys.time(), format = "[ %H:%M:%S ]"), " ", ...)
.maxtime <- 60
shinyApp(
ui = fluidPage(
useShinyjs(),
actionButton("doit", "Long process ..."),
downloadButton("dnld", "Download!")
),
server = function(input, output, session) {
.log("hello world")
started <- reactiveVal(Sys.time()[NA])
shinyjs::disable("dnld")
# just for this answer
if (file.exists("61240116.txt")) warning("file exists: '61240116.txt'", call. = FALSE)
# this is your "long process", here I just delay and touch the
# file; the important parts to preserve are 'disable' and
# 'started' (or similar)
observeEvent(input$doit, {
shinyjs::disable("dnld")
.log("starting a long process ...")
started(Sys.time())
system("sh -c '{ sleep 5 ; touch 61240116.txt; }'", wait = FALSE)
})
# this is the work-horse here: it starts whenever 'started' is
# triggered, and keeps working as long as it has not been too long
observe({
req(started())
d <- difftime(Sys.time(), started(), units = "secs")
if (file.exists("61240116.txt")) {
.log("... done!")
started(Sys.time()[NA])
shinyjs::enable("dnld")
} else if (d > .maxtime) {
.log("timed out")
# might need to do some damage-control here
} else {
.log("... cycling, ", round(d, 1))
invalidateLater(1000, session)
}
})
# simple/naive download handler
output$dnld <- downloadHandler(
filename = function() "61240116.txt",
content = function(file) file.copy("61240116.txt", file)
)
}
)
# Listening on http://127.0.0.1:5020
# [ 16:56:08 ] hello world
# [ 16:56:17 ] starting a long process ...
# [ 16:56:18 ] ... cycling, 0
# [ 16:56:19 ] ... cycling, 1
# [ 16:56:20 ] ... cycling, 2
# [ 16:56:21 ] ... cycling, 3
# [ 16:56:22 ] ... cycling, 4
# [ 16:56:23 ] ... cycling, 5
# [ 16:56:24 ] ... done!
- 启动应用,注意
Download!
被禁用(第一个shinyjs::disable
); - 单击
Long process ...
按钮,注意在控制台上您应该会看到一些状态消息。 - 大约 5 秒后,下载按钮应该会启用,您现在可以下载文件了。
本小程序的"engineering":
started
有两个方面:开始 1000 毫秒循环失效,以及永远保持此检查来自 运行。 (当然有可能(如果你 "set"started
在多个地方)打破这个结构。)使用 "time" 可能不适合你的情况,也不是完全万无一失的(例如,负载很重的系统可能需要更多时间)。- 我出于三个目的将
started
设置并重置为Sys.time()[NA]
:首先,它需要始终是POSIXt
而不是 "recent";其次,当difftime
只是一个数字时(例如0
),它会变得不安,因为这需要一个时间起点;第三,req
将导致observe
块提前终止,如果它是某种形式的NA
(或其他不真实的东西......见?req
)。我们依赖于这种行为。 shinyjs
用于禁用下载按钮,以便 我们 控制何时可以下载某些内容。