I would like to do something as: there are actionLink a and actionLink b. And they can trigger the UpdateNumericInput() which assigns some weights. I can also manually change the weights on the numericInput() in UI. What I want to realize is when clicking on the actionLink a, the textoutput would be "click on a", and same thing happened with actionLink b as "click on b". I also want want the textoutput to show up as "manually changed the weights" when I actually edit the weights on UI. Is there any way to realize this?
#triggered by actionLink a
apply_a_weights = observeEvent(input$a_weight_link, {
v_a = get_a_weights()
v_a = setNames(as.double(unname(v_a)), names(v_a))
for (nm in names(v_a)) {
updateNumericInput(session, nm, value = v_a[[nm]])
}
output$selected_weight <- renderUI({HTML("You have changed to <B>a weight</B>")}) }, priority = 1)
# triggered by actionLink b
apply_b_weights = observeEvent(input$b_weight_link, {
v_b = df_dmd$b_weights
names(v_b) = df_dmd$input_id
for (nm in names(v_b)) {
updateNumericInput(session, nm, value = v_b[[nm]])
}
output$selected_weight <- renderUI({HTML("You have changed to <B>b weight</B>")})}, priority = 1)
I also tried to put this part in the server.R as the default textoutput:
output$selected_weight <- renderUI({HTML("<B>manually</B> changed the weights")})
But those codes doesn't work. When I change the weights on my own, the "manually changed the weights" doesn't show up.
Many Thanks
You don't provide a reproducible code so it's hard to guess. Possibly, the problem is the duplicated output$selected_weight.
Define a reactiveVal to store the text do be displayed, for example at the beginning of server:
Text <- reactiveVal()
Then in your two observeEvent, remove output$selected_weight, and do:
#triggered by actionLink a
apply_a_weights = observeEvent(input$a_weight_link, {
v_a = get_a_weights()
v_a = setNames(as.double(unname(v_a)), names(v_a))
for (nm in names(v_a)) {
updateNumericInput(session, nm, value = v_a[[nm]])
}
Text("You have changed to <B>a weight</B>")
}, priority = 1)
# triggered by actionLink b
apply_b_weights = observeEvent(input$b_weight_link, {
v_b = df_dmd$b_weights
names(v_b) = df_dmd$input_id
for (nm in names(v_b)) {
updateNumericInput(session, nm, value = v_b[[nm]])
}
Text("You have changed to <B>b weight</B>")
}, priority = 1)
Finally, outside the observeEvents, do
output$selected_weight <- renderUI({HTML(Text()})
Again, your don't provide a minimal reproducible code, so I'm not sure my answer provides what you want.
Related
I'm looking at the code example provided in the R6 class documentation here.
This is the class definition:
Person <- R6Class("Person",
private = list(
.age = NA,
.name = NULL
),
active = list(
age = function(value) {
if (missing(value)) {
private$.age
} else {
stop("`$age` is read only", call. = FALSE)
}
},
name = function(value) {
if (missing(value)) {
private$.name
} else {
stopifnot(is.character(value), length(value) == 1)
private$.name <- value
self
}
}
),
public = list(
initialize = function(name, age = NA) {
private$.name <- name
private$.age <- age
}
)
)
The important part is how the age field is wrapped to be read-only, and the name field is wrapped to perform a validation check before making the assignment.
In my own usecase, I'm not interested in the read-only part, but I am implementing the validation logic. So, focus on the active field for name.
I do not understand why they are following the pattern to return self after the call. How can you chain together assignments? Assuming age wasn't read only, how would it look to try to assign age and name in a chain? I've tried a number of ways, and it never seems to work:
p <- Person$new();
p$age <- 10$name <- "Jill" # obviously doesn't work because how can you reference the name field of 10?
(p$age <- 10)$name <- "Jill" # this looks more likely to work but the parens don't help
p$age(10)$name("Jill") # Does not work, you can't invoke active fields as if they were functions.
# other syntax options?
So, the heart of my question is: if you're using active fields in R6 classes to facilitate some type-checking prior to assignment, you can't really chain those operations together, so why does the official documentation show returning self in the field accessors?
I am trying to extend websocket::Websocket with a method that sends some data and returns the message, so that I can assign it to an object. My question is pretty much identical to https://community.rstudio.com/t/capture-streaming-json-over-websocket/16986. Unfortunately, the user there never revealed how they solved it themselves. My idea was to have the onMessage method return the event$data, i.e. something like:
my_websocket <- R6::R6Class("My websocket",
inherit = websocket::WebSocket,
public = list(
foo = function(x) {
msg <- super$send(paste("x"))
return(msg)
} )
)
load_websocket <- function(){
ws <- my_websocket$new("ws://foo.local")
ws$onMessage(function(event) {
return(event$data)
})
return(ws)
}
my_ws <- load_websocket()
my_ws$foo("hello") # returns NULL
but after spending a good hour on the Websocket source code, I am still completely in the dark as to where exactly the callback happens, "R environment wise".
You need to use super assignment operator <<-. <<- is most useful in conjunction with closures to maintain state. Unlike the usual single arrow assignment (<-) that always works on the current level, the double arrow operator can modify variables in parent levels.
my_websocket <- R6::R6Class("My websocket",
inherit = websocket::WebSocket,
public = list(
foo = function(x) {
msg <<- super$send(paste("x"))
return(msg)
} )
)
load_websocket <- function(){
ws <- my_websocket$new("ws://foo.local")
ws$onMessage(function(event) {
return(event$data)
})
return(ws)
}
my_ws <- load_websocket()
my_ws$foo("hello")
I want to capture key-value pairs via Shiny (R) where a few widgets (keys) are selected by the user via the UI from a known but large list of possible optionss.
Corresponding to each Widget he selects a numeric input box is shown where he can then enter a value (quantity).
I posted a demo here:
https://statspot.shinyapps.io/app_upload_test/
For further processing it would be nice to have a data-frame with those key value pairs that were selected / entered by the user. That's the red table below. i.e. A dataframe with widgets selected & their corresponding quantities entered.
My problem is I haven't figured out how to get the values entered by the user dynamically in there (I've put in 999 as a static filler dummy value for now). The keys I could manage.
i.e. In the case of the input select above I'd want the following output
data.frame(widgets=c("Widget B","Widget A"),quantities=c(600,400))
Any ideas or pointers?
My code is here:
library(shiny)
widget_list<-c("WidgetA","WidgetB","WidgetC","WidgetD","WidgetE")
ui <- fluidPage(title = "Test Case for Dynamic Inputs",
headerPanel(h3("Test Case for Dynamic Inputs")),
sidebarPanel(width = 3,
selectInput("widgets","Select Widgets",choices=widget_list,multiple = TRUE)
),
sidebarPanel(title="Scoring Outputs",width = 3,
h3(textOutput("title"))
),
sidebarPanel(title="Dynamic UI",width=3,
uiOutput("widget_quantities"),
h4(tableOutput("output_table"),style="color: red")
)
)
server <- function(input, output) {
output$title<-renderText("Test dynamic inputs")
fn_run<-reactive({
count_widgets(input$widgets)
})
len_widgets<-reactive({
length(input$widgets)
})
output$output_table<-renderTable(data.frame(widgets=input$widgets,quantities=rep(999,len_widgets())))
output$widget_quantities <- renderUI({
code<-list()
for( item in input$widgets)
{
inp_name<-paste("inp",item,sep = "_")
inp_display_name<-paste("Quantity of",item,sep = " ")
code<-list(code,numericInput(inp_name, inp_display_name,value=300))
}
return(code)
})
}
count_widgets<-function(inp=c())
{
return(length(inp))
}
shinyApp(ui = ui, server = server)
Probably you want this:
You could make it cleaner not hardcoding all the widgets, but i think you can adapt it from here
also the quantities will reset when you update your widgets, but my Lunchbreak is over :D Let me know if you can solve that, the basic question should be answered.
If you need any docu let me know, I can add it later.
library(shiny)
x <- data.frame()
widget_list = c("Widget_A","Widget_B","Widget_C","Widget_D","Widget_E")
ui <- fluidPage(title = "Test Case for Dynamic Inputs",
headerPanel(h3("Test Case for Dynamic Inputs")),
sidebarPanel(width = 3,
selectInput("widgets","Select Widgets",choices=widget_list,multiple = TRUE)
),
sidebarPanel(title="Scoring Outputs",width = 3,
h3(textOutput("title"))
),
sidebarPanel(title="Dynamic UI",width=3,
uiOutput("widget_quantities"),
h4(tableOutput("output_table"),style="color: red")
)
)
server <- function(input, output) {
global <- reactiveValues(Widget_A = 300, Widget_B = 300, Widget_C = 300, Widget_D = 300, Widget_E = 300)
output$title<-renderText("Test dynamic inputs")
fn_run<-reactive({
count_widgets(input$widgets)
})
observe({
for(item in input$widgets){
global[[item]] <- input[[paste("inp",item,sep = "_")]]
}
})
output$output_table<-renderTable({
data.frame(global$Widget_A, global$Widget_B, global$Widget_C, global$Widget_D, global$Widget_E)
})
output$widget_quantities <- renderUI({
code<-list()
for( item in input$widgets)
{
inp_name<-paste("inp",item,sep = "_")
inp_display_name<-paste("Quantity of",item,sep = " ")
code<-list(code,numericInput(inp_name, inp_display_name,value=300))
}
return(code)
})
}
count_widgets<-function(inp=c())
{
return(length(inp))
}
shinyApp(ui = ui, server = server)
So I haven't gotten the final solution yet but thought I'd post some progress I made so far as a partial answer in case it clarifies what I'm trying to do.
https://statspot.shinyapps.io/app_upload_test_v2/
This new version now sets the right data frame (and hence prints the right table) which takes the values (quantities) from the input dynamically instead of static 999 as before).
output$output_table<-renderTable(data.frame(widgets=input$widgets,quantities=rep(input[["inp_WidgetA"]],len_widgets())))
Only flaw is that it is hardcoded to regurgitate whatever is entered as the quantity for Widget A.
What I'd like is to programatically have it loop and do this for whatever widgets are entered by the user.
Ideas?
In my code I plot a function and then raise a dialog to ask the user about it. The issue is that the dialog is raised before the plot is updated, so the user sees whatever was last in the plot window (I'm using R-studio). How can I solve this?
Example:
for(i in (1:3)) {
x = (1:100); #not the real vectors...just an example
f = (601:700);
plot(x,f)
ans = winDialog(type = c("yesno"), "is it any good?")
}
Searching for "R flush plot" led me to this solution. At least on my system I had to replace Sys.sleep(0) by Sys.sleep(1). But then it works.
for(i in (1:3)) {
x = (1:100); #not the real vectors...just an example
f = x^i
plot(x,f)
Sys.sleep(1)
ans = winDialog(type = c("yesno"), "is it any good?")
}
Consider either function which (for rstudio) will open something in the viewer if y = TRUE and in your browser if y = FALSE. You can force the whatever to open in your browser via options(viewer = NULL) (and then you need to reset to before), but I can't get this to work inside functions using the normal on.exit approach. Tested on windows and osx.
f <- function(x, y = TRUE) {
if (y) {
oo <- getOption('viewer')
on.exit(options(viewer = oo))
options(viewer = NULL)
} else options(viewer = NULL)
print(getOption('viewer'))
DT::datatable(x)
}
g <- function(x, y = TRUE) {
if (y) {
oo <- getOption('viewer')
on.exit(options(viewer = oo))
options(viewer = NULL)
} else options(viewer = NULL)
print(getOption('viewer'))
htmlTable::htmlTable(x)
}
## in rstudio, returns the viewer function
getOption('viewer')
# function (url, height = NULL)
# ...
## opens in viewer despite `options(viewer = NULL)`
g(mtcars)
# NULL
## again returns the function, ie, reset my options to before g call successfully
getOption('viewer')
# function (url, height = NULL)
# ...
## opens in browser but leaves `options(viewer = NULL)` after exiting
g(mtcars, FALSE)
# NULL
getOption('viewer')
# NULL
It seems like the viewer isn't respecting my options within the function environment with either just some html (g) or a widget (f). I thought both would use viewer = NULL inside the function and return my options the way they were upon exiting so that I can control where I want to view the result.
Or is there a better way of doing this for both html and widgets? I have tried the options argument in DT::datatable to no avail, but this wouldn't help for the htmlTable::htmlTable case.
The only other approach I can think of is to write all the code to a temp file and use if (rstudio) rstudio::viewer(tempfile) else browseURL(tempfile) which I think is a lot of work for something seemingly so straight-forward.
Although this isn't a fix, I think it illustrates what's going on. Try adding a Sys.sleep() call in the on.exit() handler:
f <- function(x) {
viewer <- getOption("viewer")
on.exit({
print("Restoring viewer...")
Sys.sleep(3)
options(viewer = viewer)
}, add = TRUE)
options(viewer = NULL)
DT::datatable(x)
}
## opens in viewer despite `options(viewer = NULL)`
f(mtcars)
You'll notice that RStudio doesn't 'decide' what to do with the result of DT::datatable() call until after the on.exit() handler has finished execution. This means that, by the time RStudio wants to figure out to do with the result, the viewer has already been restored! Odds are, RStudio waits until R is no longer 'busy' to decide how to display the resulting content, and by then is too late for temporary changes to the viewer option.
Note that this doesn't explain the htmlTable behaviour. My best guess is that there is some kind of race condition going on; the lost viewer option seems to go away with strategically placed Sys.sleep() calls...
Unfortunately, working around this means avoiding the use of on.exit() call -- unless we can figure out to handle this in RStudio, of course.
Here's one way you could get this functionality by writing the code to a temporary file and using browseURL or whatever you like.
The gist of both f and g are the same, so you could have one function to handle any type of html code or widget I suppose. And probably widgets need to be selfcontained = TRUE.
f <- function(x, y = TRUE) {
x <- if ((inherits(x, 'iplot'))) x else DT::datatable(x)
if (!y) {
htmlFile <- tempfile(fileext = '.html')
htmlwidgets::saveWidget(x, htmlFile, selfcontained = TRUE)
utils::browseURL(htmlFile)
} else x
}
g <- function(x, y = TRUE) {
x <- htmlTable::htmlTable(x)
if (!y) {
htmlFile <- tempfile(fileext = '.html')
writeLines(x, con = htmlFile)
utils::browseURL(htmlFile)
} else x
}
## opens in viewer
g(mtcars)
## opens in browser
g(mtcars, FALSE)
## same for widgets
f(mtcars)
f(mtcars, FALSE)
f(qtlcharts::iplot(1:5, 1:5), FALSE)
## and my options haven't changed
getOption('viewer')
# function (url, height = NULL)
# ...
Side note that this is actually the proper way to have htmlTable::htmlTable use a different viewer, but g should work for any html.
library('htmlTable')
print(htmlTable(mtcars), useViewer = utils::browseURL)