12
$\begingroup$

I would like to add a couple UI features from cells in python notebooks that I find helpful. Specifically, these two features from google's collaboratory and vs-code's jupyter mode:

To the left of each code/input cell,

  1. Add timing information
  2. Add icon showing whether the cell was successfully run or had errors/warnings
  3. Add length of output in tooltip (if applicable)

enter image description here

$\endgroup$

1 Answer 1

12
$\begingroup$

Here's a first attempt:

byteString[b_] :=
 Which[
  b < 10^3, ToString[b] <> "B",
  b < 10^6, ToString[NumberForm[b/10.^3, 3]] <> "kB",
  b < 10^9, ToString[NumberForm[b/10.^6, 3]] <> "MB",
  True, ToString[NumberForm[b/10.^9, 3]] <> "GB"
  ]

handler = Replace[Hold[_, True] :> (errors = True)];
SetOptions[EvaluationNotebook[],
 {
  CellProlog :> (
    startTime = AbsoluteTime[];
    outSize = 0;
    aborted = False;
    $PrePrint = (
       outSize += ByteCount[#];
       If[MatchQ[#, $Aborted|$Aborted[]], aborted = True;];
       #
       ) &;
    errors = False;
    SetOptions[EvaluationCell[],
     CellDingbat -> ToBoxes@Pane[
        ProgressIndicator[Appearance -> "Necklace", ImageSize -> 12], 
        FrameMargins -> 3
        ]
     ];
    Internal`AddHandler["Message", handler];
    ),
  CellEpilog :> (
    SetOptions[EvaluationCell[],
     CellDingbat -> ToBoxes@
       If[outSize > 0,
         Tooltip[#,
           Style[Row@{byteString[outSize]}, 8.5, GrayLevel@0.3]
           ] &,
         # &]@Style[
         Column[
          {
           Which[
            errors, Style["\[WarningSign]", 20, Red],
            aborted, Style["\[WarningSign]", 20, Orange],
            True, Style["✓", 20, Darker@Green]
            ],
           Style[
            GeneralUtilities`TimeString[AbsoluteTime[] - startTime],
            8.5, GrayLevel@0.7
            ]
           },
          Center, -0.3
          ],
         ShowStringCharacters -> False
         ]
     ];
    $PrePrint =.;
    Quiet@Internal`RemoveHandler["Message", handler];
    )
  }
 ]

Pause@1

1/0
(* Power::infy: Infinite expression 1/0 encountered. *)
(* ComplexInfinity *)

Table[i^2, {i, 100}]
(* {1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, \
225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, \
841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, \
1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601, \
2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, \
3969, 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, \
5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, \
7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, \
9216, 9409, 9604, 9801, 10000} *)

enter image description here

This makes use of CellProlog, CellEpilog and $PrePrint, together with message handlers. Some notes:

  • As can be seen by the screenshot above, there are currently four different states that can be displayed: Currently running (indicated by the spinner), completed with messages (red warning sign), completed without messages (green checkmark) and aborted (orange warning sign). For completed/aborted cells, the total time is displayed, and the tooltip contains the total number of bytes of the output (if there is any)
  • I first tried to use CellEvaluationFunction to wrap the actual evaluation. Unfortunately, we get the box structure of the entire cell like that, which makes processing rather difficult. CellProlog and CellEpilog on the other hand do not interfere with the actual evaluation at all, and have thus a significantly lower risk of breaking anything.
  • I am using a message handler to detect if any message was actually printed during the evaluation of the cell. See this Q&A for an explanation of how they work.
  • The timing is done by recording the AbsoluteTime before and after the evaluation.
  • The results are displayed in a CellDingbat
  • I am measuring the output size by looking setting $PrePrint and measuring the ByteCount of everything that will be printed.
  • I check for aborted evaluations by comparing the expressions passes to $PrePrint to $Aborted and $Aborted[]. This might fail in some weird edge cases, but should mostly work.
  • This approach uses $PrePrint, CellProlog and CellEpilog. So if any of those are set by some other code, this will break.
  • The current version just sets the CellProlog and CellEpilog options at the notebook level, but it should be easy enough to put this into a stylesheet.
$\endgroup$
6
  • $\begingroup$ ResourceFunction["DarkMode"][] $\endgroup$
    – user5601
    Aug 15 at 21:38
  • 1
    $\begingroup$ Can you make it work in that style? I think just making the dingbat background transparent would do it. $\endgroup$
    – user5601
    Aug 15 at 21:43
  • $\begingroup$ I'm in 13.1, this is my effect $\endgroup$
    – yode
    Aug 16 at 5:31
  • $\begingroup$ @yode I can reproduce the same behavior... It seems they changed how cell labels interact with cell dingbats in 13.1 (The underlying styles are the same, so it must be something more fundamental). Unfortunately, I don't know how to change the behavior $\endgroup$
    – Lukas Lang
    Aug 16 at 9:55
  • 2
    $\begingroup$ @user5601 I couldn't find a nice way to get it to look perfectly. As far as I can tell, it's impossible to disable the inheritance of the background color. You can try to set the background color of the CellDingbat to CurrentValue[{StyleDefinitions, "Notebook", Background}], but that leaves some ugly borders at the top and bottom for me $\endgroup$
    – Lukas Lang
    Aug 16 at 10:17

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.