-
Notifications
You must be signed in to change notification settings - Fork 6
New theme system
This is a description of the changes for the new ggplot2 theming system, to be in version 0.9.2. It shouldn't break any code from the previous versions of ggplot2, but it adds some new features, and makes some things easier.
The functions theme_text
, theme_rect
, theme_line
, and theme_segment
have been renamed to element_xx
.
These new names are less confusing than the old ones -- these are theme elements as opposed to themes, like theme_grey()
.
The theme_xx
functions still work, but they now print deprecation messages.
Previously, theme_line()
and theme_segment()
were distinct. Now, there is only element_line()
-- the segment version was no longer necessary and was removed.
The theme system supports inheritance.
That is, theme elements can inherit properties from other theme elements.
For example, axis.title.x
and axis.title.y
inherit their properties from axis.title
, which in tun inherits properties from text
.
If for axis.title.x
, you set just the size with:
opts(axis.title.x = element_text(size = 8))
Then axis.title.x
will inherit all the other properties (family, face, colour, etc) but size
will be set to 8.
Some elements are never used directly, but are only used for inheritance.
Examples include text
and the axis.title
.
All of the elements with text inherit from text
, directly or indirectly. Similarly, all elements that have rectangle grobs inherit from rect
.
The inheritance is defined by a data structure .element_tree
, which you can see at the bottom of theme-elements.r.
Here is a graph of the inheritance structure, generated with igraph:
Multiple inheritance is also supported by this system but it's not actually used for any existing elements.
The function calc_element
will the calculate the properties of an element (with inheritance) and return an element object:
ggplot2:::calc_element("axis.ticks.y", theme_grey())
# $colour
# [1] "grey50"
#
# $size
# [1] 0.5
#
# $linetype
# [1] 1
#
# attr(,"class")
# [1] "element" "element_line"
Theme objects are now simple nested lists.
Previously, a theme (such as the one returned by theme_grey()
) was a list of mostly functions, which return grobs.
Functions like theme_rect()
would return functions which returned grobs.
Now a theme is a list of lists.
element_rect()
returns a list (of class element_rect
), instead of a function that returns a grob.
You can see how themes are specified in theme-defaults.r.
Presently, theme_grey
is the default theme, and theme_bw
is simply a modification of it.
Relative sizing is specified with rel()
.
For example, axis.text
inherits from text
, and this would make axis.text
have a font size that is half the size of text
.
opts(axis.text = element_text(size=rel(0.5)))
It would also directly inherit all other properties, like family
and colour
.
You can still use opts()
to modify theme settings for a particular graph.
To modify a theme object, you can do something like the following:
mytheme <- theme_grey()
mytheme$text <- element_text(family="Times", face="plain", colour="red",
size=16, hjust=0.5, vjust=0.5, angle=0, lineheight=0.9)
This is actually how theme_bw()
is now defined.
Notice that, for a top-level element like text
, it's necessary to specify all the properties like family
, face
, colour
, and so on; if these properties aren't specified, they default to NULL.
Another way to modify a theme is to change the nested list items directly. I'm not sure this is a good way to do it in the long term, though. For example, this would have the same effect as above:
mytheme <- theme_grey()
mytheme$text$family <- "Times"
mytheme$text$colour <-"red"
mytheme$text$size <- 16
It seems to me that there should be a better way of modifying individual properties of an element, but it's not obvious to me at this point how to do that.
It's now possible to independently control the x and y components of following:
-
panel.grid.major.x
and.y
-
panel.grid.minor.x
and.y
-
axis.ticks.x
and.y
-
axis.line.x
and.y
For example, panel.grid.major.x
inherits from panel.grid.major
, and so if the .x version is not specified, it simply inherits all properties from panel.grid.major
.
It is now possible to disable just horizontal or just vertical grid lines without any ugly hacks. For example:
# Remove horizontal grid lines
qplot(1:5, 1:5) +
opts(panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank())
This is the code that generates the element inheritance graph.
build_element_graph <- function(tree) {
require(igraph)
inheritdf <- function(name, item) {
if (length(item$inherits) != 0)
data.frame(child = name, parent = item$inherits)
else
data.frame()
}
edges <- rbind.fill(mapply(inheritdf, names(tree), tree))
# Explicitly add vertices (since not all are in edge list)
vertices <- data.frame(name = names(tree))
graph.data.frame(edges, vertices = vertices)
}
g <- build_element_graph(.element_tree)
V(g)$label <- V(g)$name
png('element_graph.png', width=700, height=600)
set.seed(323)
par(mar=c(0,0,0,0)) # Remove unnecessary margins
plot(g, layout=layout.fruchterman.reingold, vertex.size=4, vertex.label.dist=.25)
dev.off()