2013-03-16 10 views
8

Sto provando a sviluppare una GUI (usando gWidgets) per un pacchetto R. Il mio piano era di costruire una finestra principale contenente i dati e con pulsanti che chiamavano piccoli involucri gui per ogni funzione. Sfortunatamente sono bloccato su un problema di base (?) - Non so come trasferire i dati.Come restituire i valori da gWidgets e handler?

questons:

  • come inviare correttamente i dati tra finestre separate?
  • Come inviare dati da un gestore in un'altra finestra?

Il mio problema è simile a: Loading and saving variables in R with gWidgets, ma da quello che ho letto l'uso di .GlobalEnv non è raccomandato.

Ho anche visto qualcuno che utilizza < < - operatore: http://www.mail-archive.com/[email protected]/msg00053.html, ma non riesco a riprodurlo correttamente (e non funzionerà con il mio esempio, penso).

Di seguito è riportato un semplice esempio, in cui si tenta di inviare un testo a un'altra finestra e di nuovo indietro se si preme il pulsante. Ho provato con return all'interno del gestore, ma non funziona (non sono sicuro che sia permesso). La sottofinestra restituisce immediatamente il suo valore alla fine della funzione, prima che la funzione handler/inner possa agire sui dati. Non so come raggiungere l'operatore dalla finestra principale.

main <- function(){ 

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

    w <- gwindow(title="Main window", 
       visible=FALSE) 

    txt <- gtext(text="Initial text in main window.", 
       container=w) 

    btn <- gbutton("Send to sub window", container=w) 

    addHandlerChanged(btn, handler = function(h, ...) { 
    shouldbenew <- subwindow(svalue(txt)) 
    svalue(txt) <- paste("The sub window immediately returns 'TRUE', before pushing 'Return to main':", shouldbenew) 
    }) 

    visible(w) <- TRUE 

} 

subwindow<- function(text){ 

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

    sw <- gwindow(title="Sub window", 
       visible=FALSE) 

    editedtxt <- gtext(text=paste(text, "- Text is transferred to the sub window, but I don't know how to send it back to the main window"), 
        container=sw) 

    btn <- gbutton("Send to main window", container=sw) 

    addHandlerChanged(btn, handler = function(h, ...) { 
    newtxt <- svalue(editedtxt) 
    return(newtxt) 

    }) 

    visible(sw) <- TRUE 

} 

Aggiornamento: Ecco la soluzione Ho scelto come il senso in avanti (come suggerito da jverzani), illustrato con l'esempio precedente. Spero di aver capito la soluzione suggerita e che l'ho implementata in un modo "carino", idealmente accettato da CRAN.

Per riepilogare ho creato un nuovo ambiente all'interno dell'ambiente della finestra principale. Ho modificato la finestra secondaria per prendere l'ambiente nella chiamata. Premendo il pulsante nella finestra secondaria assign il testo modificato per l'ambiente passato. Quando la finestra secondaria viene chiusa e la finestra principale viene messa a fuoco, il testo modificato è accessibile dall'ambiente utilizzando get.

main <- function(){ 

    library(gWidgets) 
    options(guiToolkit="RGtk2") 
    # Create a new environment for passing data. 
    .mainGlobal <- new.env() 

    w <- gwindow(title="Main window", visible=FALSE) 

    txt <- gtext(text="Initial text in main window.", 
       container=w) 

    btn <- gbutton("Send to sub window", container=w) 

    addHandlerChanged(btn, handler = function(h, ...) { 
    # Call sub widget passing text and environment. 
    subwindow(text=svalue(txt), env=.mainGlobal) 
    }) 

    visible(w) <- TRUE 

    addHandlerFocus(w, handler = function (h, ...) { 

    if(exists("myText", envir=.mainGlobal)){ 
     # Retrieve text and update. 
     svalue(txt) <- get("myText", envir=.mainGlobal) 
    }  
    }) 

} 

subwindow<- function(text, env=NULL){ 

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

    sw <- gwindow(title="Sub window", visible=FALSE) 

    editedtxt <- gtext(text=text, container=sw) 

    btn <- gbutton("Send to main window", container=sw) 

    addHandlerChanged(btn, handler = function(h, ...) { 
    newtxt <- svalue(editedtxt) 
    assign("myText", newtxt, envir=env) 
    }) 

    visible(sw) <- TRUE 

} 
+1

La cosa più facile da fare è utilizzare le variabili globali per i widget che si desidera modificare. In alternativa, puoi inserire le variabili in un ambiente. Come sono definiti, siedono all'interno di due funzioni in modo che i due non possano comunicare. – jverzani

+0

Grazie per il tuo suggerimento! Come utilizzerei le variabili globali in un modo che non rischia di sovrascrivere gli oggetti esistenti nello spazio di lavoro (ed è accettato per un pacchetto inviato a CRAN)? Apprezzerei se qualcuno potesse mostrare il modo corretto modificando il mio esempio sopra. –

risposta

5

Un approccio migliore, ma che prevede una rielaborazione più ampia del codice, consiste nel memorizzare le GUI nelle classi di riferimento.

È possibile chiamare setRefClass con un elenco di campi (uno per ciascun widget) e definire un metodo di inizializzazione in cui viene creata la GUI. Generalmente creo una funzione per racchiudere la chiamata che crea un'istanza. Vedere setRefClass alla fine del blocco di codice.

mainGui <- suppressWarnings(setRefClass(#Warnings about local assignment not relevant 
    "mainGui", 
    fields = list(
    #widgets 
    w = "ANY",  #"GWindow" 
    txt = "ANY",  #"GEdit" 
    btn = "ANY"  #"GButton" 
), 
    methods = list(
    initialize = function(windowPosition = c(0, 0)) 
    { 
     "Creates the GUI" 

     w <<- gwindow(
     "Main window", 
     visible = FALSE, 
     parent = windowPosition 
    ) 

     txt <<- gedit(
     "Initial text in main window.", 
     container = w 
    )  

     btn <<- gbutton(
     "Send to sub window", 
     container = w 
    )  

     addHandlerChanged(
     btn, 
     handler = function(h, ...) { 
      subWindow$setText(getText()) 
     } 
    ) 

     visible(w) <- TRUE  
    }, 
    #other methods to access GUI functionality go here 
    getText = function() 
    { 
     svalue(txt) 
    }, 
    setText = function(newTxt) 
    { 
     svalue(txt) <- newTxt 
    } 
) 
)) 

createMainGui <- function(...) 
{ 
    invisible(mainGui$new(...)) 
}  
+0

Sembra una buona soluzione. Tuttavia, quando provo il codice ottengo un errore sulla riga prima di 'createMainGui': Errore in makeUsageCollector (fun, ...): funziona solo per le chiusure. Qualche idea su cosa lo sta causando? –

+0

Oops, ho usato 'getCurrentText <- function()' invece di 'getCurrentText = function()'. Ora risolto. –

+0

Grazie, dopo aver aggiunto un '{' dopo 'setText = function (newTxt)' l'esempio sta funzionando. Giocherò un po 'con questo e vedrò come ci si sente. –

1

È possibile solo restituire i widget per ogni finestra in un elenco. Quindi la funzione principale aggiunge la riga list(w = w, txt = txt, btn = btn) alla fine per restituire ogni widget e renderli accessibili al termine della funzione.

L'esempio seguente è la modifica più piccola al codice che funziona, ma c'è un difetto minore in quanto main e subwindow ora contengono riferimenti ai valori di ritorno l'uno dall'altro. Il codice funziona, ma se stai facendo qualcosa di più complicato, potrebbe facilmente diventare difficile da mantenere.

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

main <- function(){ 
    w <- gwindow(title="Main window", 
       visible=FALSE) 

    txt <- gtext(text="Initial text in main window.", 
       container=w) 

    btn <- gbutton("Send to sub window", container=w) 

    addHandlerChanged(btn, handler = function(h, ...) { 
    svalue(subWindow$txt) <- svalue(mainWindow$txt) 
    }) 

    visible(w) <- TRUE 
    list(w = w, txt = txt, btn = btn) 
} 

subwindow<- function(text){ 
    sw <- gwindow(title="Sub window", 
       visible=FALSE) 

    editedtxt <- gtext(text="", 
        container=sw) 

    btn <- gbutton("Send to main window", container=sw) 

    addHandlerChanged(btn, handler = function(h, ...) { 
    svalue(mainWindow$txt) <- svalue(subWindow$txt) 
    }) 

    visible(sw) <- TRUE 
    list(w = sw, txt = editedtxt, btn = btn) 
} 

mainWindow <- main() 
subWindow <- subwindow() 
1

È possibile passare informazioni tra gwidgets utilizzando funzioni indipendenti e senza preventivamente conoscere il nome dell'oggetto del widget ricevente:

initMain <- function() { 
    w <- gwindow(title="Main window",visible=FALSE) 
    txt <- gtext(text="Initial text in main window.",container=w) 
    btn <- gbutton("Send to sub window", container=w) 

    list(
    run = function(partner) { 
     addHandlerChanged(btn, handler = function(h, ...) { 
     svalue(partner$txt) <- svalue(txt) 
     }) 
     visible(w) <- TRUE 
    }, 
    txt = txt 
) 
} 

initSubWindow<- function() { 
    w <- gwindow(title="Sub window",visible=FALSE) 
    txt <- gtext(text="huhu",container=w) 
    btn <- gbutton("Send to main window", container=w) 

    list(
    run = function(partner) { 
     addHandlerChanged(btn, handler = function(h, ...) { 
     svalue(partner$txt) <- svalue(txt) 
     }) 
     visible(w) <- TRUE 
    }, 
    txt = txt 
) 
} 

mw <- initMain() 
sw <- initSubWindow() 

mw$run(sw) 
sw$run(mw) 
Problemi correlati