15
$\begingroup$

There is a way to specify custom themes for plots (Is it possible to define a new PlotTheme?), but themes do not apply to Graph objects, not even to GraphPlot. Graph options are set by the GraphStyle option with which one can select from a few built-in styles:

GraphElementData["GraphStyle"]
{Automatic, "BackgroundBlack", "BackgroundBlue", ..., "VintageDiagram"}

However, GraphElementData definitions cannot be accessed (the function itself is only tangentially touched in the documentation), so I have no idea how to come up with a theme that works as a built-in one. The obvious solution is to set up default options that are passed on to Graph directly or via SetOptions, but I'd like to have a more integrated method where I can call my graph like this:

Graph[{1 -> 2, 2 -> 3, 3 -> 1}, GraphStyle -> "MyGraphTheme"]

and have a result like this:

Graph[{1 -> 2, 2 -> 3, 3 -> 1}, Background -> [email protected],
 EdgeStyle -> Directive[Green, Thick], VertexLabels -> "Name", 
 VertexLabelStyle -> Directive[Orange, 16], ImagePadding -> 20, ImageSize -> 200]

Mathematica graphics

$\endgroup$
2
  • $\begingroup$ Ultimately, I'd like to use the same themes for graphs that are available for plots. $\endgroup$ Dec 9, 2015 at 7:36
  • $\begingroup$ One possible solution (to the problem described in the bounty offer) is to have a function which takes graph options, combines them with a "theme", then returns new graph options. This is probably the safest way. The function has to handle per-vertex and per-edge options, as well as global options. A nicer way would be to hook into GraphStyle, but that feels risky. $\endgroup$
    – Szabolcs
    Apr 13, 2018 at 20:45

3 Answers 3

8
+300
$\begingroup$

If you are willing to mess around with the internals of Mathematica, I think the necessary graph style code is at top level. So:

Options[addGraphStyle] = {
    BaseStyle->Automatic,
    DirectedEdges->Automatic,
    EdgeLabels->None,
    EdgeLabelStyle->Automatic,
    EdgeShapeFunction->Automatic,
    EdgeStyle->Automatic,
    GraphHighlightStyle->Automatic,
    Options->{},
    VertexLabels->None,
    VertexLabelStyle->Automatic,
    VertexShape->Automatic,
    VertexShapeFunction->Automatic,
    VertexSize->Automatic,
    VertexStyle->Automatic
};

addGraphStyle[style_String, opts:OptionsPattern[]] := Internal`WithLocalSettings[
    Unprotect[GraphComputation`SetGraphStyle],

    Replace[
        OptionValue[EdgeLabels], 
        v_ :> Set[GraphComputation`SetGraphStyle[style, EdgeLabels], v]
    ];
    Replace[
        OptionValue[VertexLabels], 
        v_ :> Set[GraphComputation`SetGraphStyle[style, VertexLabels], v]
    ];
    Replace[
        OptionValue[Options],
        v_ :> Set[GraphComputation`SetGraphStyle[style, Options], v]
    ];
    Map[
        Replace[
            OptionValue[#],
            {
            Automatic :> Quiet @ Unset[GraphComputation`SetGraphStyle[style, #]],
            v_ :> Set[GraphComputation`SetGraphStyle[style, #], v]
            }
        ]&,
        {
        BaseStyle, DirectedEdges, EdgeLabelStyle, EdgeShapeFunction, EdgeStyle,
        GraphHighlightStyle, VertexLabelStyle, VertexShape, VertexShapeFunction, VertexSize, VertexStyle
        }
    ];
    GraphComputation`SetGraphStyle[style<>"3D", rest__] := GraphComputation`SetGraphStyle[style, rest],

    Protect[GraphComputation`SetGraphStyle]
]

Example:

addGraphStyle[
    "Example",
    {
    VertexLabels->"Name",
    EdgeStyle->Directive[Green, Dashed]
    }
]

Visualization:

Graph[{1->2, 2->2}, GraphStyle->"Example"]
Graph[{1->2, 2->2}, GraphStyle->"Example", EdgeStyle->Thickness[.02]]

enter image description here

For the OP example, we have:

addGraphStyle[
    "Istvan",
    Options->{Background->[email protected], ImagePadding->20, ImageSize->200},
    EdgeStyle->Directive[Green, Thick],
    VertexLabels->"Name",
    VertexLabelStyle->Directive[Orange, 16]
]

Graph[{1->2, 2->3, 3->1}, GraphStyle->"Istvan"]

enter image description here

$\endgroup$
5
  • $\begingroup$ It looks like exactly what I wanted. I'll play with it tomorrow (it's late now), and will award the bounty if it works. (Will also release a set of nice styles using this functionality.) $\endgroup$
    – Szabolcs
    Apr 13, 2018 at 21:06
  • $\begingroup$ @Szabolcs There is an issue with resetting styles back to their defaults, which could be fixed by adding a check for the old values, or by adding a removeGraphicStyle function. $\endgroup$
    – Carl Woll
    Apr 13, 2018 at 21:20
  • $\begingroup$ Doesn't seem to work for Graph3D. I mean that when the corresponding graph options would affect Graph3D, the graph style that was created with this function won't have an effect on it. $\endgroup$
    – Szabolcs
    Apr 13, 2018 at 22:43
  • $\begingroup$ @Szabolcs I added Graph3D support. $\endgroup$
    – Carl Woll
    Apr 13, 2018 at 23:27
  • $\begingroup$ If you've seen my answer then you know that I am ready to mess around with the internals of Mathematica, even modify built-ins : ) Nice solution, earned the accept! On the other hand, I'm still awaiting public and polished theme-setting functionality in general. $\endgroup$ Apr 14, 2018 at 12:25
7
$\begingroup$

Update 2016.8.13.

In Mathematica v11.0, Graph were modified to be able to use directly the PlotTheme option. While there are reliable ways to construct new themes (see here and here), unfortunately the theme system is still not fully compatible with graphs. At the moment, you cannot add Graph-specific rules like Themes`AddThemeRules["myGraph", Graph, VertexLabelStyle -> Green] to have any effect when called e.g. Graph[{1->2, 2->3, 3->1}, PlotTheme -> "myGraph"].


I've constructed a more-or-less safe way to do this. I aimed for the more general case: to directly use available plotting themes with graphs, at least the relevant options. I even started to wonder why PlotTheme was not designed to cooperate with all graphics object (like Graph, Graphics, GeoGraphics, etc.).

(* Define a new theme *)
Themes`AddThemeRules[
  "Dark", {Background -> [email protected], 
   EdgeStyle -> Directive[Green, Thick], VertexLabels -> "Name", 
   VertexLabelStyle -> Directive[Orange, 16], ImagePadding -> 20, 
   ImageSize -> 200, PlotRange -> All, 
   DefaultPlotStyle -> Directive[Green, Thick], 
   LabelStyle -> Directive[Orange, 12]}];

Unprotect@Graph;
Graph[arg__, pre : OptionsPattern[], PlotTheme -> t_, post___] := 
  If[MemberQ[GraphElementData@"GraphStyle", t],
   Graph[arg, pre, GraphStyle -> t, post],
   Module[{th = Charting`ResolvePlotTheme[t, Graph]},
    (* Exclude functional ticks and method as Graph can't handle these *)
    th = DeleteCases[th, _[FrameTicks | Ticks, _?(MemberQ[Flatten@{#}, _Function] &)] |
    _[Method, _]];
    Graph[arg, FilterRules[
      DeleteDuplicatesBy[Flatten@{pre, theme, post}, First], 
      Options@Graph]]
    ]];

{Plot[Sin[x], {x, 0, 10}, PlotTheme -> "Dark"],
 Graph[{1 <-> 2, 2 <-> 3, 3 <-> 1}, PlotTheme -> "Dark"],
 Graph[{1 <-> 2, 2 <-> 3, 3 <-> 1}, PlotTheme -> "Scientific", 
  ImageSize -> 200],
 Graph[{1 <-> 2, 2 <-> 3, 3 <-> 1}, 
  EdgeStyle -> Directive[Thick, Red], PlotTheme -> "Dark"],
 Graph[{1 <-> 2, 2 <-> 3, 3 <-> 1}, PlotTheme -> "DiagramGold"]}

Mathematica graphics

As you can see, it recognizes built-in GraphStyle-s. User-options take precedence over themes, if supplied before PlotTheme. Trying some of the built-in plot themes:

Multicolumn[
 Graph[{1 <-> 2, 2 <-> 3, 3 <-> 1}, PlotTheme -> #, PlotLabel -> #, 
    ImageSize -> 150] & /@ {"Business", "Detailed", "Marketing", 
   "Minimal", "Monochrome", "Scientific", "Web", "Classic"}, 4, 
 Appearance -> "Horizontal"]

Mathematica graphics

$\endgroup$
1
$\begingroup$

On the first try, I can't find a way to make something like " GraphTheme " work. Perhaps there is a way to do it, but I don't see it.

A quick hack to make this work without using themes is something like:

myGraphOptions = {Background -> [email protected], 
 EdgeStyle -> Directive[Green, Thick], VertexLabels -> "Name", 
 VertexLabelStyle -> Directive[Orange, 16]}
Graph[{1 -> 2, 2 -> 3, 3 -> 1}, myGraphOptions]

This appears to have the desired effect, more or less. (Not sure if you wanted the ImageSize or ImagePadding in the "theme" -- I didn't include it.)

One thing to keep in mind though is if you want to override some of these, you'll want to put those options before the myGraphOptions.

Graph[{1 -> 2, 2 -> 3, 3 -> 1}, EdgeStyle -> Red, myGraphOptions]

Note that in this case, the edges are red, but also not thick.

But I agree a "theme" type of construction for Graph would be better.

$\endgroup$
2
  • $\begingroup$ Thanks Kellen for the effort, but this is exactly the referred "obvious solution" I want to avoid. Sorry, no cigar for this :) $\endgroup$ Dec 9, 2015 at 7:34
  • $\begingroup$ Of course, I do understand. I thought this would be a good answer in the case nothing more useful comes up, especially if another reader comes to this question hoping for an answer but without the same foreknowledge as you and I. This might be useful to such a person. $\endgroup$ Dec 11, 2015 at 18:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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