diff --git a/NEWS.md b/NEWS.md index 78b068fc..d5be8c5d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,9 @@ where the formatting is also better._ `boxplot(x)`. (#454 @zeileis) - `type_errorbar()` and `type_point_range()` get a `dodge` argument. (#461 @vincentarelbundock) +- The new `tinyplot(..., theme = )` argument enables users to invoke + ephemeral themes as an alternative to the persistent themes that follow + `tinytheme()`. (#483 @grantmcdermott) ### Bug fixes diff --git a/R/tinyplot.R b/R/tinyplot.R index a6fbf35e..c076702c 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -349,6 +349,9 @@ #' `width` (above) apply, e.g. will default to `tpar("file.height")` if not #' specified. #' @param asp the y/xy/x aspect ratio, see `plot.window`. +#' @param theme keyword string (e.g. `"clean"`) or list defining a theme. Passed +#' on to [`tinytheme`], but reset upon exit so that the theme effect is only +#' temporary. Useful for invoking ephemeral themes. #' @param ... other graphical parameters. If `type` is a character specification #' (such as `"hist"`) then any argument names that match those from the corresponding #' `type_*()` function (such as \code{\link{type_hist}}) are passed on to that. @@ -634,6 +637,7 @@ tinyplot.default = function( width = NULL, height = NULL, asp = NA, + theme = NULL, ...) { @@ -674,6 +678,22 @@ tinyplot.default = function( } set_saved_par(when = "before", opar) + # Ephemeral theme + if (!is.null(theme)) { + # browser() + if (is.character(theme) && length(theme) == 1) { + tinytheme(theme) + } else if (is.list(theme)) { + do.call(tinytheme, theme) + } else { + warning('Argument `theme` must be a character of length 1 (e.g. "clean"), or a list. Ignoring.') + } + dtheme = theme_default + otheme = opar[names(dtheme)] + + on.exit(do.call(tinytheme, otheme), add = TRUE) + } + # ## devices and files ----- diff --git a/R/tinytheme.R b/R/tinytheme.R index 9b59ad32..5fc29348 100644 --- a/R/tinytheme.R +++ b/R/tinytheme.R @@ -34,8 +34,11 @@ #' @details #' Sets a list of graphical parameters using `tpar()` #' +#' Note that themes are persistent and will be applied to all subsequent plots. #' To reset the theme to default settings (no customization), call `tinytheme()` -#' without arguments. +#' without arguments. Altenatively, invoke the `tinyplot(..., theme = )` +#' argument for an ephemeral theme that is automatically reset at the end of the +#' plot call. #' #' **Caveat emptor:** Themes are a somewhat experimental feature of `tinyplot`. #' While we feel confident that themes should work as expected for most @@ -65,6 +68,9 @@ #' # Set a theme #' tinytheme("bw") #' p() +#' +#' # A set theme is persistent and will apply to subsequent plots +#' tinyplot(0:10) #' #' # Try a different theme #' tinytheme("dark") @@ -86,6 +92,10 @@ #' tinytheme() #' p() #' +#' # For an ephemeral theme, use `tinyplot(..., theme = )` directly +#' tinyplot(0:10, theme = "clean", main = "This theme is ephemeral") +#' tinyplot(10:0, main = "See, no more theme") +#' #' # Themes showcase #' ## We'll use a slightly more intricate plot (long y-axis labs and facets) #' ## to demonstrate dynamic margin adjustment etc. diff --git a/inst/tinytest/_tinysnapshot/tinytheme_ephemeral.svg b/inst/tinytest/_tinysnapshot/tinytheme_ephemeral.svg new file mode 100644 index 00000000..11d81648 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_ephemeral.svg @@ -0,0 +1,464 @@ + + + + + + + + + + + + + + + + + + + + +Ephemeral theme +Petal.Length +Sepal.Length + + + + + + + + + + +1 +2 +3 +4 +5 +6 +7 + + + + + + + + + +4.5 +5.0 +5.5 +6.0 +6.5 +7.0 +7.5 +8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Revert to old theme +Petal.Length +Sepal.Length + + + + + + + + + + +1 +2 +3 +4 +5 +6 +7 + + + + + + + + + +4.5 +5.0 +5.5 +6.0 +6.5 +7.0 +7.5 +8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-tinytheme.R b/inst/tinytest/test-tinytheme.R index 282f151f..01bb9d0b 100644 --- a/inst/tinytest/test-tinytheme.R +++ b/inst/tinytest/test-tinytheme.R @@ -3,7 +3,6 @@ using("tinysnapshot") tinytheme() - thms = eval(formals(tinytheme)$theme) for (thm in thms) { @@ -152,3 +151,19 @@ expect_snapshot_plot(f, label = "tinytheme_dynamic_clean_spineplot") ## reset tinytheme() + + +# +## ephemeral theme + +f = function() { + opar = par(mfrow = c(1, 2)) + plt(Sepal.Length ~ Petal.Length | Species, data = iris, + main = "Ephemeral theme", theme = "clean", legend = FALSE) + plt_add(type = "lm") + plt(Sepal.Length ~ Petal.Length | Species, data = iris, + main = "Revert to old theme", legend = FALSE) + plt_add(type = "lm") + par(opar) +} +expect_snapshot_plot(f, label = "tinytheme_ephemeral") \ No newline at end of file diff --git a/man/tinyplot.Rd b/man/tinyplot.Rd index 629e4fb8..676ed5e3 100644 --- a/man/tinyplot.Rd +++ b/man/tinyplot.Rd @@ -60,6 +60,7 @@ tinyplot(x, ...) width = NULL, height = NULL, asp = NA, + theme = NULL, ... ) @@ -504,6 +505,10 @@ specified.} \item{asp}{the y/xy/x aspect ratio, see \code{plot.window}.} +\item{theme}{keyword string (e.g. \code{"clean"}) or list defining a theme. Passed +on to \code{\link{tinytheme}}, but reset upon exit so that the theme effect is only +temporary. Useful for invoking ephemeral themes.} + \item{formula}{a \code{\link[stats]{formula}} that optionally includes grouping variable(s) after a vertical bar, e.g. \code{y ~ x | z}. One-sided formulae are also permitted, e.g. \code{~ y | z}. Only a single \code{y} and \code{x} diff --git a/man/tinytheme.Rd b/man/tinytheme.Rd index 3508a1f7..a8906549 100644 --- a/man/tinytheme.Rd +++ b/man/tinytheme.Rd @@ -49,8 +49,11 @@ whitespace. \details{ Sets a list of graphical parameters using \code{tpar()} +Note that themes are persistent and will be applied to all subsequent plots. To reset the theme to default settings (no customization), call \code{tinytheme()} -without arguments. +without arguments. Altenatively, invoke the \verb{tinyplot(..., theme = )} +argument for an ephemeral theme that is automatically reset at the end of the +plot call. \strong{Caveat emptor:} Themes are a somewhat experimental feature of \code{tinyplot}. While we feel confident that themes should work as expected for most @@ -78,6 +81,9 @@ p() tinytheme("bw") p() +# A set theme is persistent and will apply to subsequent plots +tinyplot(0:10) + # Try a different theme tinytheme("dark") p() @@ -98,6 +104,10 @@ tinyplot(I(cut(lat, 10)) ~ depth, data = quakes, type = "ridge") tinytheme() p() +# For an ephemeral theme, use `tinyplot(..., theme = )` directly +tinyplot(0:10, theme = "clean", main = "This theme is ephemeral") +tinyplot(10:0, main = "See, no more theme") + # Themes showcase ## We'll use a slightly more intricate plot (long y-axis labs and facets) ## to demonstrate dynamic margin adjustment etc. diff --git a/vignettes/themes.qmd b/vignettes/themes.qmd index 0abe7bfa..3285c4f6 100644 --- a/vignettes/themes.qmd +++ b/vignettes/themes.qmd @@ -83,6 +83,20 @@ tinytheme() tinyplot(mpg ~ hp, data = mtcars, main = "Fuel efficiency vs. horsepower") ``` +### Aside: ephemeral themes + +We expect that users will generally want to set a persistent theme for all +(most) of their plots. But it is also possible to set a ephemeral theme for a +particular plot by calling the `tinyplot(..., theme = )` argument +directly. + +```{r} +#| layout-ncol: 2 +tinyplot(mpg ~ hp, data = mtcars, main = "Ephemeral theme", theme = "clean") +tinyplot(mpg ~ hp, data = mtcars, main = "Back to the default") +``` + + ### Gallery