#' Select a subset of data through a GUI to be brushed
#'
#' We use a GUI created by \pkg{qtbase} to subset the data based on a given
#' categorical variable. Specifically, we choose certain values of the variable
#' (using the mouse or keyboard) and all the observations which have the same
#' values on this variable will be brushed. This selector can link to any plots
#' based on a mutaframe created by \code{\link{qdata}}.
#'
#' The GUI supports multiple selections when we hold the Shift or Ctrl key. If
#' the character string in the text input box matches with multiple items in the
#' list, all of them will be selected.
#'
#' When we select items in the list, usually a plot based on the same data will
#' get brushed accordingly. On the other hand, when we click on a plot, the
#' corresponding items in the list will be selected as well.
#' @inheritParams qbar
#' @param vars a character string or an integer as a column index, or a variable
#' name (without quotes): the variable to be displayed in the data selector
#' (if not specified, the first non-numeric variable will be used; if all
#' columns are numeric, the first column will be used)
#' @return \code{NULL} (a GUI will pop up)
#' @author Yihui Xie and Jason Crowley
#' @seealso \code{\link{qdata}}
#' @export
#' @example inst/examples/record_selector-ex.R
record_selector = function(vars, data) {
l = as.list(match.call()[-1])
if (is.null(l$vars)) {
vars = names(data)[!(sapply(as.data.frame(data), is.numeric))][1]
if (is.na(vars))
vars = names(data)[1]
} else if (is.symbol(l$vars)) vars = as.character(l$vars)
if (is.numeric(vars)) vars = names(data)[vars]
## vars should be of length 1
x = data[, vars[1]]
xx = as.data.frame(data[!duplicated(x), vars[1], drop = FALSE])
# instantiate the window
w = Qt$QWidget()
w$setWindowTitle(paste("Record Selector:", vars))
# setup the table and link the data model to it
lst = Qt$QListView()
lst$setSelectionMode(Qt$QAbstractItemView$ExtendedSelection)
lst$setAlternatingRowColors(TRUE)
model = qdataFrameModel(xx)
lst$setModel(model)
selModel = lst$selectionModel()
change1 = change2 = FALSE
# set up a handler to update the .brushed column when the index is changed in
# the table
qconnect(selModel, "selectionChanged", function(filler1, filler2) {
if (change1) return()
change2 <<- TRUE
currLvls = sapply(selModel$selectedIndexes(), function(i) i$data())
selected(data) = (x %in% currLvls)
change2 <<- FALSE
})
# set up a search bar and attach an auto-completer to it that is populated
# with the levels of the variable in question
le = Qt$QLineEdit()
comp = Qt$QCompleter(as.character(xx[,1]))
comp$setCaseSensitivity(Qt$Qt$CaseInsensitive)
le$setCompleter(comp)
select_items = function(idx) {
sel = Qt$QItemSelection(model$index(idx[1], 0), model$index(idx[1], 0))
if ((n <- length(idx)) > 1) {
for (i in 2:n) {
sel$select(model$index(idx[i],0), model$index(idx[i], 0))
sel$select(model$index(idx[i],0), model$index(idx[i], 0))
}
}
selModel$select(sel, Qt$QItemSelectionModel$ClearAndSelect)
}
# set up a handler that will update the selection in the table when return is
# pressed in the line edit field. clears selection if the text doesn't match a
# level of the variable. updates .brushed
qconnect(le, "returnPressed", function() {
if (change1) return()
change2 <<- TRUE
idx = if (le$text != "") {
grep(le$text, as.character(xx[, 1]))
} else integer(0)
if(length(idx) > 1) select_items(idx-1) else selModel$clear()
change2 <<- FALSE
})
## let the GUI respond to changes in .brushed too
d.idx = add_listener(data, function(i, j) {
if (change2) return()
change1 <<- TRUE
if (j == '.brushed') {
idx = which(xx[, 1] %in% x[selected(data)])
if (length(idx)) {
select_items(idx-1)
lst$scrollTo(model$index(idx[1], 0), Qt$QAbstractItemView$EnsureVisible)
} else selModel$clear()
}
change1 <<- FALSE
})
qconnect(w, 'destroyed', function(x) {
remove_listener(data, d.idx)
})
# set the layout of the widgets, and attach it to the window
lyt = Qt$QVBoxLayout()
lyt$addWidget(lst)
lyt$addWidget(le)
w$setLayout(lyt)
# make the window visible
w$show()
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.