2013-06-30 29 views
5

Tôi đang tạo GUI bằng R bằng gWidgets. Cho đến bây giờ tôi đã truyền các giá trị từ cửa sổ này sang cửa sổ khác thông qua môi trường toàn cục. Sử dụng môi trường toàn cầu rất đơn giản để thực hiện nhưng không lý tưởng. Một vấn đề là R CMD check phàn nàn về việc thiếu các ràng buộc có thể nhìn thấy đối với các biến toàn cầu.Sử dụng các lớp tham chiếu R để chuyển các giá trị từ cửa sổ này sang cửa sổ khác trong GUI

Là giải pháp cho vấn đề này, lớp tham chiếu đã được đề cập bởi một số lập trình viên R. Nhưng để hiểu các lớp tham chiếu sẽ hoạt động như thế nào trong ngữ cảnh này, nó sẽ thực sự giúp đỡ để có một ví dụ đơn giản.

Hãy để tôi cung cấp GUI giao diện ngớ ngẩn. Khi người dùng nhấn nút của cửa sổ đầu tiên, nó sẽ đặt mô hình m trong môi trường toàn cầu. Nút thứ hai nhận được m từ môi trường toàn cầu và cho đầu ra. Khi bạn nhấn nút đầu tiên một lần nữa, nó sẽ tạo ra một mô hình mới m và thay đổi đầu ra của nút thứ hai. Nếu bạn đóng cửa sổ đầu tiên, nút trong cửa sổ thứ hai sẽ vẫn hoạt động, vì m nằm trong môi trường toàn cục.

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) 

Tôi làm cách nào để sử dụng các lớp tham chiếu trong ví dụ này?

Trả lời

2

Gọi setRefClass và bao gồm từng tiện ích con và giá trị dữ liệu dưới dạng một trường. Tiện ích phải có loại ANY. Khởi tạo các widget đó theo phương thức initialize và chức năng thuê ngoài cho các phương thức khác. Tạo một hàm để bao bọc việc tạo lớp.

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

Ví dụ tuyệt vời. Mã cho một cảnh báo: Trong .checkFieldsInMethod (def, fieldNames, allMethods): Phân bổ cục bộ cho tên trường sẽ không thay đổi trường: modelData <- data.frame (x = runif (10), y = runif (10)); hiển thị (win1) <- TRUE; hiển thị (win2) <- TRUE Ý của bạn là sử dụng "<< -"? (trong phương thức "khởi tạo" cho lớp "SillyGui") – JacobVanEtten

+1

@JacobVanEtten Cảm ơn. Tôi đã sửa dòng 'modelData <-'. Các dòng <-' có thể nhìn thấy được nên được gán cục bộ. Quấn cuộc gọi đến 'setRefClass' trong' suppressWarnings' nếu nó làm phiền bạn. –

+0

Cảm ơn! Mặc dù tôi sẽ theo gợi ý của John trước tiên, thật tuyệt khi có ví dụ này trực tuyến. – JacobVanEtten

2

Câu trả lời của Richie là một cách để thực hiện. Nó cung cấp cho bạn một đối tượng duy nhất (thể hiện được trả về bởi generate_silly_gui mà bạn có thể sử dụng để thao tác các mô hình các tiện ích được sử dụng cho GUI. Một cách tiếp cận tốt. . từ mã trong câu hỏi việc sử dụng các lớp tham khảo ở đây là cách cấu trúc hơn của việc sử dụng một môi trường:.

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

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

Sau đó chỉ cần thay thế .GlobalEnv$m với model_instance$m trong mã của bạn và chạy

sử dụng một lớp tài liệu tham khảo cho phép bạn làm những việc như thêm getter's và setter's cũng làm những việc khác và đẩy bạn về phía chế độ Kiểu l-view-controller. Gói objectSignals đi theo hướng đó.

Nếu GUI của bạn trở nên phức tạp hơn, bạn có thể muốn tách riêng hai phương pháp.

+0

Ví dụ tuyệt vời và tôi sẽ thử điều này trước tiên và hãy xem 'objectSignals'. 'R CMD CHECK' sẽ không phàn nàn về các ràng buộc có thể nhìn thấy khi sử dụng phương pháp này? – JacobVanEtten

+0

Tôi vừa làm một bài kiểm tra nhỏ. Tôi tạo một thể hiện của một lớp tham chiếu trong một hàm .onLoad của gói của tôi và sau đó sử dụng nó bên trong các hàm của gói. Điều này vẫn tạo ra một vấn đề ràng buộc toàn cầu ... Có tất nhiên các thủ thuật bẩn để tránh điều này: [link] (http://stackoverflow.com/questions/8096313/no-visible-binding-for-global-variable-note- in-r-cmd-check). Nhưng tôi nghĩ các lớp tham khảo sẽ làm cho điều này không cần thiết ... – JacobVanEtten

+0

Bạn có thể tạo cá thể trong mã. Xem ví dụ https://github.com/jverzani/gWidgets2RGtk2/blob/master/R/icons.R#L106 – jverzani