我有一个小小的闪亮应用程序用于注释文本文件。
fileInput
来选择.txt
个文件。启动应用程序时,其中一个文件是默认文件。 Next
,Previous
按钮允许用户一次显示一个文件的内容。 Add Markup
按钮来注释句子。动作按钮触发javascript函数addMarkup()
。 我只在这里发布闪亮的应用代码。完整的应用代码可在github repository
上找到 library(shiny)
ui <- fluidPage(
tags$head(tags$script(src="textselection.js")),
titlePanel("Corpus Annotation Utility"),
sidebarLayout(
sidebarPanel(
fileInput('fileInput', 'Select Corpus', accept = c('text', 'text','.txt')),
actionButton("Previous", "Previous"),
actionButton("Next", "Next"),
actionButton("mark", "Add Markup")
),
mainPanel(
tags$h1("Sentence: "),
htmlOutput("sentence"),
tags$h1("Sentence marked up: "),
htmlOutput("sentenceMarkedUp")
)
)
)
server <- function(input, output) {
sourceData <- reactive({
corpusFile <- input$fileInput
if(is.null(corpusFile)){
return(readCorpus('data/news.txt'))
}
readCorpus(corpusFile$datapath)
})
corpus <- reactive({sourceData()})
values <- reactiveValues(current = 1)
observeEvent(input$Next,{
if(values$current >=1 & values$current < length(corpus())){
values$current <- values$current + 1
}
})
observeEvent(input$Previous,{
if(values$current > 1 & values$current <= length(corpus())){
values$current <- values$current - 1
}
})
output$sentence <- renderText(corpus()[values$current])
}
shinyApp(ui = ui, server = server)
readCorpus()
函数如下所示:
readCorpus <- function(pathToFile){
con <- file(pathToFile)
sentences <- readLines(con, encoding = "UTF-8")
close(con)
return(sentences)
}
我的问题是如何在注释后将句子保留到文件中?
更新 我已经完成了Persistent data storage in Shiny apps,并希望我能够按照有关持久存储的文档进行操作。但是我仍然不确定如何在标记后捕获句子。
答案 0 :(得分:2)
这里有两个问题 - 保留更改,然后保存输出。我使用一些JS和一些R代码解决了这个问题。我将在Github上执行拉取请求以提交更广泛的代码。但是,这是它的核心。
在用于选择内容的Javascript中,您可以使用Shiny.onInputChange()
更新input
向量的元素。这样做,您可以为语料库创建一个reactiveValues
项,然后使用您界面的输入进行更新。
下面,您会注意到我从使用textnode切换到仅使用内部HTML。使用节点和firstChild
,就像之前一样,你最终在第一次注释后截断句子(因为它只选择<mark>
之前的东西。这样做似乎效果更好。
window.onload = function(){
document.getElementById('mark').addEventListener('click', addMarkup);
}
function addMarkup(){
var sentence = document.getElementById("sentence").innerHTML,
selection="";
if(window.getSelection){
selection = window.getSelection().toString();
}
else if(document.selection && document.selection.type != "Control"){
selection = document.selection.createRange().text;
}
if(selection.length === 0){
return;
}
marked = "<mark>".concat(selection).concat("</mark>");
result = sentence.replace(selection, marked);
document.getElementById("sentence").innerHTML = result;
Shiny.onInputChange("textresult",result);
}
接下来,我尝试简化您的server.R
代码。您正在使用反应性上下文从另一个反应性上下文(sourceData
拉入corpus
),这似乎是不必要的。所以,我试着稍微重构一下。
library(shiny)
source("MyUtils.R")
ui <- fluidPage(
tags$head(tags$script(src="textselection.js")),
titlePanel("Corpus Annotation Utility"),
sidebarLayout(
sidebarPanel(
fileInput('fileInput', 'Select Corpus', accept = c('text', 'text','.txt')),
actionButton("Previous", "Previous"),
actionButton("Next", "Next"),
actionButton("mark", "Add Markup"),
downloadButton(outputId = "save",label = "Download")),
mainPanel(
tags$h1("Sentence: "),
htmlOutput("sentence"))
)
)
server <- function(input, output) {
corpus <- reactive({
corpusFile <- input$fileInput
if(is.null(corpusFile)) {
return(readCorpus('data/news.txt'))
} else {
return(readCorpus(corpusFile$datapath))
}
})
values <- reactiveValues(current = 1)
observe({
values$corpus <- corpus()
})
output$sentence <- renderText(values$corpus[values$current])
observeEvent(input$Next,{
if(values$current >=1 & values$current < length(corpus())) {
values$current <- values$current + 1
}
})
observeEvent(input$Previous,{
if(values$current > 1 & values$current <= length(corpus())) {
values$current <- values$current - 1
}
})
observeEvent(input$mark,{
values$corpus[values$current] <- input$textresult
})
output$save <- downloadHandler(filename = "marked_corpus.txt",
content = function(file) {
writeLines(text = values$corpus,
con = file,
sep = "\n")
})
}
现在,代码有一些变化。从文件加载基本相同。我对isolate
的怀疑态度是正确的 - 用observe
替换它来完成我想做的事情,而isolate
只会给你初始负荷。无论如何,我们使用observe
将语料库值加载到您创建的reactiveValues
对象中 - 这是为了给我们一个传播更改数据的位置。
我们保留了前进和后退的剩余逻辑。但是,我们更改输出的呈现方式,以便查看reactiveValues
对象。然后,我们创建一个观察者,使用我们更新的Javascript输入更新reactiveValues
对象。发生这种情况时,数据会永久存储,您也可以在字符串中标记多个序列(尽管我没有使用嵌套标记或删除标记做任何事情)。最后,添加了一个保存功能 - 结果字符串保存在<mark>
,用于显示标记区域。
如果您加载以前标记的文件,标记将再次显示。