Rev 61990 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
% File src/library/grid/vignettes/grobs.Rnw% Part of the R package, https://www.R-project.org% Copyright 2001-13 Paul Murrell and the R Core Team% Distributed under GPL 2 or later\documentclass[a4paper]{article}\usepackage{Rd}% \VignetteIndexEntry{Working with grid grobs}% \VignettePackage{grid}\newcommand{\grid}{\pkg{grid}}\newcommand{\grob}{\code{grob}}\newcommand{\gTree}{\code{gTree}}\newcommand{\gPath}{\code{gPath}}\newcommand{\lattice}{\CRANpkg{lattice}}\setlength{\parindent}{0in}\setlength{\parskip}{.1in}\setlength{\textwidth}{140mm}\setlength{\oddsidemargin}{10mm}\title{Modifying \grid{} \code{grob}s}\author{Paul Murrell}\begin{document}\maketitle<<echo=FALSE, results=hide>>=library(grDevices)library(grid)ps.options(pointsize = 12)options(width = 60)@There is a distinction between \grob{}s which are just storedin user-level \R{} objects and \grob{}s which representdrawn output (i.e., \grob{}s on the \grid{} display list).There is a naming convention that \code{grid.*()} functions are(mainly) used for their side-effect of producing output ormodifying existing output (they create/affect \grob{}s on the display list).Functions of the form \code{*Grob()} are used for their return value;the \grob{} that they create/modify.For example, the following creates a \grob{} and then modifies it, butperforms absolutely no drawing; this is purely manipulating adescription of a graphical object.<<>>=gl <- linesGrob()gl <- editGrob(gl, gp = gpar(col = "green"))@The next example produces output. A \grob{} is returned,but that \grob{} is just a description of the output that was drawnand has no direct link to the output. It is possible to access thegrob representing the output byusing the \grob{}'s \code{name}. In order to access a \grob{}which represents drawn output (i.e., a \grob{} on the display list),you must specify a \gPath{}. The \gPath{} should be created using the\code{gPath()} function for writing scripts, but in interactive use, itis possible to specify the \gPath{} directly as a string. The code belowshows both approaches.<<results=hide>>=grid.newpage()grid.lines(name = "lines")grid.edit(gPath("lines"), gp = gpar(col = "pink"))grid.edit("lines", gp = gpar(col = "red"))@Complex graphical objects are provided by the \gTree{} class. A\gTree{} is a \grob{} which may have other \grob{}s as children. The\code{xaxis} and \code{yaxis} \grob{}s provided by \grid{} areexamples of \gTree{}s; the children of an axis include a lines \grob{}for the tick-marks and a text \grob{} for the tick-mark labels. Thefunction \code{childNames()} can be used to list the names of thechildren of a \gTree{}. When dealing with these hierarchical objects,more complex \gPath{}s can be used to access children of a \gTree{}.In the following example, an x-axis is drawn, then the xaxis itself isedited to modify the locations of the tick-marks, then the xaxis'stext child is edited to modify the location of the labels on thetick-marks.<<results=hide>>=grid.newpage()pushViewport(viewport(w = .5, h = .5))grid.rect(gp = gpar(col = "grey"))grid.xaxis(name = "myxaxis")grid.edit("myxaxis", at = 1:4/5)grid.edit(gPath("myxaxis", "labels"), y = unit(-1, "lines"))@This next example extends the idea a step further to edit the child ofa child of a \gTree{}. It also shows the use of the \gTree{} functionto construct a simple \gTree{} (this is just creating an instance ofthe \gTree{} class -- it is also possible to extend the \gTree{} classin order to provide specialised behaviour for drawing and otherthings; more on this later). Finally, the example demonstrates how\gPath{}s of depth greater than 1 can be specified directly as astring.<<results=hide>>=grid.newpage()pushViewport(viewport(w = .5, h = .5))myplot <-gTree(name = "myplot",children = gList(rectGrob(name = "box", gp = gpar(col = "grey")),xaxisGrob(name = "xaxis")))grid.draw(myplot)grid.edit("myplot::xaxis", at = 1:10/11)grid.edit("myplot::xaxis::labels", label = round(1:10/11, 2))grid.edit("myplot::xaxis::labels", y = unit(-1, "lines"))@\code{"grobwidth"} units require a \grob{} to give the width of.There are two ways to specify the \grob{}. The following exampleshows the most obvious method of simply supplying a \grob{}. Noticethat if you modify \code{gt} it will have no effect on the width ofthe drawn rectangle.<<results=hide>>=grid.newpage()gt <- grid.text("Hi there")grid.rect(width = unit(1, "grobwidth", gt))@In order to allow a \code{"grobwidth"} unit to track changes in the\grob{}, it is possible to specify a \gPath{} rather than a \grob{} asthe data for a \code{"grobwidth"} unit. The following examplemodifies the previous example to use a \gPath{}. Now, the width ofthe rectangle changes when the width of the underlying \grob{}changes.<<results=hide>>=grid.newpage()gt <- grid.text("Hi there", name = "sometext")grid.rect(width = unit(1, "grobwidth", "sometext"))grid.edit("sometext", label = "Something different")@One issue in the evaluation of \code{"grobwidth"} units involvesestablishing the correct ``context'' for a \grob{} when determining its width(if a \grob{} has a viewport in its \code{vp} slot then that viewport getspushed before the \grob{} is drawn; that viewport should also be pushedwhen determining the widthof the \grob{}).To achieve this there are\code{preDrawDetails()}, \code{drawDetails()}, and \code{postDrawDetails()}generic functions.(suggestions for better names welcome!). The idea is thatpushing and popping of viewports should occur in the \code{pre} and\code{post} generics, and any actual drawing happens in the main\code{drawDetails()} generic. This allows the code that calculates a\grob{} width to call the \code{preDrawDetails()} in order to establishthe context in which the \grob{} would be drawn before calculating itswidth. The following example shows a test case; a \grob{} is created(extending to a new class to allow specific methods to be written),and methods are provided which establish a particular contextfor drawing the \grob{}. These methods are used both in the drawingof the \grob{} and in the calculation of the \grob{}'s width (whendrawing a bounding rectangle).<<results=hide>>=grid.newpage()mygrob <- grob(name = "mygrob", cl = "mygrob")preDrawDetails.mygrob <- function(x)pushViewport(viewport(gp = gpar(fontsize = 20)))drawDetails.mygrob <- function(x, recording = TRUE)grid.draw(textGrob("hi there"), recording = FALSE)postDrawDetails.mygrob <- function(x) popViewport()widthDetails.mygrob <- function(x) unit(1, "strwidth", "hi there")grid.draw(mygrob)grid.rect(width = unit(1, "grobwidth", mygrob))@This next example shows a slightly different test case where thestandard \code{preDrawDetails()} and \code{postDrawDetails()} methodsare used, but the \grob{} does have a \code{vp} slot so these methodsdo something. Another interesting feature of this example is theslightly more complex \gTree{} that is created. The \gTree{} has a\code{childrenvp} specified. When the \gTree{} is drawn, thisviewport is pushed and then ``up''ed before the children of the\gTree{} are drawn. This means that the children of the \gTree{} canspecify a \code{vpPath} to the viewport they should be in. Thisallows the parent \gTree{} to create a suite of viewports and thenchildren of the \gTree{} select which one they want -- this can bemore efficient than having each child push and pop the viewports itneeds, especially if several children are drawn within the sameviewport. Another, more realistic example of this is given later.<<results=hide>>=grid.newpage()mygtree <- gTree(name = "mygrob",childrenvp = viewport(name = "labelvp",gp = gpar(fontsize = 20)),children = gList(textGrob("hi there", name = "label",vp = "labelvp")),cl = "mygtree")widthDetails.mygtree <- function(x)unit(1, "grobwidth", getGrob(x, "label"))grid.draw(mygtree)grid.rect(width = unit(1, "grobwidth", mygtree))@Constructing a description of a \code{frame} \grob{} must bedone via \code{packGrob()} and \code{placeGrob()}. The following exampleshows the construction of a simple frame consisting of twoequal-size columns.<<results = hide>>=grid.newpage()fg <- frameGrob(layout = grid.layout(1, 2))fg <- placeGrob(fg, textGrob("Hi there"), col = 1)fg <- placeGrob(fg, rectGrob(), col = 2)grid.draw(fg)@This next example constructs a slightly fancier frame using packing.<<results=hide>>=grid.newpage()pushViewport(viewport(layout = grid.layout(2, 2)))drawIt <- function(row, col) {pushViewport(viewport(layout.pos.col = col, layout.pos.row = row))grid.rect(gp = gpar(col = "grey"))grid.draw(fg)upViewport()}fg <- frameGrob()fg <- packGrob(fg, textGrob("Hi there"))fg <- placeGrob(fg, rectGrob())drawIt(1, 1)fg <- packGrob(fg, textGrob("Hello again"), side = "right")drawIt(1, 2)fg <- packGrob(fg, rectGrob(), side = "right", width = unit(1, "null"))drawIt(2, 2)@In order to allow frames to update when the objects packed within themare modified, there is a \code{dynamic} argument to \code{packGrob()}(and \code{grid.pack()}). The following extends the previous exampleto show how this might be used. Another feature of this example isthe demonstration of ``non-strict'' searching that occurs in the\code{grid.edit()} call; the \grob{} called \code{"midtext"} is not atthe top-level, but is still found. Something like\code{grid.get("midtext", strict = TRUE)} would fail.<<results=hide>>=grid.newpage()fg <- frameGrob()fg <- packGrob(fg, textGrob("Hi there"))fg <- placeGrob(fg, rectGrob())fg <- packGrob(fg, textGrob("Hello again", name = "midtext"),side = "right", dynamic = TRUE)fg <- packGrob(fg, rectGrob(), side = "right", width = unit(1, "null"))grid.draw(fg)grid.edit("midtext", label = "something much longer")@There have been a few examples already which have involved creating a\gTree{}. This next example explicitly demonstrates this technique.A \gTree{} is created with two important components. The\code{childrenvp} is a \code{vpTree} consisting of a\code{"plotRegion"} viewport to provide margins around a plot and a\code{"dataRegion"} viewport to provide x- and y-scales. The\code{"dataRegion"} gets pushed within the \code{"plotRegion"} andboth are pushed and then ``up''ed before the children are drawn. The\code{children} of the \gTree{} are an \code{xaxis} and a \code{yaxis}both drawn within the \code{"dataRegion"}, and a \code{rect} drawnaround the border of the \code{"plotRegion"}. A further feature ofthis example is the use of the \code{addGrob()} and\code{removeGrob()} functions to modify the \gTree{}. The firstmodification involves adding a new child to the \gTree{} which is aset of points drawn within the \code{"dataRegion"}. The secondmodification involves adding another set of points with a differentsymbol (NOTE that this second set of points is given a name so that itis easy to identify this set amongst the children of the \gTree{}).The final modification is to remove the second set of points from the\gTree{}.<<results=hide>>=grid.newpage()pushViewport(viewport(layout = grid.layout(2, 2)))drawIt <- function(row, col) {pushViewport(viewport(layout.pos.col = col, layout.pos.row = row))grid.rect(gp = gpar(col = "grey"))grid.draw(gplot)upViewport()}gplot <- gTree(x = NULL, y = NULL,childrenvp = vpTree(plotViewport(c(5, 4, 4, 2), name = "plotRegion"),vpList(viewport(name = "dataRegion"))),children = gList(xaxisGrob(vp = "plotRegion::dataRegion"),yaxisGrob(vp = "plotRegion::dataRegion"),rectGrob(vp = "plotRegion")))drawIt(1, 1)gplot <- addGrob(gplot, pointsGrob(vp = "plotRegion::dataRegion"))drawIt(1, 2)gplot <- addGrob(gplot, pointsGrob(name = "data1", pch = 2,vp = "plotRegion::dataRegion"))drawIt(2, 1)gplot <- removeGrob(gplot, "data1")drawIt(2, 2)@This next example provides a simple demonstration of saving andloading \grid{} \grob{}s. It is also a nice demonstration that\grob{}s copy like normal \R{} objects.<<results=hide>>=gplot <- gTree(x = NULL, y = NULL,childrenvp = vpTree(plotViewport(c(5, 4, 4, 2), name = "plotRegion"),vpList(viewport(name = "dataRegion"))),children = gList(xaxisGrob(vp = "plotRegion::dataRegion"),yaxisGrob(vp = "plotRegion::dataRegion"),rectGrob(vp = "plotRegion")))save(gplot, file = "gplot1")gplot <- addGrob(gplot, pointsGrob(vp = "plotRegion::dataRegion"))save(gplot, file = "gplot2")grid.newpage()pushViewport(viewport(layout = grid.layout(1, 2)))pushViewport(viewport(layout.pos.col = 1))load("gplot1")grid.draw(gplot)popViewport()pushViewport(viewport(layout.pos.col = 2))load("gplot2")grid.draw(gplot)popViewport()@This next example just demonstrates that it is possible to use a\gPath{} to access the children of a \gTree{} when editing. This isthe \code{editGrob()} equivalent of an earlier example that used\code{grid.edit()}. One useful application of this API is the abilityto modify the appearance of quite precise elements of a large, complexgraphical object by editing the \code{gp} slot of a child (of a child\ldots{}) of a \gTree{}.<<results = hide>>=myplot <- gTree(name = "myplot",children = gList(rectGrob(name = "box", gp = gpar(col = "grey")),xaxisGrob(name = "xaxis")))myplot <- editGrob(myplot, gPath = "xaxis", at = 1:10/11)myplot <- editGrob(myplot, gPath = "xaxis::labels", label = round(1:10/11, 2))myplot <- editGrob(myplot, gPath = "xaxis::labels", y = unit(-1, "lines"))grid.newpage()pushViewport(viewport(w = .5, h = .5))grid.draw(myplot)@The following example demonstratesthe use of the \code{getGrob()} and \code{grid.get()} (along with\gPath{}s) to access \grob{}s.<<results = hide>>=myplot <- gTree(name = "myplot",children = gList(rectGrob(name = "box", gp = gpar(col = "grey")),xaxisGrob(name = "xaxis")))getGrob(myplot, "xaxis")myplot <- editGrob(myplot, gPath="xaxis", at=1:10/11)getGrob(myplot, "xaxis::labels")grid.newpage()pushViewport(viewport(w=.5, h=.5))grid.draw(myplot)grid.get("myplot")grid.get("myplot::xaxis")grid.get("myplot::xaxis::labels")@There is also an API for (re)setting children of a \gTree{}or any drawn \grob{}. This is not intended for general user use,but provides a simple way for developers to perform modificationsto the structure of a \gTree{} by doing something like \ldots{}\begin{verbatim}grob <- getGrob(<spec>)<modify grob>setGrob(<spec>, grob)\end{verbatim}This approach is used in the implementation of packing and placing grobs.The following example shows some simple usage of the\code{setGrob()} and \code{grid.set()} functions to replacechildren of a \gTree{} with different \grob{}s. NOTE that currentlysuch replacement can only occur if the name of the new \grob{}is the same as the name of the old \grob{}.<<results=hide>>=myplot <- gTree(name = "myplot",children = gList(rectGrob(name = "box", gp = gpar(col = "grey")),xaxisGrob(name = "xaxis")))myplot <- setGrob(myplot, "xaxis", rectGrob(name = "xaxis"))grid.newpage()pushViewport(viewport(w = .5, h = .5))grid.draw(myplot)grid.set("myplot::xaxis", xaxisGrob(name = "xaxis", at = 1:3/4))grid.set("myplot::xaxis::labels",textGrob(name = "labels", x = unit(1:3/4, "native"),y = unit(-1, "lines"), label = letters[1:3]))myplot <- setGrob(grid.get("myplot"), "xaxis::labels",circleGrob(name = "labels"))grid.newpage()pushViewport(viewport(w = .5, h = .5))grid.draw(myplot)@This next example just shows more complex use of the add/removefacilities for modifying \grob{}s. Again, \code{addGrob()} and\code{removeGrob()} are for constructing descriptions of graphicalobjects and \code{grid.add()} and \code{grid.remove()} are formodifying drawn output. Of particular note are the last two linesinvolving \code{grid.remove()}. The first point is that there aremultiple \grob{}s on the display list with the same name. The exampleonly affects the first one it finds; this could easily be extended toaffect the display list ``globally'' (for children of \gTree{}s, therecannot be multiple children with the same name so the issue does notarise). The last line is interesting because it actually erases the\grob{} named \code{"plot1"} from the display list altogether (well,the first instance on the display list of a \grob{} called\code{"plot1"} anyway).<<results=hide>>=drawIt <- function(row, col) {pushViewport(viewport(layout.pos.col = col, layout.pos.row = row))grid.rect(gp = gpar(col = "grey"))grid.draw(gplot)upViewport()}gplot <- gTree(name = "plot1",childrenvp = vpTree(plotViewport(c(5, 4, 4, 2), name = "plotRegion"),vpList(viewport(name = "dataRegion"))),children = gList(xaxisGrob(name = "xaxis", vp = "plotRegion::dataRegion"),yaxisGrob(name = "yaxis", vp = "plotRegion::dataRegion"),rectGrob(name = "box", vp = "plotRegion")))grid.newpage()pushViewport(viewport(layout = grid.layout(2, 2)))drawIt(1, 1)grid.add("plot1", pointsGrob(0.5, 0.5, name = "data1",vp = "plotRegion::dataRegion"))grid.add("plot1::xaxis",textGrob("X Axis", y = unit(-2, "lines"), name = "xlab"))grid.edit("plot1::xaxis::xlab", y = unit(-3, "lines"))gplot <- grid.get("plot1")gplot <- addGrob(gplot, gPath = "yaxis",textGrob("Y Axis", x = unit(-3, "lines"), rot = 90,name = "ylab"))drawIt(1, 2)gplot <- removeGrob(gplot, "xaxis::xlab")drawIt(2, 1)grid.remove("plot1::data1")grid.remove("plot1")@The next example is just a \code{grid.place()} and \code{grid.pack()}equivalent of an earlier example involving\code{placeGrob()} and \code{packGrob()}. The interesting feature isthat each action is reflected in the output as it occurs.<<results=hide>>=grid.newpage()grid.frame(name = "myframe", layout = grid.layout(1, 2))grid.place("myframe", textGrob("Hi there"), col = 1)grid.place("myframe", rectGrob(), col = 2)grid.newpage()grid.frame(name = "frame2")grid.pack("frame2", textGrob("Hi there"))grid.place("frame2", rectGrob())grid.pack("frame2", textGrob("Hello again"), side = "right")grid.pack("frame2", rectGrob(), side = "right", width = unit(1, "null"))@\end{document}