The answer here is TaggingRules
. Internally, the FE uses this all the time, and it solves many problems. And, BTW, the technique I'm about to describe is hinted at in the documentation for TaggingRules
, so go look it up.
What we're looking for is something which doesn't rely on temporary kernel variables, crosses between notebooks, and doesn't require a displayed DynamicModule
output to resolve. Ideally, we should be able to serialize its value across M-- sessions as well. DynamicModule
wormholes almost solve the problem, but there's no way to access such a variable on the rhs of a notebook-level option, such as Visible->Dynamic[x]
.
Dynamic
can access the current value of any option using CurrentValue
. TaggingRules
is an option which exists purely to hold metadata, so setting values on it will have no effect on any part of the system. If you happen to be using a palette, Mathematica will automatically remember the TaggingRules
attached to that palette notebook between sessions, which means that you can store palette state without having write access to the palette file (this is how, for example, the Basic Math Assistant palette remembers which sections are open across sessions).
Here's a first cut at a TaggingRules
solution...
CurrentValue[EvaluationNotebook[], {TaggingRules, "x"}] = True;
With[{nb = EvaluationNotebook[]},
CreateDocument[{"hello"},
Visible -> Dynamic[CurrentValue[nb, {TaggingRules, "x"}]]]];
Checkbox[Dynamic[
CurrentValue[EvaluationNotebook[], {TaggingRules, "x"}]]]
Note that I used the string "x" rather than the symbol 'x'. This isn't absolutely necessary, but it does prevent unnecessary round trips to the kernel, conflicts with existing symbols, etc., so it's good practice. This is a good start, but let's improve on it. First, let's get rid of the first line. The requirement to initialize a value is annoying and requires extra code. CurrentValue
has an undocumented (but don't worry, it's not going away) third argument which gives assigns a default value in case one doesn't exist. So, in a fresh notebook, try this code (ignore the red syntax coloring):
With[{nb = EvaluationNotebook[]},
CreateDocument[{"hello"},
Visible -> Dynamic[CurrentValue[nb, {TaggingRules, "x"}]]]];
Checkbox[Dynamic[
CurrentValue[EvaluationNotebook[], {TaggingRules, "x"}, True]]]
Some consequences of this...
- The document you're creating has a dependency on a specific
NotebookObject
which can't possibly survive the parent notebook being closed or Mathematica quitting.
- The setting will now be saved with the parent notebook, which is probably a good thing. But when you open the notebook in the future, you'll have to re-create the child notebook.
There are further permutations which allow you some different behaviors. For example, if you want something which only lasts in this session of Mathematica, you could apply the TaggingRules
to $FrontEndSession
.
CreateDocument[{"hello"},
Visible ->
Dynamic[CurrentValue[$FrontEndSession, {TaggingRules, "MyApp", "x"}]]];
Checkbox[Dynamic[
CurrentValue[$FrontEndSession, {TaggingRules, "MyApp", "x"}, True]]]
This will clean itself up as soon as you quit Mathematica, as do all changes to $FrontEndSession
, but it requires none of the tracking of NotebookObject
s and all of the disadvantages that may entail. This is essentially a system-wide DynamicModule
of sorts. Because it is global, note that I added an extra selector ("MyApp"). This effectively defines a namespace so that we don't collide with others wanting to do the same thing we just did.
DynamicModule
in the window I create, so I am not sure where to put theInheritScope -> True
this time. It won't work in eitherDynamic[x]
or inCreateDocument
, nor is it fully documented to figure out the correct usage in this case. Do you have any hints? I'll update the main post in the meantime. $\endgroup$