92
$\begingroup$

In version 10 one can define, say,

$PlotTheme = "Scientific"

Which changes the appearance of all plots. This is fantastic because on the one hand I can have nice plots very easily and globally (i.e. No need to tune every option in Plot[...]), and on the other hand when I exchange code with my collaborators they may not have Mathematica 10 and defining $PlotTheme is harmless for previous versions.

However, none of the provided plot themes fit my needs and I would need to combine some options (for example, the lines should on the one hand have colors, and on the other hand, has solid/dashed/dotted styles). Thus I wonder if it is possible to define a plot theme myself, combining a few built-in themes, and specify it globally using

$PlotTheme = "myStyle"

Note: This is related to Specifying non-conflicting PlotTheme options (which focuses on changing and tuning plot theme as PlotStyle, etc.), and @kguler has already provided a great answer.

$\endgroup$
84
$\begingroup$

Basic method

There appears to be a mechanism for doing just that, though I have yet to map its capabilities.

As a basic example for the time being:

Themes`AddThemeRules["wizard",
  DefaultPlotStyle -> Thread@Directive[{Purple, Orange, Hue[0.6]}, Thick],
  LabelStyle -> 18,
  AxesStyle -> White,
  TicksStyle -> LightGray,
  Background -> Gray
]

Now:

Plot[{Sinc[x], Sinc[2 x], Sinc[3 x]}, {x, 0, 10}, PlotTheme -> "wizard"]

enter image description here

Hideous, I know. :o)

You can attach rules to specific plot functions using the second parameter, e.g. BarChart:

Themes`AddThemeRules["wizard", BarChart,
  ChartStyle -> {Pink, Gray, Brown}
];

Now:

BarChart[{{1, 2, 3}, {1, 3, 2}}, PlotTheme -> "wizard"]

enter image description here

These themes do not persist across a kernel restart so you can experiment freely I believe.
If you wish to make any changes persist you could use kernel/init.m.

Advanced method

I could not find a way to use AddThemeRules to make Themes that would combine with others in the way that the default ones will. I found that I needed to take things to a lower level and make assignments to this System function:

System`PlotThemeDump`resolvePlotTheme

This appears to be the true home of PlotThemes and one can look at its Definition to see everything, once it has been preloaded by Plot or some other means. To read a specific definition I (once again) recommend my step function. Let's check the definition of "ThickLines" for "Plot" (note that plot function names must be given as strings):

Themes`ThemeRules;  (* preload PlotThemes subsystem *)

Defer @@ step @ System`PlotThemeDump`resolvePlotTheme["ThickLines", "Plot"]
Themes`SetWeight[{"DefaultThickness" ->
 {AbsoluteThickness[3]}}, System`PlotThemeDump`$ComponentWeight]

(Defer replaces HoldForm to allow proper copy&paste.)

We can use this knowledge to create a new thickness Theme for Plot:

System`PlotThemeDump`resolvePlotTheme["Thick5", "Plot"] := 
  Themes`SetWeight[{"DefaultThickness" -> {AbsoluteThickness[5]}}, 
    System`PlotThemeDump`$ComponentWeight]

Now we can combine this with existing Themes just as we can the defaults:

Plot[{x^2 + x, x^2}, {x, -1, 1}, PlotTheme -> {"Detailed", "Thick5"}]

enter image description here

For more on the role of SetWeight please see:

$\endgroup$
  • 4
    $\begingroup$ I was wondering how long it would take someone to find that. The Themes` context is not fully fleshed out, hence it not being documented, but there are mechanisms in there for saving and retrieving themes. $\endgroup$ – rcollyer Jul 12 '14 at 22:02
  • $\begingroup$ @rcollyer Is this something that will become "fleshed out" in v11 or sooner/later? $\endgroup$ – Will Vousden Jul 16 '14 at 14:12
  • $\begingroup$ @WillVousden I don't know what the plans are. I do know there are a few issues to be resolved, first, but I have no insight into when that will be. $\endgroup$ – rcollyer Jul 16 '14 at 14:30
  • 1
    $\begingroup$ Congrats on the 100k! (Happy to be the one to tip you over :D) $\endgroup$ – rm -rf Jul 17 '14 at 0:25
  • $\begingroup$ @rm-rf Thank you! :D $\endgroup$ – Mr.Wizard Jul 17 '14 at 0:26
44
$\begingroup$

Illuminated by @Mr.Wizard's answer, here I provide a complete example of a self-made plot theme, called "Academic". It can be used as a base theme.

  • The axes feature theme is based on the theme "AxesFrame" of "Scientific" with {Black, AbsoluteThickness[1], FontSize -> 12} axes/frames.
  • The color feature theme is based on "VibrantColor" with modifications of density colors (use "ThermometerColors") and MatrixPlot colors.
  • The size feature theme is adjusted to be compatible with the label font size.
  • The point marker feature theme is based on "OpenMarkers" with a smaller size.

Examples with PlotTheme->"Academic": Academic Plot Theme Gallery

The following code defines the "Academic" PlotTheme, which provides a framework for defining plot themes, and can be further modified based on the user's taste. The idea is to break down the base theme to specific feature themes and define each feature (or subfeature) separately for different groups of visualization functions. By executing the following code, "Academic" will be set as the default plot theme.

(* = Academic Plot Theme = *)
Begin["System`PlotThemeDump`"];
Themes`ThemeRules; (* preload Theme system *)

(* Define the base theme *)
resolvePlotTheme["Academic",def:_String]:=
  Themes`SetWeight[Join[
    resolvePlotTheme["AcademicFrame",def],     (* Axes feature *)
    resolvePlotTheme["Figure",def],            (* Size feature *)
    resolvePlotTheme["HeavyLines",def],        (* Curve thickness feature *)
    resolvePlotTheme["VividColor",def],        (* Color feature *)
    resolvePlotTheme["SmallOpenMarkers",def]], (* Point marker feature *)
   Themes`$DesignWeight];

(* == Axes feature == *)
(* === 2D Plots === *)
resolvePlotTheme["AcademicFrame",def:_String]:=
  Themes`SetWeight[Join[
    {Axes->False,Frame->True}, (* Academic figures are framed by default *)
    resolvePlotTheme["AcademicFrame2D",def]],
   $ComponentWeight];

resolvePlotTheme["AcademicFrame",def:"PairedBarChart"|"PairedHistogram"]:=
  Themes`SetWeight[Join[
    {Axes->True,Frame->True}, (* Cases with axes also *)
    resolvePlotTheme["AcademicFrame2D",def]],
   $ComponentWeight];

resolvePlotTheme["AcademicFrame",def:"ArrayPlot"|"MatrixPlot"]:=
  Themes`SetWeight[Join[
    (* Frame not specified but MeshStyle specified to be thin and light *)
    {MeshStyle->Directive[AbsoluteThickness[0.5],Opacity[0.25]]},
    resolvePlotTheme["AcademicFrame2D",def]],
   $ComponentWeight];

resolvePlotTheme["AcademicFrame",
   def:"BarChart"|"PieChart"|"RectangleChart"|"SectorChart"|
     "CandlestickChart"|"KagiChart"|"LineBreakChart"|
     "PointFigureChart"|"RenkoChart"|"InteractiveTradingChart"|
     "TradingChart"|"NumberLinePlot"|"TimelinePlot"]:=
  resolvePlotTheme["AcademicFrame2D",def]; (* Charts not framed *)

(* === 3D Plots === *)
resolvePlotTheme["AcademicFrame",
   def:_String/;StringMatchQ[def,___~~"3D"]]:=
  Themes`SetWeight[Join[
    {Axes->True,AxesEdge->{{-1,-1},{1,-1},{-1,-1}},
     Boxed->{Left,Bottom,Back}}, (* Front axes back box *)
    resolvePlotTheme["AcademicFrame3D",def]],
   $ComponentWeight];

resolvePlotTheme["AcademicFrame","ChromaticityPlot3D"]:=
  Themes`SetWeight[Join[
    {Axes->True,AxesEdge->{{-1,-1},{-1,-1},{-1,1}},
     Boxed->{Left,Top,Front}}, (* Front box back axes *)
    resolvePlotTheme["AcademicFrame3D","ChromaticityPlot3D"]],
   $ComponentWeight];

resolvePlotTheme["AcademicFrame",
   def:"BarChart3D"|"PieChart3D"|"RectangleChart3D"|"SectorChart3D"]:=
  resolvePlotTheme["AcademicFrame3D",def]; (* Chart3Ds not boxed *)

(* === Common features of axes/frame === *)
(* Mathematica's working theme: axes and frames too thin, terribly grayish, 
   and tick/label font too small, but grids too thick. The new theme will fix these. *)
resolvePlotTheme["AcademicFrame2D",_]:=
  Themes`SetWeight[
   {AxesStyle->Directive[AbsoluteThickness[1],monoColor,FontSize->14],
    FrameStyle->Directive[AbsoluteThickness[1],monoColor,FontSize->14],
    TicksStyle->Directive[monoColor,FontSize->12],
    FrameTicksStyle->Directive[monoColor,FontSize->12],
    GridLinesStyle->Directive[AbsoluteThickness[0.5],Opacity[0.5]]},
   $ComponentWeight];

resolvePlotTheme["AcademicFrame3D",_]:=
  Themes`SetWeight[
   {AxesStyle->Directive[AbsoluteThickness[1],monoColor,FontSize->14],
    TicksStyle->Directive[monoColor,FontSize->12],
    BoxStyle->monoColor},
   $ComponentWeight];

(* == Size feature == *)
resolvePlotTheme["Figure",def:_String]:=
  Themes`SetWeight[
   {ImageSizeRaw->{{180},{180}}, (* 2D plots 180 pts *)
    LabelStyle->Directive[monoColor,FontSize->12]},
   Themes`$SizeWeight];

resolvePlotTheme["Figure","ArrayPlot"|"MatrixPlot"]:=
  Themes`SetWeight[
   {ImageSizeRaw->{{140},{140}}, (* Array/matrix 140 pts *)
    LabelStyle->Directive[monoColor,FontSize->12]},
   Themes`$SizeWeight];

resolvePlotTheme["Figure",def:_String/;StringMatchQ[def,___~~"3D"]]:=
  Themes`SetWeight[
   {ImageSizeRaw->{{200},{200}}, (*3D plots 200 pts*)
    LabelStyle->Directive[monoColor,FontSize->12]},
   Themes`$SizeWeight];

(* Size not specified for LinePlots (because they are wide) *)
resolvePlotTheme["Figure","NumberLinePlot"|"TimelinePlot"]:={};

(* == Color feature == *)
(* Color scheme based on VibrantColor, which is bright and vivid *)
resolvePlotTheme["VividColor",def:_String]:=
  Module[{},
   $ThemeColorIndexed=112; (* ColorData[112] = Vibrant *)
   $ThemeColorDensity="ThermometerColors"; (* Thermometer for density *)
   $ThemeColorArrayPlot={GrayLevel[0],GrayLevel[1]}; (* Grayscale for Array *)
   $ThemeColorDensity3D="ThermometerColors"; (* Thermometer for density *)
   $ThemeColorVectorDensity="VibrantColorVectorDensityGradient";
   $ThemeColorFinancial={RGBColor[0., 0.596078, 0.109804],
     RGBColor[0.790588, 0.201176, 0.]}; (* Green and red *)
   $ThemeColorGradient={RGBColor[0.790588, 0.201176, 0.],
 RGBColor[0.567426, 0.32317, 0.729831],
 RGBColor[0.192157, 0.388235, 0.807843],
 RGBColor[0., 0.596078, 0.109804],RGBColor[1., 0.607843, 0.]};
   $ThemeColorMatrix= (* Red-blue split *)
    {{0,RGBColor[0.128105, 0.25882, 0.538562]},
     {0.1,RGBColor[0.192157, 0.388235, 0.807843]},
     {0.499999,RGBColor[0.8384314, 0.877647, 0.9615686]},
     {0.5,RGBColor[{1, 1, 1}]},
     {0.500001,RGBColor[0.9581176, 0.8402352, 0.8]},
     {0.9,RGBColor[0.790588, 0.201176, 0.]},
     {1,RGBColor[0.527059, 0.134117, 0.]}};
   $ThemeColorFractal="VibrantColorFractalGradient";
   $ThemeColorWavelet={RGBColor[0.0621178, 0.273882, 0.727059],
     RGBColor[0.790588, 0.201176, 0.],RGBColor[1., 0.607843, 0.],
     RGBColor[1., 1., 1.]};
   resolvePlotTheme["ColorStyle",def]];

(* == Point marker feature ==*)
(* No markers by default *)
resolvePlotTheme["SmallOpenMarkers",def:_String]:={};

(* Set markers for ListPlots *)
resolvePlotTheme["SmallOpenMarkers",
    "DateListLogPlot"|"DateListPlot"|"DiscretePlot"|"ListCurvePathPlot"|
    "ListLinePlot"|"ListLogLinearPlot"|"ListLogLogPlot"|
    "ListLogPlot"|"ListPlot"]:=
  Themes`SetWeight[
   {PlotMarkers->Module[{s1 = 2., s2 = 1.8, s3 = 2.5, s4 = 1.3, thickness = 1.5},
      {Graphics[{{White, Disk[{0, 0}, Offset[{s1, s1}]]}, {AbsoluteThickness[thickness], Dashing[{}], Circle[{0, 0}, Offset[{s1, s1}]]}}], 
       Graphics[{{White, Polygon[{Offset[{0, 2*s4}], Offset[s4*{-Sqrt[3], -1}], Offset[s4*{Sqrt[3], -1}]}]}, {AbsoluteThickness[thickness], Dashing[{}], JoinedCurve[Line[{Offset[{0, 2*s4}], Offset[s4*{-Sqrt[3], -1}], Offset[s4*{Sqrt[3], -1}], Offset[{0, 2*s4}]}], CurveClosed -> True]}}], 
       Graphics[{{White, Polygon[{Offset[{0, s3}], Offset[{s3, 0}], Offset[{0, -s3}], Offset[{-s3, 0}]}]}, {AbsoluteThickness[thickness], Dashing[{}], Line[{Offset[{0, s3}], Offset[{s3, 0}], Offset[{0, -s3}], Offset[{-s3, 0}], Offset[{0, s3}]}]}}], 
       Graphics[{{White, Polygon[{Offset[{-s2, -s2}], Offset[{s2, -s2}], Offset[{s2, s2}], Offset[{-s2, s2}], Offset[{-s2, -s2}]}]}, {AbsoluteThickness[thickness], Dashing[{}], Line[{Offset[{-s2, -s2}], Offset[{s2, -s2}], Offset[{s2, s2}], Offset[{-s2, s2}], Offset[{-s2, -s2}]}]}}], 
       Graphics[{{White, Polygon[{Offset[{0, -2*s4}], Offset[s4*{-Sqrt[3], 1}], Offset[s4*{Sqrt[3], 1}]}]}, {AbsoluteThickness[thickness], Dashing[{}], JoinedCurve[Line[{Offset[{0, -2*s4}], Offset[s4*{-Sqrt[3], 1}], Offset[s4*{Sqrt[3], 1}], Offset[{0, -2*s4}]}], CurveClosed -> True]}}]}]},
   $ComponentWeight];
End[];
$PlotTheme="Academic"; (* Set to default plot theme *)

The plot theme definition does not persist across a kernel restart. If one wish to make persistent change, one could consider adding the above code to .../Kernel/init.m, located at FileNameJoin[{$UserBaseDirectory, "Kernel", "init.m"}].

$\endgroup$
  • $\begingroup$ Let us continue this discussion in chat. $\endgroup$ – Everett You Apr 1 '16 at 3:49
  • $\begingroup$ This is a great work. Would it be possible to provide the code of the examples as well ? $\endgroup$ – faysou Apr 7 '16 at 7:41
  • 1
    $\begingroup$ @faysou To see the effect of the plot theme, you can first run the above code to define the new theme. Then open Wolfram Documentation under the Help menu, and go through the visualization functions (Plot, ListPlot, ...) as you want, and run the examples therein, you can see how changes happen in front of your eyes! $\endgroup$ – Everett You Apr 8 '16 at 4:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.