23

Is there a way to define functions in a stylesheet? So far I have managed to write my stylesheets without the need to use my own custom functions or definitions. For instance, maybe I have some color that I wish to use throughout my stylesheet.

myColor = RGBColor[.5,.5,.5];

The reason I would like to do this is that later on, if I change the color, every style that depends myColor will automatically change. This can apply to other functions that you might create. I know that I can use any built-in function I want in the stylesheets. Is there a way to define custom functions to use in a stylesheet without making a package?

If making a package is the only way, how can we make sure the stylesheets that use such package load the package first so that the styles are properly defined?

jmlopez
  • 6,470
  • 4
  • 28
  • 48
  • No, I don't think you can do that. You can't even use variables (e.g. named colours such as Blue) in stylesheets – rm -rf Jul 01 '12 at 02:10
  • 3
    Darn, I like the fact that we can use Red and other defined colors or functions in the stylesheets. Too bad we can't define our own. Or can ... ALL GLORY TO THE HYPNOTOAD!!! – jmlopez Jul 01 '12 at 02:13
  • 1
    I don't think you can use Red or any other named colour (AFAIK). See the difference by adding Cell[StyleData["Input"], Background -> RGBColor[1,0,0]] and Cell[StyleData["Input"], Background -> Red] (one at a time) to the stylesheet – rm -rf Jul 01 '12 at 02:17
  • @RM, yeah, just did, I better go eat something. So how come we can use RGBColor[1,0,0] but not Red. What are the limits of built-in expressions we can use in the making of stylesheets? – jmlopez Jul 01 '12 at 02:23
  • 3
    That's a good question and I don't really know the answer... The way I interpret it (possibly incorrect) is that the stylesheet can understand any command/directive that doesn't require the use of the kernel to interpret it. In this framework, RGBColor[r,g,b] can be interpreted simply by parsing the expression, whereas Blue requires it to be evaluated or its OwnValues read to get the rgb values. – rm -rf Jul 01 '12 at 02:55

4 Answers4

19

Functions can be used in stylesheets but not defined as per:

myColor = RGBColor[.5,.5,.5];

The reason you cannot, for example, enter Red in a stylesheet is because it is typically written in the underlying box code. By analogy you cannot choose show expression and replace RGBColor[1.,0.,0.] in the underlying expression with Red.

But as @Mr.Wizard said this can be done programatically. For example you can manipulate your current notebooks stylesheet with this sort of code:

SetOptions[EvaluationNotebook[],
 StyleDefinitions -> Notebook[
   {Cell[StyleData[StyleDefinitions -> "Default.nb"]], 
    Cell[StyleData["Input"], FontColor -> myColor]}, 
StyleDefinitions -> "PrivateStylesheetFormatting.nb"
   ]
 ]

...in which you can enter a named color. You can change the stylesheet dynamically:

Manipulate[
 (SetOptions[EvaluationNotebook[],
   StyleDefinitions -> Notebook[
     {Cell[StyleData[StyleDefinitions -> "Default.nb"]], 
      Cell[StyleData["Input"], FontColor -> myColor]},
StyleDefinitions -> "PrivateStylesheetFormatting.nb"
     ]
   ];
  Style["Input Colour", 18, myColor]),

 {myColor, Red}
 ]

By combining code for "Input", comments, strings and so on you can dynamically compare combinations of font and background colours to create a stylesheet that you personally find aesthetically pleasing.

(* a comment *)
Manipulate[
 (SetOptions[EvaluationNotebook[],
   StyleDefinitions -> Notebook[
     {Cell[StyleData[StyleDefinitions -> "Default.nb"]],
      Cell[StyleData["Input"],
       Background -> background,
       FontSize -> size,
       FontColor -> myColor,
       AutoStyleOptions -> {
         "CommentStyle" -> {FontColor -> commentcolor, FontSize -> 11,
            FontFamily -> "Comic Sans MS", ShowAutoStyles -> False, 
           ShowSyntaxStyles -> False, AutoNumberFormatting -> False},
         "StringStyle" -> {FontColor -> stringcolor, FontSize -> 11, 
           FontFamily -> "Century Gothic", ShowAutoStyles -> False, 
           ShowSyntaxStyles -> False, AutoNumberFormatting -> False}}]},
StyleDefinitions -> "PrivateStylesheetFormatting.nb"
     ]
   ];
  Style["Input Colour", 18, myColor]),

 {myColor, Black},
 {commentcolor, Purple},
 {stringcolor, Brown},
 {background, White},
 {{size, 12, "size"}, 8, 20, 1}
 ]

Edit

re: questions from @R.M and @jmlopez

"Do you also know if it is possible to copy all the definitions from a stylesheet to the private stylesheet?"

I'm not actually sure what this question means. Stylesheet settings are inherited. The first cell in a private stylesheet

 Cell[StyleData[StyleDefinitions -> "Default.nb"] 

means the stylsheet is taking its styles from the default stylesheet. So you don't have to copy over any styles. If your private stylesheet is based on one of the nested stylesheets you would insert e.g.

StyleData[
 StyleDefinitions -> 
  FrontEnd`FileName[{"Creative"}, "NaturalColor.nb"]]

...and so on. If you want to define multiple styles then

SetOptions[EvaluationNotebook[],
   StyleDefinitions -> Notebook[
     {Cell[StyleData[StyleDefinitions -> "Default.nb"]],
Cell[StyleData["Input"],
...

]
Cell[StyleData["Output"],
...

]

Cell[StyleData["Text"],

...
] etc.

If you want to create a stand alone stylesheet programmatically then

nb = CreateDocument[
   Notebook[{Cell[StyleData[StyleDefinitions -> "Default.nb"]], 
     Cell[StyleData["Input"], FontColor -> myColor]}, 
    StyleDefinitions -> "PrivateStylesheetFormatting.nb"],
   WindowSize -> All];
NotebookSave[nb, "mystylesheet.nb"]

You can now distribute "mystylesheet.nb"

Mike Honeychurch
  • 37,541
  • 3
  • 85
  • 158
  • Really nice answer! Live updating of the style sheet doesn't quite work on my system; I see something like this as I drag the size slider, but it refreshes as soon as a start to scroll the window for example. – Mr.Wizard Jul 01 '12 at 13:28
  • 1
    On mine (OS X 10.6.8 and 8.0.4) updating is instantaneous. When are you going to get V8??!! (...although maybe it is your computer?) – Mike Honeychurch Jul 01 '12 at 13:33
  • Impressive answer! – Silvia Jul 01 '12 at 13:41
  • Very impressive! Do you also know if it is possible to copy all the definitions from a stylesheet to the private stylesheet? The reason I ask is that if you share your notebook with someone, it gives a file not found error for them because they don't have your stylesheet. If I can, with the click of a button, copy the contents over to the private stylesheet, then I can easily share – rm -rf Jul 01 '12 at 14:44
  • As @R.M mentioned, we do need an export functionality so that once we have decided on the style we can simply get the stylesheet we want to give out to other people. I like your answer by the way Mike. – jmlopez Jul 01 '12 at 14:47
  • @R.M wasn't entirely sure what you were asking but see edit – Mike Honeychurch Jul 01 '12 at 22:24
  • @jmlopez see edit showing how to save the stylesheet. – Mike Honeychurch Jul 01 '12 at 22:24
  • I know stylesheets are inherited, and that wasn't the question. Simply put it's this: User A has a notebook that uses custom definitions from styles.nb. Now A passes the notebook to B. The styles do not show up for B because B does not have styles.nb. This is because the private stylesheet for the notebook inherits from styles.nb (which inherits from Default.nb). My question was to be able to easily copy the definitions from style.nb into the private styles for the notebook i.e. as if style.nb didn't exist and all custom definitions were in the private styles and base inherited from Default.nb – rm -rf Jul 01 '12 at 22:32
  • @R.M ok now I understand. I will edit and post some code for this later this morning. – Mike Honeychurch Jul 01 '12 at 22:52
  • @R.M thinking about this, what you are asking is unrelated to the original question about using functions and variables. Programmatically modifying stylesheets in the way you describe so as to eliminate a dependency on a non-existant parent should be a separate searchable question. I'm going out for a couple of hours. If you post the question someone may have already answered by the time I get back otherwise I'll post a solution. – Mike Honeychurch Jul 01 '12 at 23:10
  • @R.M see answer here: http://mathematica.stackexchange.com/questions/7779/how-can-i-easily-eliminate-the-dependency-of-a-stylesheet-on-a-non-built-in-styl – Mike Honeychurch Jul 02 '12 at 04:01
11

This is my suggestion: use CurrentValue and TaggingRules in this way:

1) Create a notebook in the stylesheet lookup path, say "myStyleParameters.nb", where you'll store your parameters. Add a cell such as

Cell[StyleData["Notebook"], TaggingRules->{"color"->GrayLevel[0.5], "height"->222}];

2) Open your custom stylesheet, say "myStyle.nb", then go to Format->Edit stylesheet to edit the stylesheet's style, and prepend a cell similar to the only one you have there, but inheriting from "myStyleParameters.nb"

Cell[StyleData[StyleDefinitions->"myStyleParameters.nb"]

3) Go to your stylesheet "myStyle.nb" and prepend that same cell

4) Use, wherever you like that color, CurrentValue[{TaggingRules, "color"}]. E.g

Cell[StyleData["testStyle"], Background->CurrentValue[{TaggingRules, "color"}]
Rojo
  • 42,601
  • 7
  • 96
  • 188
  • Could you point out how to attach the TaggingRules to the custom stylesheet? All I want is to create a stylesheet so that when a user installs it everything works the way it should. – jmlopez Jul 01 '12 at 15:54
  • I just tried it and it seems to work. The only concern I have now is that I get: The specified setting for the option Background cannot be used. in the Messages window when I use that option for the background. It works fine though :), Now, if you just show me where I can put these tagging rules in the stylesheet notebook I'll be able to define a theme of colors to use throughout the notebook. – jmlopez Jul 01 '12 at 16:00
  • The answer to my question in the comments: Cell[StyleData["Notebook"], TaggingRules->{"myGreen"->RGBColor[0,.1,0]}]. The only thing that keeps bothering me is the messages that I get when I change the expressions using CurrentValue. Other than that, I believe you have answered the question. Gracias! – jmlopez Jul 01 '12 at 16:17
  • @jmlopez, I'm on it. It seems it is not as simple as I thought. Attaching the rules is simple, but making the CurrentValue refer to the style notebook and not to the notebook you're using is not so straightforward for me. – Rojo Jul 01 '12 at 16:20
  • However, I'm seing fun things while investigating, such as the background of some cell style changing colors while I switch the input notebook from one to the other, hehe – Rojo Jul 01 '12 at 16:21
  • I had forgotten Albert Retey answer in which he gave a link to the joy of tagging. I think I will spend today reading more about this. – jmlopez Jul 01 '12 at 16:26
  • Using some "myParameters" style should be smarter – Rojo Jan 16 '14 at 16:03
8

A style sheet is nothing more than a specific sequence of Mathematica Cell and StyleData expressions. An excerpt looks like this:

Cell[StyleData["SmallText", "Presentation"],
 CellMargins->{{60, 10}, {9, 9}},
 LineSpacing->{1, 5},
 FontSize->18]

Cell[StyleData["SmallText", "Condensed"],
 CellMargins->{{8, 10}, {5, 5}},
 LineSpacing->{1, 2},
 FontSize->9]

Cell[StyleData["SmallText", "Printout"],
 CellMargins->{{2, 2}, {5, 5}},
 TextJustification->0.5,
 Hyphenation->True]

There is no reason these expressions cannot be generated programmatically using the familiar tools of expression construction and manipulation. If you have a style sheet that needs frequent changes create a meta-stylesheet program that builds the desired style sheet with whatever parameters you give it.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • I don't think this answers the question (or if it does, it's not clear to me). The question was not on how to programmatically generate stylesheets, but how to include functions in them. How would you, for example, add the following function in the input style BackgroundColor -> RGBColor[1,0,"current minute"/60], so that a new input cell has a different colour based on when it is created? – rm -rf Jul 01 '12 at 14:38
  • 1
    @R.M I based my answer on: "The reason I would like to do this is that later on, if I change the color, every style that depends myColor will automatically change." – Mr.Wizard Jul 02 '12 at 00:10
5

I believe it is like what @R.M said in the comments. And I agree with @Mr.Wizrd suggestion of using a meta-stylesheet. But in case you just want to do this in one stylesheet (eg. restrict in the private one), this is how I did:

Open a new plain notebook "nb", in its private stylesheet, create three style like this:

Cell[StyleData["color"],
     FontColor->RGBColor[1,0,0]]

Cell[StyleData["style1"],
     FontSize->30,
     FormatType->color]

Cell[StyleData["style2"],
     FontSlant->"Italic",
     FormatType->color]

You may find that style1 and style2 is still without color. It's because color style is not defined for this stylesheet notebook but for "nb".

Now in "nb" you can create cells with style1 and style2:

Cell["This is style1.", "style1"]

Cell["This is style2.", "style2"]

You can see it has been correctly formated. And if you change the color in color style definition cell, color of style1 and style2 will both be changed.

(Obviously this method works fine for simple case, but still far from what if macros or variables would achieve :( )

Silvia
  • 27,556
  • 3
  • 84
  • 164
  • This is actually something I wanted to do but had no idea how to do it. I will be experimenting with a combination of this and Mike's answer. – jmlopez Jul 01 '12 at 14:49