Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

improved print methods for theme and element classes #693

Closed
wants to merge 3 commits into
from

Conversation

Projects
None yet
2 participants
Contributor

jrnold commented Oct 14, 2012

I added new print methods for theme and element objects which are easier to understand than the existing methods. Additionally, the output of print can be evaluated to recreate the object. This makes it easy to create a new theme by printing an existing theme and making edits to the output.

Example of print output for theme_gray

> print(theme_gray()$rect)
element(fill = "white", colour = "black", size = 0.5, linetype = 1) 
> print(theme_gray())
theme(line = element(colour = "black", size = 0.5, linetype = 1, lineend = "butt"),
rect = element(fill = "white", colour = "black", size = 0.5, linetype = 1),
text = element(family = "", face = "plain", colour = "black", size = 12, hjust = 0.5, vjust = 0.5, angle = 0, lineheight = 0.9),
axis.text = element(colour = "grey50", size = rel(0.8)),
strip.text = element(size = rel(0.8)),
axis.line = element(),
axis.text.x = element(vjust = 1),
axis.text.y = element(hjust = 1),
axis.ticks = element(colour = "grey50"),
axis.title.x = element(),
axis.title.y = element(angle = 90),
axis.ticks.length = unit(0.15, "cm"),
axis.ticks.margin = unit(0.1, "cm"),
legend.background = element(colour = NA),
legend.margin = unit(0.2, "cm"),
legend.key = element(fill = "grey95", colour = "white"),
legend.key.size = unit(1.2, "lines"),
legend.key.height = NULL,
legend.key.width = NULL,
legend.text = element(size = rel(0.8)),
legend.text.align = NULL,
legend.title = element(face = "bold", size = rel(0.8), hjust = 0),
legend.title.align = NULL,
legend.position = "right",
legend.direction = NULL,
legend.justification = "center",
legend.box = NULL,
panel.background = element(fill = "grey90", colour = NA),
panel.border = element(),
panel.grid.major = element(colour = "white"),
panel.grid.minor = element(colour = "grey95", size = 0.25),
panel.margin = unit(0.25, "lines"),
strip.background = element(fill = "grey80", colour = NA),
strip.text.x = element(),
strip.text.y = element(angle = -90),
plot.background = element(colour = "white"),
plot.title = element(size = rel(1.2)),
plot.margin = unit(c(1, 1, 0.5, 0.5), "lines"),
complete = TRUE) 

The print output evaluates to the original object.

> identical(eval(parse(text=as.character(theme_gray()))), theme_gray())
TRUE

@hadley hadley commented on an outdated diff Oct 15, 2012

R/theme-elements.r
@@ -87,6 +83,10 @@ rel <- function(x) {
structure(x, class = "rel")
}
+as.character.rel <- function(x) {
@hadley

hadley Oct 15, 2012

Owner

I think this would be better as a format method. Also you need to export all S3 methods with a roxygen directive like @S3method format rel

@hadley hadley commented on an outdated diff Oct 15, 2012

R/theme-elements.r
@@ -353,3 +353,25 @@ validate_element <- function(el, elname) {
}
invisible()
}
+
+as.character.element <- function(x) {
+ .f <- function(x) {
+ if (class(x) %in% c("rel", "unit")) {
+ as.character(x)
+ } else {
+ deparse(x)
+ }
+ }
+ element_type <- class(x)[2]
+ x <- lapply(Filter(notnull, x), .f)
@hadley

hadley Oct 15, 2012

Owner

I prefer defining compact <- function(x) Filter(Negate(is.null), x)

@hadley hadley commented on an outdated diff Oct 15, 2012

R/theme-elements.r
@@ -353,3 +353,25 @@ validate_element <- function(el, elname) {
}
invisible()
}
+
+as.character.element <- function(x) {
+ .f <- function(x) {
+ if (class(x) %in% c("rel", "unit")) {
@hadley

hadley Oct 15, 2012

Owner

class(x) can be a vector so you need any here

Owner

hadley commented Oct 15, 2012

I made a few smaller comments, but I'm not entirely sure that printing the object should produce something you can easily copy and pasted.

Contributor

jrnold commented Oct 15, 2012

This and #692 are really two solutions to the same problem. My workflow when extend a theme has generally gone as follows. I'd start with theme_gray, and make a few additions. Wonder why the axis ticks were still gray. Repeat those two steps a couple of times. Then give up and just copy the source code of theme_gray() and manually edit it. The later step arose because of the nature of the basic themes (as in #692), but also because the print output of themes makes it hard to get a quick global understanding of what is going on. I found that the source code used to create the theme was more understandable than the theme itself. #692 mitigates some of the need for this. However, this did come out of a real need in my workflow.

I'd generally agree with you on the dangers of copy and pasting. However, in this case, I tend to think of the themes more like data than code. Inheriting from other themes is a quick way to fill in the entries of the data. But once I have the set of elements in a theme, I don't mind if its entries are no longer tied to the themes that it was created from, and in many cases I may not want it to change if the definition of the other theme changes.

I myself do not put p=1 that this is the best way to do it, but I thought that I'd throw the idea out there and see whether it was useful.

jrnold added some commits Oct 15, 2012

incorporated suggestions from hadley
- register all S3 methods
- use format instead of as.character
- use Negate instead of adding a notnull function
- fix bug in checking classes
fix typos in previous commit
- whitespace line in utilities.r
- as.format.rel -> format.rel
Owner

hadley commented Feb 24, 2014

Could you please rebase/merge against master, re-document with the development version of roxygen2 (install_github("klutometis/roxygen) and resubmit?

@hadley hadley closed this Feb 24, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment