``` {r echo=FALSE, results="asis"}

Nothing to see here ... move along

require("questionr", quietly=TRUE) page <- questionr:::Page$new() nav <- questionr:::NavBar$new() cat(page$write_header())

`r I(nav$write_header("gWidgetsWWW2.rapache", "Programming web pages with the gWidgets API"))`

`r I(nav$add("About"))`

The `gWidgetsWWW2.rapache` package implements most of the `gWidgets`
API to program interactive web pages with pure `R` code.

The `gWidgets` API is a simple-to-learn set of constructors and
methods that make easy the specifying of widgets for a GUI, their
layout and their interactions. The API is implemented, to varying
degrees, for use with the `RGtk2`, `tcltk` and `qtbase` packages to
make desktop GUIs. This package provides the ability to serve such
interfacess with the `apache` web server through Jeffrey Horner's `rapache`
module linking `R` with `apache.

Implementing the API for web programming took some iterations. The
`gWidgetsWWW` package (documented in the JSS article
http://www.jstatsoft.org/v49/i10/) was the first start. That package
was rewritten to take advantage of the `Rook` package interfacing with
`R`'s httpd server and incorporated reference classes instead of the
`proto` package. The result was `gWidgetsWWW2`. With that package,
serving pages locally through `Rook` works fine, but the package did
not play nicely with `rapache` to serve pages remotely. (One could use
`Rook` for this, but that method doesn't scale well.)

This package, `gWidgetsWWW2.rapache`, then is a rewrite of the
plumbing to serve pages through apache and a streamlining of some of
the functionality. The main technical issue involved having the
callbacks in R share their environment amongst potentially many `R`
processes spawned by `apache`. The solution used was to work to make
these environments small enough that they can be
serialized/unserialized to disk in a reasonable amount of time.

`r I(nav$add("Basic usage"))`

Once installed (see below) the use of the package is fairly
simple. One places a script into the appropriate directory. The name
of this script then maps to the URL that will call the script. A
simple "hello-world" script might look like:

w <- gwindow('Hello world title') g <- gvbox(cont=w) b <- gbutton('Click me world', cont=g) addHandlerChanged(b, function(h,...) { galert('Hello world!') })

The above script demonstrates the main points of the package:

* A toplevel window maps to a web page (`gwindow`)

* Containers are used to organize the layout (`gvbox` is a vertical
  box container)

* Controls are used to give the user a place to interact with
  (`gbutton`)

* dialogs can be called (`galert`)

* interactivity is created by adding a handler
  (`addHandlerChanged`). A handler, or callback, is an `R` function
  that is evaluated in an event driven manner. In this case, the
  "changed" event for a button object is triggered when the button is
  clicked. The callback above produces some JavaScript that causes a
  message to flash briefly on the screen.

If this script is saved as `test.R` in the proper directory, then the
page will load through a url such as
http://www.youraddress.com/gw/test.R .

There are more examples in the `examples` directory of the
package. A default installation has these accessible out of the box.


`r I(nav$add("Components", "containers and controls"))`

<h3>Containers</h3>

Containers are used to organize components (controls or other
containers). Unlike some GUI toolkits, the layout of child components
is integrated into the specification of the widget. The widget
constructors have a `container` argument where the parent is
specified. (E.g., above we had `gvbox(cont=w)` to next the vertical
box container inside the top-level window.) `Layout arguments are
given at the time of construction, such as `expand` or `fill`.

The package implements most -- but not all -- of the containers in the
`gWidgets` API:

* box containers, which pack children in left to right (horizontal) or
  top to bottom (vertical): `ggroup`, `gvbox` (a vertical box),
  `gframe` (a decorated box) and `gexpandgroup` (a box with a
  disclosure icon to hide or show the contents).

* a page layout container `gborderlayout` for placing widgets at the
  center or any of the four compass directions. These panels may have
  optional titles and disclosure buttons.

* a notebook container (`gnotebook`) and the card container
  (`gstackwidget`) for displaying multiple pages one at a time

* a container for forms `gformlayout` that holds labels and controls
  in an neatly organized manner.

Not implemented are the table layout container (`glayout`) and the
paned container (`gpanedgroup`). The `gformlayout` container covers
most uses of the former and the latter can be implemented with
`gborderlayout` (though that container does not like to be nested
within other containers.)

Containers have just a few methods:

* `visible<-` can be used to adjust if the components are visible

* `delete` can be used to remove a child component. Use `add` to
  restore.


<h3>Controls</h3>

The `gWidgets` API has several controls specified. A control allows
the user to interact with the GUI or the programmer to display
information for the user. Most controls have some event that will
trigger a callback, if specified. For most controls, this event is
clear and is identified through the generic `addHandlerChanged`. As
seen above, for a button this is the clicking of the button. A few
controls have more than one event they can respond. For these, there
are other `addHandlerXXX` methods.


The basic controls are:

* labeling controls: `glabel`, `gstatusbar`, `ghtml`, `gseparator`

* action controls: `gbutton`, `gmenu` (but there is no
  `gtoolbar`). These can be configured with sharable `gaction`
  instances.

* text controls: `gedit` (single line) and `gtext` (multi-line)

* selection controls: `gcheckbox`, `gradio`, `gcheckboxgroup`,
  `gcombobox`, `gtable` (with `selection="checkbox"`), `gslider`,
  `gspinbutton`

* table displays: `gtable` (`gdf` is not (yet) implemented in
  `gWidgetsWWW2.rapache` but is in `gWidgetsWWW2`)

* graphics displays: `gimage`, `gcanvas`, `gsvg`

* `gpanel` for incorporating external JavaScript libraries

* `gfile` for uploading files

Implemented in `gWidgetsWWW2` but not (yet) in `gWidgetsWWW2.rapache`
are `gtree`, `ggooglemaps`, and `gdf`.


The main widget methods are

* `svalue` and `svalue<-` for getting and setting the widget's main property

* `[` and `[<-` for accessing the items that selection can occur from
  (for the selection widgets). For such widgets, the S3 methods
  `names`, `length` and `dim` are typically implemented.

* `enabled<-` to change if a widget is sensitive to user input

* `visible<-` to change some visibility property of the widget. (There
  are many overloads, but the default is to specify if a widget is
  shown or hidden. One overload is for `gtable` objects, where this
  method is use to show or hide rows of the displayed data.)

* `tooltip<-` to specify a tooltip when the mouse hovers over the widget.

`r I(nav$add("Handlers"))`


In addition to user-defined callbacks, all widgets with a state have a
transport handler defined for it that copies changes in the GUI back
to the `R` process. As such, these callbacks happen often. Each callback
requires the handling `R` process to unserialize an environment
containing the scope of the callback, call the handler, then serialize
the environment so it can be shared with other processes. This should
happen quickly. To make this the case, the environment must be kept
small. (In `gWidgetsWWW2` it was seen that environments with reference
classes do not serialize small and so this took far too long.) To
ensure that a few compromises are needed:

* you should avoid reference classes

* you will need to load packages in the handler call. Packages
  attached during a script (not loaded when `rapache` is) are not
  recorded or restored when the serialization takes place.

By employing these compromises, the response time for handling an
event was kept down to between 30 and 100ms. Not great, but
acceptable. We don't try to track keystrokes in `gedit` or all updates
for the `gslider` widget, but otherwise, most things work with this
response time.

<h3>gtimer</h3>

The `gtimer` constructor can be used to call a handler after some
prescribed period of time. The call can be repeated (the default) or
be `one.shot`. This can be useful, say, if a long running process is
implemented. The user can interact with multiple R processes, so even
if one is tied up on a long computation, the interface should still be
usable. The `gtimer` constructor should be able to poll until the
process is complete to carry back the results to the browser.

The `manipulate` example shows one use of `gtimer` that unfortunately
has no better idiom within this framework. That example has several
combo boxes. The value of a combobox is only available after the data
is loaded. This is done asynchronously. As such, the initial graphic
can not be made until after some unknow period. Unfortunately, there
is no hook (in `gWidgetsWWW2.rapache`) to defer the graphic call to
happen until after that event. Instead, we just use a one-shot timer
with a heuristically chosen delay to pause before making the call to
create the initial graphic.

`r I(nav$add("Graphics"))`

The display of graphics has several different options:

* one can display images created through the `png` driver, say, with
  the `gimage` widget

* one can display svg files created with the `svg` driver (or others)
  with the `gsvg` widget

* one can use the `canvas` package to display graphics through
  `gcanvas`.

The `gcanvas` solution is best for quickly refreshing a graph produced
with R's base graphics. It does not work with lattice graphics or
`ggplot2`. One can do low level commands on the JavaScript `canvas`
object.

The `gsvg` solution is arguably the best looking, but flickers when
the graphic is refreshed making it not such a great choice for
interactive graphics.

The `gimage` solution looks fine and is nearly as responsive as
`gcanvas`.

There are a few examples in the `examples` directory including an
implementation of RStudio's `manipulate` package which shows how all
three can be used.

`r I(nav$add("Data","uploads"))`

The `gfile` constructor can be used to allow a user to upload data to
the server. A cap on the size of the upload can be specified when
`rapache` is configured.

The widget calls any `upload` callback when the data is successfully
uploaded. The example in the `examples` directory shows how
`gstackwidget` can be used to open a new page once the data is
uploaded. Otherwise, the callback can update portions of the current
GUI to reflect the change in state.

The uploaded file and its name are passed back to the callback via the
`svalue` method (the path of the uploaded file) and `[` (the original
name).

`r I(nav$add("Persistence"))`

Each time a page is loaded (even when refreshed) a new environment is
made for evaluating any callbacks. As such, the data within a page is
not persistent across page loads unless it is programmed to be so. One
can write to the file system or data base to keep values across
sessions. (The package uses a `redis` server for this purpose.)

`r I(nav$add("Start up"))`

The initial web page is constructed through various stages:

* a basic template is loaded

* some large `JavaScript` libraries are loaded from CDNs. These will
  be slow the first time, but should be cached so subsequent reloads
  are not lengthy

* Then a unique session ID is fetched from the server

* Then the `JavaScript` that creates the page layout is fetched from
  the server

* Finally the browser uses this `JavaScript` to layout out the page.

Of these, the time of the last two steps depends on the complexity of
the GUI design. In basic testing with a modest machine, each
additional widget takes about 40ms to load with a base initialization
of around 100ms (40ms is a minimum, more complex widgets will take
longer). So a not-so-complex interface with 20 or so widgets will take
around 1 second or more to fetch from the server. It may take another
second for the browser to actually process the `JavaScript`. Once
loaded the pages are fairly responsive, but the initial loading can
seem somewhat slow. A loading mask it employed to indicate that
activity is taking place.


`r I(nav$add("JavaScript"))`

The package uses the `ExtJS` libraries provided by `sencha.com`. These
are widely used in commercial and GPL projects. There may be some
restrictions on their use in your project, there may not be. I don't
know and don't want to. 

`JavaScript` is a language that can be used to manipulate a web page
in numerous ways. The package makes no assumption that an R user knows
`JavaScript`. Rather, its novelty is to make it easy for non-web
programmers to create web pages. That being said, there are a few ways
one with `JavaScript` knowledge can integrate that into the framework.

The `ExtJS` API is very rich and far more extensive than that provided
by `gWidgets`. The function `call_ext` can be used to call a method on
an object not available through the base interface.

Each constructor has an argument `ext.args` for passing in extra
arguments to the `ExtJS` constructor. This is specified with a list
that is converted to a `JavaScript` object through the function
`list_to_object`.

When a callback is run, the package creates `JavaScript` to pass back
to the browser. So a command like `svalue(obj) <- "some value"` will
produce the appropriate `JavaScript`. Each command has its
`JavaScript` added to a queue. One can add their own `JavaScript`
commands to the queue via the `push_queue` function. For example, to
use the browsers alert dialog add this to a handler

push_queue("alert('hello world')")

Finally, the `gpanel` widget does nothing more than make a `DIV` tag
on the page to be filled in by `JavaScript`. The widget allows one to
load some external `JavaScript` code asynchronously and call a handler
once loaded.



`r I(nav$add("Security"))`

Web security is a big deal. There is the potential for malicious
intent on the part of a user. This package provides no additional
security features, but standard web safety guidelines should be
followed. (E.g., don't eval code that a user can upload, ...) With
the advent of cloud-based servers, you might want to deploy your
server in a stand-alone manner.

`r I(nav$add("Debugging"))`

Debugging scripts is made more difficult, as the scripts must be
processed through the browser. Here are some useful tricks:

* A test setup is useful where a local `apache` server is used. (This
  isn't helpful for windows users). Most -- but not all -- scripts can
  be tested locally with `gWidgetsWWW2` under `Rook`. There are a few
  differences in the API though, so this won't be fool proofed.

* Errors are logged in apache's log file

* `print` statements do not appear in the log file, but those
  generated with `message` appear in apache's log file. This can be
  useful to track down issues with the script.

* There may be errors in `gWidgetsWWW2.rapache` that are `JavaScript`
  errors. Of course, the package author would love to hear about this,
  so they can be fixed. But how can the end user tell? Well, a
  `JavaScript` console can be used. The main browsers include these:
  Chrome and Safari have built in ones available through the menu bar,
  Firefox has the excellent `firebug` add on http://getfirebug.com/ .

`r I(nav$add("Installation:","rapache configuration"))`

Installing this software requires a few steps:

* the package needs to be installed in such a way that the R process
  spawned by the web server can see the package (i.e., do not use a
  local directory)

require(devtools) install_github("gWidgetsWWW2.rapache", "jverzani")

* rapache must be installed (follow the instructions here
  http://www.rapache.net).

* rapache must be configured. There are 2-3 steps:

- The `mod_R.so` module must be loaded. Likely this is already done,
  but if not you must add this to your apache configuration:

LoadModule R_module /usr/libexec/apache2/mod_R.so

  The location of `mod_R.so` is system dependent, the above is a Mac
  OS X installation point.

- Some basic customization must be made. The defaults are installed
  through the apache directive:

RSourceOnStartup 'system.file("rapache", startup.R", package="gWidgetsWWW2.rapache")'

Alternatively, one can copy that file to the file system and modify
it. The option `gWidgetsWWW2.rapache::script_base` is the most
important, as this specifies where to look for files.

- The following `Location` directive is given defining the urls for your scripts:

LimitRequestBody 1024000 SetHandler r-handler ## from system.file("rapache", "www2.R", package="gWidgetsWWW2.rapache") RFileHandler /Library/Frameworks/R.framework/Versions/2.15/Resources/library/gWidgetsWWW2.rapache/rapache/www2.R ```

The above uses the base url http://your.address.com/gw/ for accessing scripts. (So the url http://your.address.com/gw/ex.R causes a search for the file ex.R in the directory (directories) specified through the option gWidgetsWWW2.rapache::script_base.

The LimitRequestBody limits the size of file uploads. A value of 0 is use to indicate no limit.

The path of the www2.R script comes from invoking system.file with the appropriate commands, as indicated in the comment. Alternatively, this script can be copied and modified should there be a need.

One could add to the above apache configuration, say restricting access using one of apache's modules.

That's it. Not much harder than installing rapache itself. With the default installation, the examples directory from the package is where URLs will be mapped to. Going to http://your.address.com/gw/ will open a page with links.

r I(nav$write_footer()) r I(page$write_footer())



jverzani/gWidgetsWWW2.rapache documentation built on May 20, 2019, 5:19 a.m.