Interactive plots with tkrplot

Interactive plots with tkrplot

James Wettenhall & Philippe Grosjean bio photo By James Wettenhall & Philippe Grosjean

The R code for this example is a little longer than that of the simpler examples. The basic idea is that we put a scatter plot in a Tk window, using the tkrplot package by Luke Tierney. We then allow the user to click on (or near) one of the plotted points in order to attach a label to that point (and replot the graph).

The hardest part is mapping between image (Tk widget) coordinates and R plot coordinates, which is done in the function onLeftClick().

Running the code below gives the following graph window:

R plot

Clicking on the upper-left point gives the following message box:

Question

After answering Yes to the message box question, the graph is updated. Note the label, A above the point that was clicked on.

R plot with one point labelled

After labeling all of the points, the graph looks like this:

R plot with all points labelled

R code for interactive tkrplot example

library(tcltk2)
library(tkrplot)

xCoords <- -12:13
yCoords <- xCoords^2
labelsVec <- LETTERS

indexLabeled <- c()
labeledPoints <- list()

win1 <- tktoplevel()
tktitle(win1) <- "Click on a point to label it"

parPlotSize <- c()
usrCoords <- c()

plotTk <- function() {
  plot(xCoords, yCoords, main = "Click on a point to label it")
  if (length(indexLabeled)) {
    for (i in (1:length(indexLabeled))) {
      indexClosest <- indexLabeled[i]
      text(xCoords[indexClosest], yCoords[indexClosest],
        labels = labelsVec[indexClosest], pos = 3)
    }
  }
  parPlotSize <<- par("plt")
  usrCoords   <<- par("usr")
}

win1$env$plot <- tkrplot(win1, fun = plotTk, hscale = 1.5, vscale = 1.5)
tkgrid(win1$env$plot)

labelClosestPoint <- function(xClick, yClick, imgXcoords, imgYcoords) {
  squaredDistance <- (xClick - imgXcoords)^2 + (yClick - imgYcoords)^2
  indexClosest <- which.min(squaredDistance)
  indexLabeled <<- c(indexLabeled, indexClosest)
  tkrreplot(win1$env$plot)
}

onLeftClick <- function(x, y) {
  xClick <- x
  yClick <- y
  width  <- as.numeric(tclvalue(tkwinfo("reqwidth", win1$env$plot)))
  height <- as.numeric(tclvalue(tkwinfo("reqheight", win1$env$plot)))

  xMin <- parPlotSize[1] * width
  xMax <- parPlotSize[2] * width
  yMin <- parPlotSize[3] * height
  yMax <- parPlotSize[4] * height

  rangeX <- usrCoords[2] - usrCoords[1]
  rangeY <- usrCoords[4] - usrCoords[3]

  imgXcoords <- (xCoords - usrCoords[1]) * (xMax - xMin) / rangeX + xMin
  imgYcoords <- (yCoords - usrCoords[3]) * (yMax - yMin) / rangeY + yMin

  xClick <- as.numeric(xClick) + 0.5
  yClick <- as.numeric(yClick) + 0.5
  yClick <- height - yClick

  xPlotCoord <- usrCoords[1] + (xClick - xMin) * rangeX / (xMax - xMin)
  yPlotCoord <- usrCoords[3] + (yClick - yMin)* rangeY / (yMax - yMin)

  msg <- paste0("Label the point closest to these ",
    "approximate plot coordinates: \n",
    "x = ", format(xPlotCoord, digits = 2),
    ", y = ", format(yPlotCoord, digits = 2), "?")
  mbval <- tkmessageBox(title =
    "Label Point Closest to These Approximate Plot Coordinates",
    message = msg, type = "yesno", icon = "question")

  if (tclvalue(mbval)== "yes")
    labelClosestPoint(xClick, yClick, imgXcoords, imgYcoords)
}

tkbind(win1$env$plot, "<Button-1>", onLeftClick)
tkconfigure(win1$env$plot, cursor = "hand2")