Skip to main content

stylesheet - Automatic generation of cell tags


A similar question has been asked before, with no answers. I would love to have a way to easily reference objects associated with counters. To provide such a capability for things like figures, tables, etc., it would be great if it was possible to automatically create tags as part of a Stylesheet definition. Can this be done?


For example, I have a Stylesheet definition for my figure captions that reads like this:


Cell[StyleData["FigureCaption", StyleDefinitions -> StyleData["SmallText"]],
TextAlignment->Center,
TextJustification->0.,
CounterIncrements->"Figure",

MenuSortingValue->10000,
FontFamily->"Source Sans Pro",
FontSize->12,
FontWeight->"Plain",
FontSlant->"Plain",
FontVariations->{"StrikeThrough"->False,
"Underline"->False},
FontProperties->{"ScreenResolution"->Automatic}]

Is it possible to add to this some code that will automatically create a tag of the form, say, "figctr", where ctr would be the value of the counter associated with the caption?



CLARIFICATION: Suppose I have a document that includes many FigureCaption cells that reference the counter above. Is it possible to programmatically create CellTags that reference the current value of the counter on each of these cells?


For example, I would like the 5th FigureCaption cell in notebook to have the CellTag "fig'ctr'", where 'ctr' is 5.


I use CellTags for navigation in a lecture notebook. It would very helpful to have the ability to add and delete FigureCaption cells in the notebook and then have some code that would update the cell tags to their appropriate values. Here is a related question I asked.


Note: As outlined in my comment, updating cell tags after having inserted more figures cannot be done in isolation: If we update the tags, the question comes up as to what happens to links after you edit the notebook. The part that's problematic about this is of course not the updating of the CellTags, but the fact that if we simply update the tags so they correspond to the counters, then existing references will point to the wrong cells. This means that we also need to search for and update the references to all of the tags.



Answer



Here's a solution. The inspiration for much of the code came from Eric Schulz's lecture on "Wisdom Gained from Publishing a CDF ebook" as well as as some code from this post. Below are the components for creating automatic cell tags for FigureCaption cells, but the idea can be extended to more than one type. The following are the components needed to keep everything updated for the cell type "FigureCaption" but can be used for multiple cell types.




  1. A custom stylesheet that includes a definition for a "FigureCaption" cell. Make sure to setup the counters as shown.


    Cell[StyleData["FigureCaption"], CellMargins->{{54, 24}, {10, -5}}, TextAlignment->Center, LineSpacing->{1, 2}, ParagraphSpacing->{0, 5}, LanguageCategory->"NaturalLanguage", ScriptLevel->1, CounterIncrements->"FigureCaption", FontFamily->"Times New Roman", FontSize->12]





  2. A notebook that contains some FigureCaption cells. This is the notebook we will be manipulating I will refer to as the lecture notebook.




  3. A function that builds the cell tags for the lecture notebook. The function is located in the Action Menu below.




DynamicModule[{found}, celltagdialog = Tooltip[Dynamic[ ActionMenu[ "C", {"Build Cell Tags" :> {SelectionMove[InputNotebook[], Before, Notebook]; found = NotebookFind[InputNotebook[], "FigureCaption", Next, CellStyle]; While[! FailureQ[found], SetOptions[NotebookSelection[], CellTags -> "Figure " <> ToString[ CurrentValue[ NotebookSelection[InputNotebook[]], {"CounterValue", "FigureCaption"}]]]; found = NotebookFind[InputNotebook[], "FigureCaption", Next, CellStyle];];}}, Appearance -> "Palette", FrameMargins -> None, Alignment -> Center, BaselinePosition -> Axis, ImageSize -> {24, 24}]], "Show Gridbox Dialog", TooltipDelay -> 0]]




  1. A function that creates the links to the cell tags in the lecture notebook. These are basically pointers that point to updated tags so that you can hyperlink to them... and them opens and closes groups to make it look nice.


CreateLinks[nb_] := Module[{tagLis = {}, data, imax, i}, SelectionMove[nb, Before, Notebook]; SelectionMove[nb, Next, Cell]; data = NotebookRead[nb]; While[data =!= {}, tagLis = Join[tagLis, Cases[data, Rule[CellTags, tg_] :> tg]]; SelectionMove[nb, Next, Cell]; data = NotebookRead[nb]]; MapThread[ Which[StringContainsQ[#1, "Figure"], " " <> #1, True, Style[#1, Bold]] :> {FrontEndExecute[FrontEndToken["SelectAll"]]; FrontEndExecute[{FrontEndToken[InputNotebook[], "SelectionCloseAllGroups"]}]; NotebookLocate[#1], FrontEndExecute[{FrontEndToken[ "SelectionOpenAllGroups"]}];} &, {tagLis}]]


Applying the functions given in 3 will keep the cell tags automatically generated so that even when you insert a new cell, the cell tags are renamed using 3. The function in 4 will rebuild the links which can be used for hyperlinking. These need to be rebuilt whenever we add or remove or move cells that have cell tags in the notebook or they won't match up. Let me know if you would like to see more code.


Below is a short gif I made to illustrating how we can do this in pratice. I have many cells with tags and some of them with counters that I reference in the tags. As you can see, I used a docked cell with a dropdown menu to rebuild the celltags.


enter image description here


The pointers created in 4 can be used for hyperlinks to any celltag in the notebook. Below is a gif illustrating how this can be implemented. Make sure to rebuild the docked cell to keep these pointers updated. I prefer to update the docked cell at the same time as I build the celltags, so it's one click instead of two. Again I used a dropdown menu and use the CreateLinks function above for its content.


enter image description here


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

equation solving - Invert and fit implicitly defined curve

I need to fit an implicitly defined curve. I thought I could get some data out of Solve , and then using FindFit . Therefore, I would like to find the relation the parametric curve defined by $F(x,y)=0$: Solve[-(1/2) + 1/2 (0.41202 BesselK[0, 0.1 Sqrt[x^2 + y^2]] + (0.101483 x BesselK[1, 0.1 Sqrt[x^2 + y^2]])/Sqrt[x^2 + y^2]) == 0, y] But I can't get an output: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system. Since many of the methods used by Solve require exact input, providing Solve with an exact version of the system may help. >> Edit: In particular, I would like to fit the data coming from the curve with the expression of another curve, and not with a function $f(x)$. In particular, since this clearly looks like a cardioid , I would like it to fit to something like it. What other strategies could I try?

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...