3
$\begingroup$

I want to write a notebook file by myself. I use a text editor (such as Vim) to do that, for the purpose of having full control of my code I am writing, and to really understand what is in my .nb file when I read it later on my version control repository.

So, I headed over to the following Wolfram Tutorial, Manipulating Notebooks, and learned that it is possible, and I am rather enthusiastic about it!

But I got stuck in a quite basic stage:

For example, how do I create a button, that when clicked prints the number $4\cdot5$, for example?

In the front-end, one just does

Button["Click here!", Print[4*5]]
(*20*)

Using the "Cell"->"Show Expression" tool (or pressing ++e, I get the following text:

Cell[BoxData[
 RowBox[{"Button", "[", 
  RowBox[{"\"\<Click here!\>\"", ",", 
   RowBox[{"Print", "[", 
    RowBox[{"4", "*", "5"}], "]"}]}], "]"}]], "Input",
 CellChangeTimes->{{3.835353874007769*^9, 3.835353879618236*^9}},
 CellLabel->"In[10]:="]

Ok so I guess I can delete the CellLabel and CellChangeTimes options for Cell, but is there any possibility to make this Click here! button shorter when typed by a human in a text editor?

Do I have to take care for all those ", } and RowBoxs?

Even a simple task such as assigning the symbol x a numerical value seems too long:

Cell[
    BoxData[
       RowBox[{"x", "=", "2"}]], "Input"]

Is it possible to shorten it?

My .nb now is this:

(*Low level notebook*)
Notebook[{
    Cell[
        "Some text","Text"
    ]
}
    ,WindowFrameElements->{"MinimizeBox","CloseBox","DocumentIcon","ResizeArea"}
    ,Saveable->False
    ,WindowTitle->"My Awesome Notebook"
    ]
$\endgroup$
5
  • 5
    $\begingroup$ I would seriously reconsider this. As you have seen, the box structure of notebook files is extremely verbose, even if technically readable. If version control is your main goal, I would suggest you look into package (.wl) files. And a side node: Your button example is not really correct: What you are showing is the cell expression of the input cell that will create the button, instead of the output cell that contains the button (that one is at least a bit simpler than what you show) $\endgroup$
    – Lukas Lang
    Jul 15 at 16:41
  • $\begingroup$ @Lukas Lang I will certainly check the .wl files you mentioned, but let me ask again: there is no shortcut I can make, at least regarding the two examples I have presented above? Moreover, can you show me the "more" correct command for the button cell? $\endgroup$
    – tush
    Jul 15 at 17:35
  • $\begingroup$ @tush - when I evaluate your Button expression, the output cell expression is Cell[BoxData[ButtonBox["\<\"Click here!\"\>", Appearance->Automatic, ButtonFunction:>Print[4 5], Evaluator->Automatic, Method->"Preemptive"]], "Output", CellChangeTimes-> ..., CellLabel->....] $\endgroup$
    – Jason B.
    Jul 15 at 17:49
  • $\begingroup$ @tush One of the main reasons why .nb files do not play nice with version control systems, is that each time you evaluate one (even if nothing changes), Mathematica will still modify quite some cell properties such as ExpressionUUID, CellChangeTimes etc. And in the more recent MMa versions there is no way to turn it off, cf. link. So for all book keeping purposes, .wl/.m files are a much better choice. $\endgroup$
    – vsht
    Jul 16 at 9:06
  • $\begingroup$ @vsht I understand. Thanks a lot for your advise! $\endgroup$
    – tush
    Jul 16 at 9:17
3
$\begingroup$

As noted in the comments, I would advise against doing this. Package files (.wl) are designed to be plaintext and are as such way more compatible with version control and text editors.

That being said, there are a few things you could do to make your code more readable:

  • Omit input cells if you are interested only in the output: For your button example, you can just include the output cell containing the button:

    Cell[BoxData[
      ButtonBox["Click here!",
        Appearance -> Automatic,
        ButtonFunction :> Print[4 5],
        Evaluator->Automatic,
        Method->"Preemptive"
    ]],"Output"]
    

    This gets you around one layer of box structure. (In this particular example, you need to write quite a bit of stuff, but this can help in many cases)

  • Strings can often be simplified from "\"\<String\>\"" to "\"String\"", see this answer for more details.

  • Identify and omit options and boxes that are unnecessary: Grid@{{a}} for example produces the following output cell structure:

    Cell[BoxData[
     TagBox[GridBox[{
        {"a"}
       },
       AutoDelete->False
       ],
      "Grid"]], "Output",
     CellContext->"Cell$$11425`",
     CellLabel->"Out[8]=",
     CellID->531179843]
    

    You already noticed that CellLabel is not really needed. In fact, the following will be virtually identical in most cases:

    Cell[BoxData[
     GridBox[{
      {"a"}
     }]], "Output"]
    
  • Note: Thanks to @ihojnicki we know that the following is undocumented, and that the behavior described should not be counted on. Use at your own risk!

    Apparently, the following works:

    Cell[BoxData@"Button[\"Click here!\", Print[4*5]]","Input"]
    

    This results in a cell with the fully parsed box structure you show in your question. It looks like BoxData["string"] will convert the "string" part into boxes using something like FrontEnd`UndocumentedTestFEParserPacket. This means that you can put essentially anything there just like you would enter it into the front-end (careful about " of course)

$\endgroup$
2
  • 1
    $\begingroup$ That behavior of BoxData should not be counted on. BoxData is for box expressions, not plaintext strings. $\endgroup$
    – ihojnicki
    Jul 17 at 22:47
  • $\begingroup$ @ihojnicki Thanks for providing an authoritative statement about the supported-ness of that "feature"! I have updated the answer to include a note on that. $\endgroup$
    – Lukas Lang
    Jul 18 at 11:47

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.