0

I recently started using R and find myself blocked while trying to build a graphical user interface.

My aim is to code a GUI for the conduction of a survey. Basically, I would like to open a window composed of a question, an answer and a Next button. Upon cliking on Next I wish to save the answer and jump to the next window/question.


As I had problems opening several windows I looked here and found it was possible to open windows one after the other with the help of a handler:

addHandlerChanged() #upon the Next button

So I tried to input a code (see below) for two questions/windows. I thought to save the results in to_return: a matrix 2 rows (1, 2) and 3 columns (Question number, Question, Answer). [I am now trying to write answers question after question in an excel file]

My problem is as follows:

  1. I fail to close the first window after opening the second (I tried adding dispose(h$obj) or visible(win1)=FALSE to the the first windows button handler but it fails)

  2. I fail to "apend" data to my matrix (the to_return matrix is renewed with every window)

# calling GUI library
library(gWidgets)
options(guiToolkit="tcltk")

Q1 <- function(){
  # creating first window
  win1 <- gwindow("I) Q1.", visible=TRUE)
  group <- ggroup(horizontal = FALSE, container=win1)
  # creating question
  question <- glabel("Do you have a driving license?", container = group)
  # creating answer
  answer <- gradio(c("Yes","No"), container=group)
  # creating next button
  nextQuestion <- gbutton("Next",container=group)
  # handler
  addHandlerChanged(nextQuestion, handler = function(h, ...) {
    # answer to save in matrix
    to_return <- rbind(to_return,c(svalue(win1),svalue(question),svalue(answer)))
    #opening next question
    Q2()
    print(to_return)
  } )
}


Q2<- function(){
  # creating second window
  win2 <- gwindow("I) Q2.", visible=TRUE)
  group <- ggroup(horizontal = FALSE, container=win2)
  # creating question
  question <- glabel("What is your gender?", container = group)
  # creating answer
  answer <- gradio(c("Female","Male"), container=group)
  # finish button
  nextQuestion <- gbutton("Finish",container=group, handler = function(h,...) {
    # answer to save in matrix
    to_return <- rbind(to_return,c(svalue(win2),svalue(question),svalue(answer)))
    print(to_return)
    # finish and close
    dispose(h$obj)
  })
}

If I run Q1() I get the following results where to_return doens't keep the data...

     [,1]     [,2]                             [,3] 
[1,] "I) Q1." "Do you have a driving license?" "Yes"
     [,1]     [,2]                   [,3]    
[1,] "I) Q2." "What is your gender?" "Female"

Any help or insight will be greatly appreciated!

Thank you!

Octave


EDIT : Following the answer of jverzani, here is a possible code for two questions with data saving in csv file.

## calling GUI library
library(gWidgets)
options(guiToolkit="tcltk")

setwd("Your\\Path\\Here")

w <- gwindow(title="Survey")
g <- ggroup(cont=w)

state = new.env()
pages <- list()
pages[[1]] = function(cont, state) {
  group <- ggroup(horizontal = FALSE, container=cont)
  ## creating question
  question <- glabel("Do you have a driving license?", container = group)
  ## creating answer
  answer <- gradio(c("Yes","No"), container=group)
  ## creating next button
  nextQuestion <- gbutton("Next",container=group)
  ## handler
  addHandlerChanged(nextQuestion, handler = function(h, ...) {
    ## answer to save in matrix
    assign("A", c(svalue(question),svalue(answer)), state)
    delete(cont, group)
    pages[[2]](cont, state)
  })
}

pages[[2]] = function(cont, state) {
  group <- ggroup(horizontal = FALSE, container=cont)
  ## creating question
  question <- glabel("What is your gender?", container = group)
  ## creating answer
  answer <- gradio(c("Male","Female", "Other"), container=group)
  ## creating next button
  nextQuestion <- gbutton("Next",container=group)
  ## handler
  addHandlerChanged(nextQuestion, handler = function(h, ...) {
    ## answer to save in matrix
    assign("B", c(svalue(question),svalue(answer)), state)
    delete(cont, group)
    pages[[3]](cont, state)
  })
}

pages[[3]] = function(cont, state) {
  group <- ggroup(horizontal=FALSE, container=cont)
  ## result matrix for csv
  to_return=matrix(nrow=0,ncol=2)
  colnames(to_return) <- c("Question", "Answer")
  for (k in 1:length(names(state))) {
    a = get(names(state)[k], state)
    to_return <- rbind(to_return,a)
    rownames(to_return)[k] <- paste("Q",k,sep = "")
    g = ggroup(cont=group, horizontal=TRUE)
    glabel(a[1], cont=g)
    glabel(" ", cont=g)
    glabel(a[2], cont=g)
  }
  btn <- gbutton("Finish", container=group, handler = function(h,...) {
    write.csv(to_return, "survey.csv", row.names=TRUE, col.names=TRUE)
    dispose(h$obj)})
}

## start it off
pages[[1]](g, state)

EDIT 2: the line assign("one", c(svalue(question),svalue(answer)), state) should use alphabetical letters ("A", "B"...) or numbers in the right order otherwise answers get mixed up when using for k in names(state).

Octave J
  • 31
  • 2

1 Answers1

0

This isn't the most abstracted, but this pattern using delete over new windows, should be easily understood for subsequent modification. The use of an environment helps you keep state between calls to a handler, which won't be the case without some such trick, as assignments will be made within the environment of the handler by default.

# calling GUI library
library(gWidgets2)
options(guiToolkit="tcltk")


w <- gwindow()
g <- ggroup(cont=w)

state = new.env()
pages <- list()
pages[[1]] = function(cont, state) {
    group <- ggroup(horizontal = FALSE, container=cont)
    ## creating question
    question <- glabel("Do you have a driving license?", container = group)
    ## creating answer
    answer <- gradio(c("Yes","No"), container=group)
    ## creating next button
    nextQuestion <- gbutton("Next",container=group)
    ## handler
    addHandlerChanged(nextQuestion, handler = function(h, ...) {
        ## answer to save in matrix
        assign("one", c(svalue(question),svalue(answer)), state)
        delete(cont, group)
       pages[[2]](cont, state)
    })
}

pages[[2]] = function(cont, state) {
    group <- ggroup(horizontal = FALSE, container=cont)
    ## creating question
    question <- glabel("What is your gender?", container = group)
    ## creating answer
    answer <- gradio(c("Male","Female", "Other"), container=group)
    ## creating next button
    nextQuestion <- gbutton("Next",container=group)
    ## handler
    addHandlerChanged(nextQuestion, handler = function(h, ...) {
        ## answer to save in matrix
        assign("two", c(svalue(question),svalue(answer)), state)
        delete(cont, group)
        pages[[3]](cont, state)
    })
}

pages[[3]] = function(cont, state) {
    group <- ggroup(horizontal=FALSE, container=cont)
    for (k in names(state)) {
        g = ggroup(cont=group, horizontal=TRUE)
        a = get(k, state)
        glabel(a[1], cont=g)
        glabel(" ", cont=g)
        glabel(a[2], cont=g)
    }
}

## start it off
pages[[1]](g, state)
jverzani
  • 5,600
  • 2
  • 21
  • 17
  • Thank you for your answer ! Everything works and I'll try to look deeper into new.environment() and handler() functions to understand how everything goes. If I understand it correctly, here we only create one window and update the groups shown in this window? However, when I try to input a size in the preliminary window w, it doesn't affect the way windows are displayed afterwards... Is the size recalculated with each group shown in the window? – Octave J Sep 13 '18 at 02:52
  • That is toolkit dependent. I forget about tcltk. But you may need to call size. That isn’t necessary for your problem, but I’m not a fan of new windows, so added it. – jverzani Sep 13 '18 at 23:39