我正在尝试在R中创建一个交互式直方图,可以通过移动滑块或在文本框中输入一个值来调整bin的宽度。除此之外,我还想为用户提供一个选项,可以将图保存为特定的箱宽。

为此,我发现“aplpack”库的“gslider”功能是一个很好的起点。我试图对其进行修改以满足我的目的,并了解有关Tcl/Tk构造的更多信息。但是我现在陷入了困境,无法继续前进,主要是因为我还没有完全理解如何在函数之间捕获并传递滑块值。

以下是我不太了解的代码片段。这些来自“gslider”功能的源代码。

# What is the rationale behind using the 'assign' function here and at
# other instances in the code?

  img <- tkrplot::tkrplot(gr.frame, newpl, vscale = 1, hscale = 1)
  tkpack(img, side = "top")
  assign("img", img, envir = slider.env)

# I understand the below lines when considered individually. But collectively,
# I am having a difficult time comprehending them. Most importantly, where
# exactly is the slider movement captured here?

  sc <- tkscale(fr, from = sl.min, to = sl.max,
              showvalue = TRUE, resolution = sl.delta, orient = "horiz")
  assign("sc", sc, envir = slider.env)
  eval(parse(text = "tkconfigure(sc, variable=inputbw1)"), envir = slider.env)
  sl.fun <- sl.function
  if (!is.function(sl.fun))
    sl.fun <- eval(parse(text = paste("function(...){",
                                    sl.fun, "}")))
    fname <- 'tkrrsl.fun1'
    eval(parse(text = c(paste(fname, " <-"), " function(...){",
                    "tkrreplot(get('img',envir=slider.env),fun=function()",
                    deparse(sl.fun)[-1], ")", "}")))
    eval(parse(text = paste("environment(", fname, ")<-parent.env")))
    if (prompt)
      tkconfigure(sc, command = get(fname))
    else tkbind(sc, "<ButtonRelease>", get(fname))

  if (exists("tkrrsl.fun1")) {
    get("tkrrsl.fun1")()
  }
  assign("slider.values.old", sl.default, envir = slider.env)

感谢每个人的回答范围。 Juba和Greg的答案是我可以编写以下代码的答案:
slider_txtbox <- function (x, col=1, sl.delta, title)
{
  ## Validations
  require(tkrplot)
  pos.of.panel <- 'bottom'
  if(is.numeric(col))
    col <- names(x)[col]
  x <- x[,col, drop=FALSE]
  if (missing(x) || is.null(dim(x)))
     return("Error: insufficient x values")
  sl.min <- sl.delta # Smarter initialization required
  sl.max <- max(x)
  xrange <- (max(x)-min(x))
  sl.default <- xrange/30
  if (!exists("slider.env")) {
    slider.env <<- new.env(parent = .GlobalEnv)
  }
  if (missing(title))
    title <- "Adjust parameters"

  ## Creating initial dialogs
  require(tcltk)
  nt <- tktoplevel()
  tkwm.title(nt, title)
  if(.Platform$OS.type == 'windows')
    tkwm.geometry(nt, "390x490+0+10")
  else if(.Platform$OS.type == 'unix')
     tkwm.geometry(nt, "480x600+0+10")
  assign("tktop.slider", nt, envir = slider.env)
  "relax"
  nt.bak <- nt
  sl.frame <- tkframe(nt)
  gr.frame <- tkframe(nt)
  tx.frame <- tkframe(nt)
  tkpack(sl.frame, tx.frame, gr.frame, side = pos.of.panel)

  ## Function to create and refresh the plot
  library(ggplot2)
  library(gridExtra)
  makeplot <- function(bwidth, save) {
    if(bwidth <= 0) {
      df <- data.frame('x'=1:10, 'y'=1:10)
       histplot <- ggplot(df, aes(x=x, y=y)) + geom_point(size=0) + xlim(0, 10) +  ylim(0, 100) +
    geom_text(aes(label='Invalid binwidth...', x=5, y=50), size=9)
    } else {

    histplot <- ggplot(data=x, aes_string(x=col)) +
  geom_histogram(binwidth=bwidth, aes(y = ..density..), fill='skyblue') +
  theme(axis.title.x=element_text(size=15), axis.title.y=element_text(size=15),
        axis.text.x=element_text(size=10, colour='black'),
        axis.text.y=element_text(size=10, colour='black'))
    }
    print(histplot)
    if(save){
  filename <- tkgetSaveFile(initialfile=paste('hist_bw_', bwidth, sep=''),
                            filetypes='{{PNG files} {.png}} {{JPEG files} {.jpg .jpeg}}
                            {{PDF file} {.pdf}} {{Postscript file} {.ps}}')
  filepath <- as.character(filename)
  splitpath <- strsplit(filepath, '/')[[1]]
  flname <- splitpath[length(splitpath)]
  pieces <- strsplit(flname, "\\.")[[1]]
  ext <- tolower(pieces[length(pieces)])
  if(ext != 'png' && ext != 'jpeg' && ext != 'jpg' && ext != 'pdf' && ext != 'ps') {
    ext <- 'png'
    filepath <- paste(filepath, '.png', sep='')
    filename <- tclVar(filepath)
  }
  if(ext == 'ps')
    ext <- 'postscript'
  eval(parse(text=paste(ext, '(file=filepath)', sep='')))
  eval(parse(text='print(histplot)'))
  dev.off()
}
  }
  img <- tkrplot::tkrplot(gr.frame, makeplot(sl.default, FALSE), vscale = 1, hscale = 1)
  tkpack(img, side = "top")
  assign("img", img, envir = slider.env)

  ## Creating slider, textbox and labels
  parent.env <- sys.frame(sys.nframe() - 1)
  tkpack(fr <- tkframe(sl.frame), side = 'top')
  sc <- tkscale(fr, from = sl.min, to = sl.max,
            showvalue = TRUE, resolution = sl.delta,
            orient = "horiz")
  tb <- tkentry(fr, width=4)
  labspace <- tklabel(fr, text='\t\t\t')
  tkpack(sc, labspace, tb, side = 'left')

  tkpack(textinfo <- tkframe(tx.frame), side = 'top')
  lab <- tklabel(textinfo, text = '                    Move slider', width = "20")
  orlabel <- tklabel(textinfo, text='          OR', width='10')
  txtboxmsg <- tklabel(textinfo, text = 'Enter binwidth', width='20')
  tkpack(txtboxmsg, orlabel, lab, side='right')

  tkpack(f.but <- tkframe(sl.frame))
  tkpack(tklabel(f.but, text=''))
  tkpack(tkbutton(f.but, text = "Exit", command = function() tkdestroy(nt)),
     side='right')
  tkpack(tkbutton(f.but, text = "Save", command = function(...) {
    bwidth <- as.numeric(tclvalue(get('inputtb', envir=slider.env)))
    tkrreplot(get('img',envir=slider.env),fun=function() { makeplot(bwidth, TRUE); sync_slider()})
  }), side='right')

  ## Creating objects and variables associated with slider and textbox
  assign("sc", sc, envir = slider.env)
  eval(parse(text = "assign('inputsc', tclVar(sl.default), envir=slider.env)"))
  eval(parse(text = "tkconfigure(sc, variable=inputsc)"), envir = slider.env)

  assign("tb", tb, envir = slider.env)
  eval(parse(text = "assign('inputtb', as.character(tclVar(sl.default)),
         envir=slider.env)"))
  eval(parse(text = "tkconfigure(tb, textvariable=inputtb)"), envir = slider.env)

  ## Function to update the textbox value when the slider has changed
  sync_textbox <- function() {
  bwidth_sl <- tclvalue(get('inputsc', envir=slider.env))
  assign('inputtb', tclVar(bwidth_sl), envir=slider.env)
  eval(parse(text = "tkconfigure(tb, textvariable=inputtb)"), envir = slider.env)
}

 ## Function to update the slider value when the textbox has changed
 sync_slider <- function() {
 bwidth_tb <- tclvalue(get('inputtb', envir=slider.env))
 assign('inputsc', tclVar(bwidth_tb), envir=slider.env)
 eval(parse(text = "tkconfigure(sc, variable=inputsc)"), envir = slider.env)
}

  ## Bindings : association of certain functions to certain events for the slider
  ## and the textbox

  tkbind(sc, "<ButtonRelease>", function(...) {
    bwidth <- as.numeric(tclvalue(get('inputsc', envir=slider.env)))
    tkrreplot(get('img',envir=slider.env),fun=function() { makeplot(bwidth, FALSE); sync_textbox()})
  })

  tkbind(tb, "<Return>", function(...) {
    bwidth <- as.numeric(tclvalue(get('inputtb', envir=slider.env)))
    if(bwidth > sl.max && !is.na(bwidth)) {
      bwidth <- sl.max
      assign('inputtb', tclVar(bwidth), envir=slider.env)
      eval(parse(text = "tkconfigure(tb, textvariable=inputtb)"), envir = slider.env)
     } else
    if(bwidth < sl.min || is.na(bwidth)) {
      bwidth <- sl.min
      assign('inputtb', tclVar(bwidth), envir=slider.env)
      eval(parse(text = "tkconfigure(tb, textvariable=inputtb)"), envir = slider.env)
     }
  tkrreplot(get('img',envir=slider.env),fun=function() { makeplot(bwidth, FALSE);    sync_slider()})
})

}

library(ggplot2)
slider_txtbox(movies, 'rating', 0.1, 'Adjust binwidth')

最佳答案

这是一个基于注释的最小工作示例,它基于您首次提交的完整代码。由于我不是tcl/tk专家,因此可能会有更清洁或更完善的方法。而且它是非常不完整的(例如,应检查文本框的值是否在滑块的范围内,等等):

library(ggplot2)
library(gridExtra)
title <- "Default title"
data(movies)

## Init dialog
require(tkrplot)
if (!exists("slider.env")) slider.env <<- new.env(parent = .GlobalEnv)
require(tcltk)
nt <- tktoplevel()
tkwm.title(nt, title)
tkwm.geometry(nt, "480x600+0+10")
assign("tktop.slider", nt, envir = slider.env)
"relax"
nt.bak <- nt
sl.frame <- tkframe(nt)
gr.frame <- tkframe(nt)
tx.frame <- tkframe(nt)
tkpack(sl.frame, tx.frame, gr.frame, side = "bottom")
## First default plot
newpl <- function(...) {
  dummydf <- data.frame('x'=1:10, 'y'=1:10)
  dummy <- ggplot(dummydf, aes(x=x, y=y)) + geom_point(size=0) + xlim(0, 10) + ylim(0, 100) +
    geom_text(aes(label='Generating plot...', x=5, y=50), size=9)
  print(dummy)
  }
img <- tkrplot::tkrplot(gr.frame, newpl, vscale = 1, hscale = 1)
tkpack(img, side = "top")
assign("img", img, envir = slider.env)
tkpack(fr <- tkframe(sl.frame), side = 'top')

## Creating slider, textbox and labels
sc <- tkscale(fr, from = 0, to = 5, showvalue = TRUE, resolution = 0.1, orient = "horiz")
tb <- tkentry(fr, width=4)
lab <- tklabel(fr, text = 'Select binwidth ', width = "16")
orlabel <- tklabel(fr, text=' or ', width='4')
tkpack(lab, sc, orlabel, tb, side = 'left')
tkpack(textinfo <- tkframe(tx.frame), side = 'top')


## Creating objects and variables associated with slider and textbox
assign("sc", sc, envir = slider.env)
assign("tb", tb, envir = slider.env)
assign('inputsc', tclVar(2.5), envir=slider.env)
assign('inputtb', tclVar('2.5'), envir=slider.env)
eval(parse(text = "tkconfigure(sc, variable=inputsc)"), envir = slider.env)
eval(parse(text = "tkconfigure(tb, textvariable=inputtb)"), envir = slider.env)

## Function to update the textbox value when the slider has changed
sync_textbox <- function() {
  bwidth_sl <- tclvalue(get('inputsc', envir=slider.env))
  assign('inputtb', tclVar(bwidth_sl), envir=slider.env)
  eval(parse(text = "tkconfigure(tb, textvariable=inputtb)"), envir = slider.env)
}

## Function to update the slider value when the textbox has changed
sync_slider <- function() {
  bwidth_tb <- tclvalue(get('inputtb', envir=slider.env))
  assign('inputsc', tclVar(bwidth_tb), envir=slider.env)
  eval(parse(text = "tkconfigure(sc, variable=inputsc)"), envir = slider.env)
}

## Function to refresh the plot
refresh <- function(bwidth) {
  histplot <- ggplot(data=movies, aes_string(x="rating")) +
     geom_histogram(binwidth=bwidth,
                    aes(y = ..density..), fill='skyblue') +
                      theme(axis.title.x=element_text(size=15), axis.title.y=element_text(size=15),
                            axis.text.x=element_text(size=10, colour='black'),
                            axis.text.y=element_text(size=10, colour='black'))
  print(histplot)
}

## Bindings : association of certain functions to certain events for the slider
## and the textbox

tkbind(sc, "<ButtonRelease>", function(...) {
  bwidth <- as.numeric(tclvalue(get('inputsc', envir=slider.env)))
  tkrreplot(get('img',envir=slider.env),fun=function() { refresh(bwidth); sync_textbox()})
})

tkbind(tb, "<Return>", function(...) {
  bwidth <- as.numeric(tclvalue(get('inputtb', envir=slider.env)))
  tkrreplot(get('img',envir=slider.env),fun=function() { refresh(bwidth); sync_slider()})
})

关于r - r-了解 'gslider'函数以进行交互式绘图,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14619820/

10-12 23:35