2013-06-30 23 views
5

Sto creando una GUI in R utilizzando gWidgets. Fino ad ora ho passato i valori da una finestra all'altra attraverso l'ambiente globale. L'utilizzo dell'ambiente globale è semplice da implementare ma non ideale. Un problema è che R CMD check si lamenta della mancanza di collegamenti visibili per le variabili globali.Uso delle classi di riferimento R per passare valori da una finestra all'altra in una GUI

Come soluzione a questo problema, le classi di riferimento sono state citate da diversi programmatori R. Ma per capire come funzionano le classi di riferimento in questo contesto, sarebbe davvero utile avere un semplice esempio.

Lasciami dare un'interfaccia grafica stupida con cui lavorare. Quando l'utente preme il pulsante della prima finestra, inserisce il modello m nell'ambiente globale. Il secondo pulsante riceve m dall'ambiente globale e fornisce un'uscita. Quando si preme di nuovo il primo pulsante, si crea un nuovo modello m e si modifica l'output del secondo pulsante. Se chiudi la prima finestra, il pulsante nella seconda finestra funzionerà ancora, perché m si trova nell'ambiente globale.

library(gWidgets) 
options(guiToolkit = "tcltk") 

h1 <- function(h, ...){ 
    d1 <- data.frame(x=runif(10), y=runif(10)) 
    .GlobalEnv$m <- lm(x ~ y, data=d1) 
} 

g1 <- gbutton("1. Make model", 
    container=gwindow(), handler=h1) 

h2 <- function(h, ...){ 
    d2 <- data.frame(y=(1:10)/10) 
    p <- predict(.GlobalEnv$m, newdata=d2) 
    print(p) 
} 

g2 <- gbutton("2. Make prediction", 
    container=gwindow(), handler=h2) 

Come utilizzare le classi di riferimento in questo esempio?

risposta

2

Chiama il numero setRefClass e includi ciascun widget e il valore dei dati come un campo. I widget dovrebbero avere il tipo ANY. Inizializzare questi widget nel metodo initialize e esternalizzare la funzionalità ad altri metodi. Crea una funzione per avvolgere la creazione della classe.

silly_gui_generator <- setRefClass(
    "SillyGui", 
    fields = list(
    #widgets 
    win1   = "ANY", 
    win2   = "ANY", 
    button1  = "ANY", 
    button2  = "ANY", 
    #data 
    modelData  = "data.frame", 
    predictionData = "data.frame", 
    model   = "lm" 
), 
    methods = list(
    initialize = function(modelData = NULL) 
    { 
     if(is.null(modelData)) 
     { 
     modelData <<- data.frame(x = runif(10), y = runif(10)) 
     } 

     win1 <<- gwindow(visible = FALSE) 
     win2 <<- gwindow(visible = FALSE) 
     button1 <<- gbutton(
     "1. Make model", 
     container = win1, 
     handler = function(h, ...) 
     {   
      makeModel() 
     } 
    ) 
     button2 <<- gbutton(
     "2. Make prediction", 
     container = win2, 
     handler = function(h, ...) 
     {   
      print(predictModel()) 
     } 
    ) 
     visible(win1) <- TRUE 
     visible(win2) <- TRUE 
    }, 
    makeModel = function() 
    { 
     model <<- lm(x ~ y, data = modelData) 
    }, 
    predictModel = function() 
    { 
     predictionData <<- data.frame(y = (1:10)/10) 
     predict(model, newdata = predictionData) 
    } 
) 
) 

generate_silly_gui <- function(modelData = NULL) 
{ 
    invisible(silly_gui_generator$new(modelData = modelData)) 
} 
+0

Grande esempio. Il codice fornisce un avvertimento: In .checkFieldsInMethod (def, fieldNames, allMethods): L'assegnazione locale al nome campo non cambierà il campo: modelData <- data.frame (x = runif (10), y = runif (10)); visible (win1) <- TRUE; visible (win2) <- TRUE Intendevi usare "<< -"? (nel metodo "inizializza" per la classe "SillyGui") – JacobVanEtten

+1

@JacobVanEtten Grazie. Ho corretto la riga 'modelData <-'. Le righe 'visible <-' dovrebbero essere assegnate localmente. Termina la chiamata a 'setRefClass' in' sopprimi avvisi 'se ti infastidisce. –

+0

Grazie! Anche se seguirò prima il suggerimento di John, è bello avere questo esempio online. – JacobVanEtten

2

La risposta di Richie è un modo per farlo. Fornisce un singolo oggetto (l'istanza restituita da generate_silly_gui che è possibile utilizzare per manipolare il modello e i widget utilizzati per la GUI .Un buon approccio.Il seguente è più semplice, fa semplicemente il modello ed è semplicemente una leggera deviazione . dal codice nella questione l'uso di classi di riferimento qui è il modo più strutturato utilizzando un ambiente:.

OurModel <- setRefClass("OurModel", 
         fields="m") 

## a global 
model_instance = OurModel$new(m=NULL) 

Poi basta sostituire .GlobalEnv$m con model_instance$m nel codice ed eseguire

utilizzando una classe di riferimento consente fai cose come aggiungere getter e setter che fanno anche altre cose e ti spinge verso la modalità Stile del controller di visualizzazione. Il pacchetto objectSignals va in quella direzione.

Se la GUI diventa più complicata, potresti voler disaccoppiare i due approcci.

+0

Un grande esempio e proverò questo prima e dare un'occhiata a 'objectSignals'. 'R CMD CHECK' non si lamenterà delle associazioni visibili quando si utilizza questo approccio? – JacobVanEtten

+0

Ho appena fatto un piccolo test. Creo un'istanza di una classe di riferimento in una funzione .onLoad del mio pacchetto e quindi la utilizzo all'interno delle funzioni del pacchetto. Questo produce ancora un problema di binding globale ... Ci sono ovviamente trucchi sporchi per evitare questo: [link] (http://stackoverflow.com/questions/8096313/no-visible-binding-for-global-variable-note- in-r-cmd-check). Ma pensavo che le classi di referenza avrebbero reso questo non necessario ... – JacobVanEtten

+0

Puoi creare l'istanza nel codice. Vedi ad esempio https://github.com/jverzani/gWidgets2RGtk2/blob/master/R/icons.R#L106 – jverzani

Problemi correlati