% \VignetteIndexEntry{widgetTools Introduction} % \VignetteDepends{widgetTools} % \VignettePackage{widgetTools} \documentclass[12pt]{article} \usepackage{hyperref} \textwidth=6.2in \textheight=8.5in %\parskip=.3cm \oddsidemargin=.1in \evensidemargin=.1in \headheight=-.3in \newcommand{\Robject}[1]{{\texttt{#1}}} \newcommand{\Rfunction}[1]{{\texttt{#1}}} \newcommand{\Rpackage}[1]{{\textit{#1}}} \begin{document} \author{Jianhua Zhang} \title{Building a widget using widgetTools} \maketitle \copyright{2002 Bioconductor} \begin{verbatim} $Id: widgetTools.Rnw 39013 2009-04-20 23:24:12Z p.aboyoun $ \end{verbatim} \tableofcontents \section{Introduction} The purpose of \Rpackage{widgetTools} is to provide a simple interface for users to build interactive widgets. Although the underlying implementation of any widget built using \Rpackage{widgetTools} is through the R \Rpackage{tcltk} package, users of widgetTools do not need to know all of the low-level \texttt{Tcl/Tk} commands, because the widget functionality is encapsulated in S4 classes which interact directly with standard R objects. For example, specifying the layout of some widgets (e.g. buttons and text labels) on a window can be as simple as supplying a list of lists, representing the list of rows of the grid of widgets, instead of using low level \texttt{Tcl/Tk} options to place and align each widget manually. The design of widgetTools follows the Model-View-Controller pattern, described in \texttt{[1]}. This enables the separation of the information content of a widget (model) from the visual representation (view) and the actions/event-handlers associated with the widget (controller), in order to facilitate code reuse. A basic primary widget class \Robject{basicPW} is implemented in order to create \Robject{pWidget} objects (instances of this class) which contain the application data (i.e. the \emph{model}). A widget controller class \Robject{widget} is implemented to keep track of the various \Robject{pWidget} objects (in a nested list structure) and their associated actions/event-handlers. Finally, a \Robject{widgetView} class is implemented to store the low-level widget/window attributes, such as the ID attribute of an object created using the \Robject{tktoplevel} or \Robject{tkwidget} function in the R \Rpackage{tcltk} package. These widget IDs are used to update the visual representation of the widget (view) if the internal data (model) changes. The system is designed in such a way that users only have to deal with \Robject{pWidget} and \Robject{widget} objects. \Robject{widgetView} will be managed by \Robject{widget}. \section{Build a widget} The remainder of the vignette will be used to illustrate the construction of a dialog box which allows the user to: \begin{itemize} \item browse for files (using the \Robject{fileBrowser} function from the \Rpackage{tkWidgets} package), \item read some text labels displayed on the dialog, \item enter some text, \item select an option from a set of radio buttons, \item select an option from a listbox, and \item make some yes/no decisions using checkbuttons (checkboxes). \end{itemize}. The basic steps are as follows: \begin{enumerate} \item Define an R environment for creating an manipulating \Robject{pWidget} objects, (rather than storing everything in the global environment). \item Define several \Robject{pWidget} objects of different types (text label, text entry, button, listbox, textbox, radiobutton and checkbutton (checkbox)) using the functions \Robject{label}, \Robject{entry}, \Robject{button}, \Robject{listBox}, \Robject{textBox}, \Robject{radioButton} and \Robject{checkButton} respectively. \begin{itemize} \item Each of these functions creates a \Robject{pWidget} object (of class \Robject{basicPW}) with appropriate slot values for the particular widget, e.g. the widget type slot (\Robject{wType}) would be ``list'' for a listbox. \end{itemize} \item Define \Robject{pWidgets}, a list of lists of \Robject{pWidget} objects in order to define the layout of widgets on the dialog, where the inner lists in the data structure correspond to rows of widgets on the dialog. \item Use the \Robject{widget} function to construct and display a dialog containing the widgets in the \Robject{pWidgets} list \item Use the \Robject{wValue} accessor function to extract the values specified by the user within the dialog and store them in standard R objects. \end{enumerate} Shorter examples of using some of the \Rpackage{widgetTools} functions can be found in the help files for \Robject{makeViewer} and \Robject{tooltip}. The code shown in this vignette is a little longer, but illustrates the use of almost all of the most common widget elements in one dialog box. Defining an R environment in which to manipulate \Robject{pWidget} objects has several advantages. Not only does it avoid the risk of overwriting/masking existing objects in the global workspace, but it has the advantage that objects from this environment can be obtained using the \Robject{get} function, thus avoiding the unnecessary copying of data in memory which would result from excessive passing (by value) of \Robject{pWidget} objects between functions every time they needed to be updated. <<>>= library(widgetTools) PWEnv <- new.env(hash = TRUE, parent = parent.frame(1)) @ Then, we can create the \Robject{pWidget} objects that define the widget elements that are going to be rendered. A \Robject{pWidget} is an object of basicPW class defined below: \begin{verbatim} |-----------------------| | basicPW | |-----------------------| | wName: string | | wType: string | | wValue: string | | wWidth: string | | wFuns: list | | wPreFun: function | | wPostFun: function | | wView: widgetView | | wEnv: environment | | wNotify: list | |-----------------------| \end{verbatim} Where \begin{itemize} \item \Robject{wName} - a character string for the name to be associated with a given pWidget; \item \Robject{wType} - type of a \texttt{Tk} widget ("text" for text box, "list" for list box, "entry" for entry box, "button" for button, "radio" for radiobutton, "check" for checkbutton,"label" for text label,...); \item \Robject{wValue}: an undefined data type used to store information that will be displayed on the interface or updated using values obtained from the interface; \item \Robject{wWidth} - the physical width (in pixels) of the \Robject{pWidget} to be rendered. Applicable to most \Robject{pWidget} objects; \item \Robject{wFuns} - functions that will be associated with the given pWidget and invoked when a given operation to the \Robject{pWidget} takes place (e. g. clicked, get focused ...); \item \Robject{wPreFun} - the function defining the operations to be performed on the text of the \Robject{pWidget} before rendering the text; \item \Robject{wPostFun} - the function defining the operations to be performed on the text of the \texttt{pWidget} upon existing; \item \Robject{wView} - a \texttt{widgetView} object associated with each \Robject{pWidget} that will be used for updating the data displayed; \item \Robject{wEnv} - an R environment object where data updating and retrieval will take place; \item \Robject{wNotify} - a list of functions defining the actions to be performed when the value of the \texttt{pWidget} changes. \end{itemize} The following code creates several \Robject{pWidget} objects of different types: <<>>= label1 <- label(wName = "label1", wValue = "File Name: ", wEnv = PWEnv) entry1 <- entryBox(wName = "entry1", wValue = "Feed me using browse", wEnv = PWEnv) browse2Entry1 <- function(){ tempValue <- tclvalue(tkgetOpenFile()) temp <- get(wName(entry1), env = PWEnv) wValue(temp) <- paste(tempValue, sep = "", collapse = ";") assign(wName(entry1), temp, env = PWEnv) } button1 <- button(wName = "button1", wValue = "Browse", wFuns = list(command = browse2Entry1), wEnv = PWEnv) list1 <- listBox(wName = "list1", wValue = c(Option1 = TRUE, Option2 = FALSE, Option3 = FALSE), wEnv = PWEnv) text1 <- textBox(wName = "text1", wValue = "Feed me something", wEnv = PWEnv) label2 <- label(wName = "label2", wValue = "Select one: ", wEnv = PWEnv) radios1 <- radioButton(wName = "radios1", wValue = c(radio1 = TRUE, radio2 = FALSE, radio3 = FALSE), wEnv = PWEnv) label3 <- label(wName = "label3", wValue = "Select one to many: ", wEnv = PWEnv) checks1 <- checkButton(wName = "checks1", wValue = c(check1 = TRUE, check22 = FALSE, check3 = FALSE), wEnv = PWEnv) @ The \Robject{fileBrowser} function from the \Rpackage{tkWidgets} package is specified to be the action associated with pushing the \texttt{Browse} button (\Robject{button1}). The file name returned by the \Robject{fileBrowser} function is then displayed in the entry box (\Robject{entry1}) by assigning the filename to the \Robject{wName} slot in \Robject{entry1}. All the data manipulations will be done within the environment previously defined. At this time the environment does not contain the \Robject{pWidget} objects. They will be assigned to the environment by the system later. It is important that the \Robject{wName} for each widget element be the same as the name of the corresponding \Robject{pWidget} and to be unique. Values for {\texttt{list1}}, {\texttt{radios1}}, and {\texttt{checks1}} are named vectors. Names of the values will be displayed either as elements (for listboxes) or text descriptions (for radio and check buttons) of the corresponding widget elements to be rendered. Event-handlers can be defined for each \Robject{pWidget} object, using the \Robject{wFuns} parameter of the appropriate \Robject{pWidget} constructor function, e.g. \Robject{button}. In the case of a button, the a function would be defined to be executed as the command for that button, and it would be specified in the \Robject{wFuns} list passed to the \Robject{button} constructor function as an attribute with name \Robject{command}. Any button defined is assumed to have a command element in its \Robject{wFuns} list. List boxes, radio buttons, check buttons, and text entries will have default functions defined for them to update their values when the corresponding widget element is pressed or text is changed. If you want anything additional to that, you will have to define a function and list that function in funs. Adding functions to the \Robject{wFuns list} is not fully implemented right now but will be available later. Radio buttons and check buttons are defined as groups with the name and value of individual radio button or check button defined by the vector for value (see \Robject{radios1} and \Robject{checks1}). \Robject{pWidget} objects have \Robject{set} and \Robject{get} functions to access and modify the variables. For example, we can get and set the value for the type of a \Robject{pWidget}: <<>>= wName(label1) wName(label1) <- "YYY" wName(label1) wName(label1) <- "label1" @ To specify the layout of the widgets on the interface, the \Robject{pWidget} objects need to be put in a list of lists in such a way that the inner lists in the data structure correspond to rows in the grid of widgets on the interface. Each element (list) in the outer list will be treated as a unit and rendered one after another vertically and each element (\Robject{pWidget}) in the inner list will be treated as a unit and rendered one after another horizontally. For example, if we want to have the first three \Robject{pWidget}s namely {\texttt{label1}}, {\texttt{entry1}}, and {\texttt{button1}} to appear in one row on top of the screen, {\texttt{list1}} and {\texttt{text1}} in one row below, {\texttt{label2}} and {\texttt{radios1}} in another row, and {\texttt{label3}} and {\texttt{checks1}} in yet another row, we will need to put the \Robject{pWidgets} in a list like the following. Naming of the lists for \Robject{pWidget} groups is arbitrary. <<>>= pWidgets <- list(topRow = list(label1 = label1, entry1 = entry1, button1 = button1), textRow = list(list1 = list1, text1 = text1), radGroup = list(label2 = label2, radios1 = radios1), chkGroup = list(label3 = label3, checks1 = checks1)) @ When we have the list of \Robject{pWidget}s ready, we can proceed to create a \Robject{widget} object that will construct a \Robject{widgetView} object to render the widget elements. A \Robject{widget} object is defined as follows: \begin{verbatim} |-----------------------| | widget | |-----------------------| | pWidgets: list | | funs: list | | preFun: function | | postFun: function | | wTitle: string | | env: environment | |-----------------------| \end{verbatim} where \begin{itemize} \item \Robject{pWidgets} - a list of lists with each element being a \Robject{pWidget} object to appear on the main interface represented by the \Robject{widget} object.; \item \Robject{funs} - a list of functions that will be associated with buttons on the interface to be created. The name of a function in the list will be the text which appears on the button and the function will be executed when the button is pressed; \item \Robject{preFun} - a function that will be executed when the \texttt{Tk} widget is constructed; \item \Robject{postFun} - a function that will be executed when the \texttt{Tk} widget is destroyed. \item \Robject{wTitle} - a character string for the title to be displayed in the title bar of the main interface represented by the \Robject{widget} object. \item \Robject{env} - an R environment object within which the original values for the main \Robject{widget} object will be stored and updating and retrieval of the values will take place \end{itemize} The following code constructs a {\texttt{widget}} object. In practice it is not necessary to use \texttt{if(interactive())\{...\}}. This is only included so that this vignette can be constructed automatically using \texttt{Sweave} without necessarily having a graphics device open. <<>>= if(interactive()){ aWidget <- widget(wTitle = "A test widget", pWidgets, funs = list(), preFun = function() print("Hello"), postFun = function() print("Bye"), env = PWEnv) } @ \includegraphics{widget.pdf} Now, we can change the values of the \texttt{pWidget} objects we have created through the interface. Click the \texttt{Browse} button to select a file using the \texttt{fileBrowser} widget from the \Rpackage{tkWidgets} package. After clicking the \texttt{Finish} button on the \texttt{fileBrowser} widget (not shown), the File Name entry box on the test widget above will contain the file name which was selected (and returned by the \texttt{fileBrowser} function). Try selecting an item from the list box, selecting a radio button, toggling the check buttons, and typing in a few words in the text box. Then click the \texttt{Finish} button on the test widget, and a \Robject{widget} object will be constructed. The \Robject{widget} object contains \texttt{pWidget} objects, the value of whose elements will be modified depending what has been performed on the interface we just played with. The values for {\texttt{entry1}}, {\texttt{list1}}, {\texttt{text1}}, {\texttt{radios1}}, and {\texttt{checks1}} now become: <<>>= if(interactive()){ wValue(pWidgets(aWidget)[["topRow"]][["entry1"]]) wValue(pWidgets(aWidget)[["textRow"]][["list1"]]) wValue(pWidgets(aWidget)[["textRow"]][["text1"]]) wValue(pWidgets(aWidget)[["radGroup"]][["radios1"]]) wValue(pWidgets(aWidget)[["chkGroup"]][["checks1"]]) } @ \section*{References} \begin{enumerate} \item{MVC} Krasner, G. and Pope, S. 1988. Huynen, M.~A. and Bork, P. 1998. A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 system. {\em Journal of Object Oriented Programming}, vol. 1, no. 3, pp. 26--49, 1988. \end{enumerate} \end{document}