Skip to content

Commit 14dcea2

Browse files
committed
First working version
1 parent 9e6a65f commit 14dcea2

24 files changed

+447
-243
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ Description: Shiny is a great package. Yet, for more complex
1010
its reactivity paradigm. This package wraps shiny
1111
into more traditional event handlers. Not yet working.
1212
License: GPL >= 2.0
13-
Depends: stringtools, shiny, restorepoint
13+
Depends: shiny, restorepoint

R/addHandler.r

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Manually trigger shiny events when some value changes
2+
# While this is also the key idea of reactive programming
3+
# Shiny (at least in current versions as of October 2014)
4+
# tends to trigger events too often.
5+
6+
# What we need:
7+
# 1. Call a function or render output when a button is clicked
8+
# 2. Call a function or render output when an input value has changed
9+
# 3. Update input values when an input value or variable has changed without triggering further events
10+
11+
12+
resetEventHandlers = function(app = getApp()) {
13+
app$values=list()
14+
}
15+
16+
addEventHandlersToSession = function(handlers, session.env=app$session.env, app=getApp()) {
17+
for (el in handlers) {
18+
call = el$call
19+
eval(call, session.env)
20+
}
21+
}
22+
23+
addEventHandlerToApp = function(id, call, type="unknown", app = getApp(),session.env=app$session.env) {
24+
n = length(app$handlers)+1
25+
app$handlers[[n]] = list(id=id, call=call, type=type)
26+
names(app$handlers)[n] <- id
27+
if (app$is.running) {
28+
eval(call,app$session.env)
29+
}
30+
}
31+
32+
#' Add an handler to an input that is called when the input value changes
33+
#'
34+
#' @param id name of the input element
35+
#' @param fun function that will be called if the input value changes. The function will be called with the arguments: 'id', 'value' and 'session'. One can assign the same handler functions to several input elements.
36+
addChangeHandler = function(id, fun,...,app=getApp(), on.create=FALSE) {
37+
#browser()
38+
fun = substitute(fun)
39+
# Create dynamic observer
40+
args = list(...)
41+
ca = substitute(env=list(s_id=id, s_fun=fun,s_args=args, s_on.create=on.create),
42+
observe({
43+
display("called event handler for ",s_id)
44+
input[[s_id]]
45+
if (hasWidgetValueChanged(s_id, input[[s_id]], on.create=s_on.create)) {
46+
display("run event handler for ",s_id)
47+
do.call(s_fun, c(list(id=s_id, value=input[[s_id]], session=session),s_args))
48+
}
49+
})
50+
)
51+
addEventHandlerToApp(id=id,call=ca,type="change",app=app)
52+
}
53+
54+
55+
#' Add an handler to a button
56+
#'
57+
#' @param id name of the button
58+
#' @param fun function that will be called if button is pressed. The function will be called with the arguments: 'id', 'value' and 'session'. One can assign the same handler functions to several buttons.
59+
addButtonHandler = function(id, fun,..., app = getApp()) {
60+
61+
if (app$verbose)
62+
display("\naddButtonHandler('",id,'",...)')
63+
64+
fun = substitute(fun)
65+
args = list(...)
66+
67+
ca = substitute(env=list(s_id=id, s_fun=fun,s_args=args),
68+
observe({
69+
if (hasButtonCounterIncreased(s_id, input[[s_id]])) {
70+
display(s_id, " has been clicked...")
71+
do.call(s_fun, c(list(id=s_id, value=input[[s_id]], session=session),s_args))
72+
}
73+
})
74+
)
75+
addEventHandlerToApp(id=id,call=ca,type="button",app=app)
76+
}
77+
78+
79+
#' Checks whether the value of an input item has been changed (internal function)
80+
hasWidgetValueChanged = function(id, new.value,on.create=FALSE, app = getApp()) {
81+
restore.point("hasWidgetValueChanged")
82+
if (!id %in% names(app$values)) {
83+
app$values[[id]] = new.value
84+
changed = on.create
85+
} else {
86+
changed = !identical(app$values[[id]],new.value)
87+
if (changed) {
88+
app$values[[id]] = new.value
89+
}
90+
}
91+
return(changed)
92+
}
93+
94+
#' Checks whether a button has been pressed again (internal function)
95+
hasButtonCounterIncreased = function(id, counter, app=getApp()) {
96+
restore.point("hasButtonCounterIncreased")
97+
if (isTRUE(counter == 0) | is.null(counter) | isTRUE(counter<=app$values[[id]])) {
98+
app$values[[id]] = counter
99+
cat("\nno counter increase: ", id, " ",counter)
100+
return(FALSE)
101+
}
102+
app$values[[id]] = counter
103+
cat("\ncounter has increased: ", id, " ",counter)
104+
return(TRUE)
105+
}

R/add_handler.r

-91
This file was deleted.

R/app.r

+29-21
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,38 @@
88
# 2. Call a function or render output when an input value has changed
99
# 3. Update input values when an input value or variable has changed without triggering further events
1010

11-
events.env = new.env()
11+
.SHINY.EVENTS.ENV = new.env()
1212

13-
shiny.app = function() {
13+
#' Generate an empty shiny events app
14+
eventsApp = function(set.app=FALSE, verbose=FALSE) {
1415
app = new.env()
1516
app$is.running = FALSE
1617
app$handlers = list()
1718
app$values = list()
1819
app$run.event.handlers=FALSE
1920
app$do.update = list()
21+
app$verbose=TRUE
2022
app$server = function(session, input, output) {
21-
app = get.app()
22-
set.app.session(session,app)
23-
add.handlers.to.session(app$handlers, app)
24-
#add.renderer.to.session(app$renderer, app)
23+
app = getApp()
24+
setAppSession(session,app)
25+
addEventHandlersToSession(app$handlers, app)
2526
}
27+
if (set.app)
28+
setApp(app)
2629
app
2730
}
2831

29-
set.app = function(app) {
30-
events.env$app = app
32+
#' set the current app
33+
setApp = function(app) {
34+
.SHINY.EVENTS.ENV$app = app
3135
}
3236

33-
get.app = function() {
34-
events.env$app
37+
#' get the current app
38+
getApp = function() {
39+
.SHINY.EVENTS.ENV$app
3540
}
3641

37-
set.app.session = function(session, app=get.app()) {
42+
setAppSession = function(session, app=getApp()) {
3843
app$session = session
3944
app$input = session$input
4045
app$output = session$output
@@ -47,19 +52,22 @@ set.app.session = function(session, app=get.app()) {
4752
app$session.env = session.env
4853
}
4954

50-
run.app = function(app=get.app(),...) {
51-
#add.ui.renderer(app=app)
52-
runApp(list(ui=app$ui, server=app$server),...)
55+
#' set the main ui object for the app
56+
setAppUI = function(ui, app=getApp()) {
57+
app$ui = ui
5358
}
5459

55-
display = function(...) {
60+
#' run shiny events app
61+
runEventsApp = function(app=getApp(),ui=NULL,...) {
62+
#add.ui.renderer(app=app)
63+
if (!is.null(ui))
64+
setAppUI(ui=ui, app=app)
5665

66+
app$is.running = TRUE
67+
runApp(list(ui=app$ui, server=app$server),...)
68+
#app$is.running = FALSE
5769
}
5870

59-
updater.exists = function(id, app=get.app()) {
60-
id %in% names(app$do.update)
61-
}
62-
63-
perform.update = function(id, app=get.app()) {
64-
app$do.update[[id]]$counter = isolate(app$do.update[[id]]$counter+1)
71+
display = function(...) {
72+
cat(...)
6573
}

0 commit comments

Comments
 (0)