List boxes in R TclTk

List boxes in R TclTk

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

The following examples illustrate how to use a list box in a Tk window. The first example does not have a scrollbar, so it is simpler.

List box with tk2listbox()

library(tcltk2)

win1 <- tktoplevel()
win1$env$lst <- tk2listbox(win1, height = 4, selectmode = "single")
tkgrid(tk2label(win1, text = "What's your favorite fruit?", justify = "left"),
  padx = 10, pady =c(15, 5), sticky = "w")
tkgrid(win1$env$lst, padx = 10, pady = c(5, 10))
fruits <- c("Apple", "Orange", "Banana", "Pear", "Apricot")
for (fruit in fruits)
  tkinsert(win1$env$lst, "end", fruit)
# Default fruit is Banana.  Indexing starts at zero.
tkselection.set(win1$env$lst, 2)

onOK <- function() {
  fruitChoice <- fruits[as.numeric(tkcurselection(win1$env$lst)) + 1]
  tkdestroy(win1)
  msg <- paste0("Good choice! ", fruitChoice, "s are delicious!")
  tkmessageBox(message = msg)
}
win1$env$butOK <-tk2button(win1, text = "OK", width = -6, command = onOK)
tkgrid(win1$env$butOK, padx = 10, pady = c(5, 15))

The code above produces the following window:

listbox simple

The user can then select their favorite fruit with the mouse:

listbox simple selected

listbox simple OK

The tk2listbox() function automatically adds a scrollbar. With the tcltk version (tklistbox()), you have to add it manually yourself and it requires much more code for the same result. The tk2listbox() function also eases the initial filling of the list and the preselection of items, as it will be done in the second example here under.

Prefilling of a list and deletion of items from a list

Here is a multiple selection list that is prefilled:

win2 <- tktoplevel()
tkgrid(tk2label(win2, text = "Please delete the fruit(s) which you don't like.",
  wraplength = 200, justify = "left"),
  padx = 10, pady = c(15, 5), sticky = "w", columnspan = 2)
# Note that 'selection' uses indices starting at 1, like R and not Tcl/Tk!
win2$env$lst <- tk2listbox(win2,
  values <- c("Apple", "Orange", "Banana", "Pear"),
  selection = 3, height = 4, selectmode = "extended")
tkgrid(win2$env$lst, padx = 10, pady = c(5, 10), sticky = "ew", columnspan = 2)

onDelete <- function() {
  fruitsSel <- as.integer(tkcurselection(win2$env$lst))
  # Warning! We have to delete elements from bottom to top, otherwise
  # as soon as we delete an element in the front of the list, the indices
  # of the remaining items are shifted!
  for (i in rev(fruitsSel))
    tkdelete(win2$env$lst, i)
}
win2$env$butDel <- tk2button(win2, text = "Delete", width = -6,
  command = onDelete)

onOK <- function() {
  fruitsRemaining <- as.character(tkget(win2$env$lst, 0, "end"))
  tkdestroy(win2)
  if (!length(fruitsRemaining)) {
    msg <- "Oh no! You don't like fruits at all, isn't it?"
  } else {
    msg <- paste0("So, you do like these fruits: ",
      paste(fruitsRemaining, collapse = ", "))
  }
  tkmessageBox(message = msg)  
}
win2$env$butOK <-tk2button(win2, text = "OK ", width = -6, command = onOK)

tkgrid(win2$env$butDel, win2$env$butOK, padx = 10, pady = c(10, 15))

Running the code above gives the following window:

listbox delete

Select “Orange” from the list, then press <Ctrl> key and select “Pear”. You got a multiple selection:

listbox delete sel

Click Delete to remove these fruits from the list.

listbox deleted item

Click OK to get this message:

listbox remaining items