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 -> GrayLevel@.15,
 EdgeStyle -> Directive[Green, Thick], VertexLabels -> "Name", 
 VertexLabelStyle -> Directive[Orange, 16], ImagePadding -> 20, ImageSize -> 200]

Mathematica graphics

$\endgroup$
  • $\begingroup$ Ultimately, I'd like to use the same themes for graphs that are available for plots. $\endgroup$ – István Zachar Dec 9 '15 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 '18 at 20:45
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->GrayLevel@.15, 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$
  • $\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 '18 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 '18 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 '18 at 22:43
  • $\begingroup$ @Szabolcs I added Graph3D support. $\endgroup$ – Carl Woll Apr 13 '18 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$ – István Zachar Apr 14 '18 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 -> GrayLevel@.15, 
   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 -> GrayLevel@.15, 
 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$
  • $\begingroup$ Thanks Kellen for the effort, but this is exactly the referred "obvious solution" I want to avoid. Sorry, no cigar for this :) $\endgroup$ – István Zachar Dec 9 '15 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$ – Kellen Myers Dec 11 '15 at 18:04

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.