Behind the Scenes

Tom Elliott

satRdays Auckland 2020

Part I

What is iNZight, and who uses it?

(Briefly, for those who missed Chris’ talk …)

library(iNZight)
iNZight()

iNZight main window

  • package!
  • GUI for visually exploring data
  • easy-to-learn tool for students/beginners …
  • … but also non-beginners!

  • package!
  • GUI for visually exploring data
  • easy-to-learn tool for students/beginners …
  • … but also non-beginners!
  • bridge between GUI and coding

Data

GUI

Explore

Save script

Part II

The development of iNZight

Chris Wild

UoA statistics students

gWidgets2 + gWidgets2RGtk2 RGtk2 GTK

† John Verzani ‡ Michael Lawrence

Started off small

  • a feature here …
  • a module there …

… et voilà: a rewrite was needed!

What makes Version 2+ better?

  • Object Oriented Programming (OOP)
  • Separability
  • Reactivity
  • Extensibility

Object Oriented Programming

  • each component of the UI is represented by a class

iNZight main window

iNZight main window

Object Oriented Programming

  • each component of the UI is represented by a class
  • classes have properties (“fields”) and methods
  • Reference Classes (RC) and R6

LoadDataWindow <- setRefClass("LoadDataWindow",
    fields = list(
        filename = "character"
    ),
    methods = list(
        initialize = function(...) {
            w <- gwindow("Load data")
            g <- ggroup(horizontal = FALSE, container = w)

            # Get filename from user
            fileedit <- gedit(container = g)

            # Load data
            loadbtn <- gbutton("Load", container = g)

            addHandlerKeystroke(fileedit,
                handler = function(h, ...) {
                    filename <<- svalue(h$obj)
                }
            )

            addHandlerClicked(loadbtn, function(h, ...) loadData())
        },
        loadData = function() {
            data <- read.csv(filename)
        }
    )
)

Demo Load Window

Separability

  • keep UI and data logic separate
  • Desktop and Shiny versions use the same data logic
  • UI inputs control arguments to a single function
  • existing or new wrapper for another package

LoadDataWindow <- setRefClass("LoadDataWindow",
    fields = list(
        filename = "character"
    ),
    methods = list(
        initialize = function() { ... },
        loadData = function() {
            data <- read.csv(filename)
        }
    )
)

LoadDataWindow <- setRefClass("LoadDataWindow",
    fields = list(
        filename = "character",
        filetype = "character",
        delimiter = "character", # csv can use , or ;
        ...
    ),
    methods = list(
        initialize = function(...),
        loadData = function() {
            if (filetype == "csv") {
                data <- read.csv(filename, delimiter = delimiter, ...)
            } else if (filetype == "excel") {
                data <- readxl::read_xls(filename, ...)
            } else ...
        }
    )
)

LoadDataWindow <- setRefClass("LoadDataWindow",
    fields = list(
        filename = "character",
        filetype = "character",
        delimiter = "character", # csv can use , or ;
        ...
    ),
    methods = list(
        initialize = function(...),
        loadData = function() {
            data <- iNZightTools::smart_read(filename,
                filetype = filetype,
                delim = delimiter,
                ...
            )
        }
    )
)

LoadDataWindow <- setRefClass("LoadDataWindow",
    fields = list(
        filename = "character",
        filetype = "character",
        delimiter = "character", # csv can use , or ;
        ...
    ),
    methods = list(
        initialize(...),
        loadData = function() {
            data <- iNZightTools::smart_read(filename,
                filetype = filetype,
                delim = delimiter,
                ...
            )
            cat(iNZightTools::code(data), file = "script.R")
        }
    )
)
# script.R
data <- readr::read_csv("data.csv", delim = ";", comment = "#")

LoadDataWindow <- setRefClass("LoadDataWindow",
    fields = list(
        filename = "character",
        filetype = "character",
        delimiter = "character", # csv can use , or ;
        ...
    ),
    methods = list(
        initialize(...),
        loadData = function() {
            data <- iNZightTools::smart_read(filename,
                filetype = filetype,
                delim = delimiter,
                ...
            )
            cat(iNZightTools::code(data), file = "script.R")
        }
    )
)
# script.R
data <- readxl::read_excel("data.xlsx")

Reactivity

  • Instant feedback
  • Makes exploring data faster and easier
  • Widget handlers
    Change, keystroke, click, mouseover, focus, …

Demo <- setRefClass("Demo",
    field = list(
        data = "data.frame",
        colour = "character"
    ),
    methods = list(
        initialize = function() {
            w <- gwindow("Demo")
            g <- ggroup(horizontal = FALSE, container = w)
            col <- gcombobox(
                c("Orangered", "Goldenrod", "Steelblue"),
                container = g,
                handler = function(h, ...) {
                    colour <<- svalue(h$obj)
                    draw()
                }
            )

            data <<- data.frame(x = rnorm(100), y = rnorm(100))
            colour <<- "Orangered"

            draw()
        },
        draw = function() {
            plot(y ~ x,
                data = data,
                col = colour
            )
        }
    )
)

Extensibility

  • Foresight and planning
  • Add new or change existing features …
  • … without rewriting everything!
  • OOP: add new classes, inheritance, …
  • Separability: new field = new argument
    everything else unchanged
  • In progress: a system for adding completely new modules

# DemoModule.R
DemoModule <- setRefClass(
    "Demo Module",
    contains = "CustomModule",
    fields = list(
        GUI = "ANY",
        ...
    ),
    methods = list(
        initialize = function(gui, name) {
            callSuper(gui,
                name = name,
                embedded = TRUE
            )
            ...
        },
        ...
    )
)

path/to/iNZightVIT/modules

  • DemoModule.R

iNZight main window

path/to/iNZightVIT/modules

  • DemoModule.R

iNZight main window

Thank you

Slides: bit.ly/iNZightSatRday