2013-06-30 13 views
5

Ich mache eine GUI in R mit gWidgets. Bis jetzt habe ich Werte über die globale Umgebung von einem Fenster zum anderen weitergegeben. Die Verwendung der globalen Umgebung ist einfach zu implementieren, aber nicht ideal. Ein Problem ist, dass R CMD check über fehlende sichtbare Bindungen für globale Variablen klagt. Als Lösung für dieses Problem wurden Referenzklassen von mehreren R-Programmierern erwähnt. Aber um zu verstehen, wie Referenzklassen in diesem Kontext funktionieren, würde es wirklich helfen, ein einfaches Beispiel zu haben.Verwenden von R-Referenzklassen zum Übergeben von Werten von einem Fenster zu einem anderen in einer GUI

Lassen Sie mich ein dummes GUI geben, mit zu arbeiten. Wenn der Benutzer auf die Schaltfläche des ersten Fensters klickt, wird das Modell m in die globale Umgebung versetzt. Die zweite Schaltfläche ruft m aus der globalen Umgebung ab und gibt eine Ausgabe aus. Wenn Sie die erste Taste erneut drücken, wird ein neues Modell m erstellt und die Ausgabe der zweiten Taste geändert. Wenn Sie das erste Fenster schließen, funktioniert die Schaltfläche im zweiten Fenster weiterhin, da sich m in der globalen Umgebung befindet.

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) 

Wie kann ich Referenzklassen in diesem Beispiel verwenden?

Antwort

2

Rufen Sie setRefClass, und enthalten Sie jedes Widget und Datenwert als ein Feld. Widgets sollten den Typ ANY haben. Initialisieren Sie diese Widgets in der initialize-Methode und lagern Sie die Funktionalität an andere Methoden aus. Erstellen Sie eine Funktion, um die Erstellung der Klasse zu umbrechen.

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

Großartiges Beispiel. Der Code gibt eine Warnung aus: In .checkFieldsInMethod (def, fieldNames, allMethods): Die lokale Zuweisung zum Feldnamen ändert das Feld nicht: modelData <- data.frame (x = runif (10), y = runif (10)); sichtbar (win1) <- WAHR; visible (win2) <- TRUE Wollten Sie "<< -" verwenden? (in der Methode "initialisieren" für die Klasse "SillyGui") – JacobVanEtten

+1

@JacobVanEtten Danke. Ich habe die 'modelData <-' Zeile behoben. Die sichtbaren Zeilen sollten lokale Zuweisungen sein. Wickeln Sie den Aufruf von 'setRefClass' in' suppressWarnings', wenn es Sie nervt. –

+0

Danke! Obwohl ich Johns Vorschlag zuerst folgen werde, ist es großartig, dieses Beispiel online zu haben. – JacobVanEtten

2

Richies Antwort ist eine Möglichkeit, dies zu tun. Es gibt Ihnen ein einzelnes Objekt (die von generate_silly_gui zurückgegebene Instanz, die Sie verwenden können, um das Modell und die Widgets für die GUI zu manipulieren. Ein guter Ansatz. Das Folgende ist einfacher, es macht nur das Modell und ist nur eine kleine Abweichung . aus dem Code in der Frage der Verwendung von Referenzklassen hier ist besser strukturiert, ein Umfeld zu verwenden.

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

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

Dann einfach .GlobalEnv$m mit model_instance$m in Ihrem Code, und führen Sie ersetzen

Unter Verwendung einer Referenzklasse erlaubt Du machst Dinge wie Add-Getter und Setter, die auch andere Dinge tun und schiebt dich in den Modus L-View-Controller-Stil. Das objectSignals Paket geht in diese Richtung.

Wenn Ihre GUI komplizierter wird, möchten Sie vielleicht die beiden Ansätze entkoppeln.

+0

Großartiges Beispiel und ich werde das zuerst versuchen und einen Blick auf 'objectSignals' werfen. 'R CMD CHECK' wird sich bei Verwendung dieses Ansatzes nicht über die sichtbaren Bindungen beschweren? – JacobVanEtten

+0

Ich habe gerade einen kleinen Test gemacht. Ich erstelle eine Instanz einer Referenzklasse in einer .onLoad-Funktion meines Pakets und verwende sie dann in den Funktionen des Pakets. Dies erzeugt immer noch ein globales Bindeproblem ... Es gibt natürlich schmutzige Tricks, um dies zu vermeiden: [link] (http://stackoverflow.com/questions/8096313/no-visible-binding-for-global-variable-note- in-r-cmd-Prüfung). Aber ich dachte, Referenzklassen würden das unnötig machen ... – JacobVanEtten

+0

Sie können die Instanz im Code erstellen. Siehe zum Beispiel https://github.com/jverzani/gWidgets2RGtk2/blob/master/R/icons.R#L106 – jverzani