21
$\begingroup$

Let's say I'm debugging a program step by step and want to Print some expressions (using ShowIt, for example).

Is there a way to output the result of Print on top of already printed expressions instead of at the bottom?

$\endgroup$
14
$\begingroup$

This is admittedly messy, but something along these lines might work:

insertBelowEvaluationCell[expr_] := 
  (SelectionMove[EvaluationNotebook[], After, EvaluationCell]; 
   NotebookWrite[EvaluationNotebook[], Cell[BoxData@ToBoxes[expr], "Print"]])

This function moves the insertion point just below the evaluation cell before inserting the text or expression to be printed.

Let's test it:

insertBelowEvaluationCell /@ Range[10]

Problems:

  • This messes with the insertion point in the notebook which can be modified interactively as well. Perhaps it's better to write the output into a separate notebook instead.

  • It does not work in command line mode (without a front end).

  • It's slow (noticeably slower than Print).

$\endgroup$
  • 1
    $\begingroup$ I don't think there's a way to do it without a front end (unless you are on a terminal interpreting escape sequences, in which case you could output the relevant ones with Print, but even then, you might have problems determining how many lines to go up). $\endgroup$ – celtschk Jun 14 '12 at 10:38
9
$\begingroup$

This is an arguably even messier solution than Szabolcs', and its performance isn't going to win any awards, but it has some (somewhat dubious) advantages:

  1. It allows you to choose whatever target you want for the printing, putting things in an arbitrary notebook, and the output will appear at the current selection in that notebook;
  2. After that, printing won't affect the current selection;
  3. You can have many different targets for printing;
  4. It provides another use for the seemingly useless CellTags feature!

The function in question creates a dummy cell that will eventually contain the output as a side-effect, and returns a closure that you call like Print (more or less; it only takes one argument) in order to direct output to that target cell. The function is here:

Pillsy`UpsideDownPrinter[nb_: InputNotebook[]] :=
 With[{
   tag = ToString@FileHash[
      StringToStream@ToString@{NotebookGet@nb, Date[]}]},
  NotebookWrite[nb, Cell["", CellTags -> tag]];
  Function[content,
   Module[{nbExpr = NotebookGet[nb], pos},
     pos = Position[nbExpr, Cell[___, CellTags -> tag, ___]];
     NotebookPut[
      MapAt[
       Replace[#,
         Cell[stuff_, opts___] :>
          Cell[CellGroup[{Cell[content], Cell[stuff]}], opts]] &,
       nbExpr,
       pos],
      nb]];]]

First, NotebookWrite is used to create a Cell in the target notebook with a unique CellTag (which is what all the FileHash stuff is for), and then we continually modify the target notebook's structure using standard Mathematica structural operations. The stuff with CellGroup allows us to effectively create a "linked list" of generated cells in a (probably futile) stab at maintaining some degree of efficiency.

You use it like so:

printer = Pillsy`UpsideDownPrinter[];

printer["foo!"]; printer["bar!"]; printer["baz!"];

which will give output that looks like

"baz!"
"bar!"
"foo!"
$\endgroup$
  • $\begingroup$ This looks interesting, I'll test it tomorrow. It's a good example of what can be achieved by manipulating notebooks. $\endgroup$ – faysou Apr 11 '12 at 23:09
6
$\begingroup$

It turns out that the ordering of the output of Print cells is determined by whether the cells printed have the options CellAutoOverwrite and GeneratedCell both set to False. If so, the cells are printed at the top (in reverse order), otherwise they are printed at the bottom.

In general, it is not easy to control whether the printed cells have these options set to False using Print. On the other hand, the mostly equivalent version using CellPrint does not have this problem. So, using CellPrint with appropriate options instead of Print will print the cells in "reverse" order. However, having these options set to False means the next time the input is evaluated, the cells will not be deleted.

This leads to the following function:

SetAttributes[ReversePrints, HoldAll];
ReversePrints[expr_] := Block[{Print=myprint, uuid=CreateUUID[]},
    Internal`WithLocalSettings[
        Null,
        expr,
        (* clean up *)
        CurrentValue[Cells[CellTags->uuid], CellAutoOverwrite]=Inherited;
        CurrentValue[Cells[CellTags->uuid], GeneratedCell]=Inherited;
    ]
]

Clear[myprint]
myprint[a__] := CellPrint @ Cell[
    BoxData@MakeBoxes@SequenceForm[a],
    "Print",
    CellTags->uuid,
    GeneratedCell->False,
    CellAutoOverwrite->False
]

The function myprint "print" cells with the desired options. It also includes a CellTags option so that the desired options can be cleared afterwards.

Example:

ReversePrints[Do[Print[i], {i, 3}]]

3

2

1

One final note: CellPrint is noticeably slower than Print.

$\endgroup$
  • $\begingroup$ I did not mean to make this a community wiki post, lol. $\endgroup$ – Carl Woll Sep 26 '18 at 2:37
  • 1
    $\begingroup$ I removed it, you can flag things should you have similar accidents. $\endgroup$ – J. M. will be back soon Sep 26 '18 at 3:10
  • $\begingroup$ @J.M.issomewhatokay. Thanks! $\endgroup$ – Carl Woll Sep 26 '18 at 3:17
2
$\begingroup$

Here is robust and efficient solution which exploits the observations described in the answer by Carl Woll (see also this answer), but is based on another idea.

We change the default style for printed output for EvaluationCell[] using the option "GeneratedCellStyles". After printing we move the insertion point right after EvaluationCell[] (in order to prevent autoscrolling), and then remove the options GeneratedCell and CellAutoOverwrite from the printed cells:

SetOptions[EvaluationCell[],
 "GeneratedCellStyles" -> {"Print" -> {"ReversePrintTag", "Print", GeneratedCell -> False,
      CellAutoOverwrite -> False}}]

Do[Pause[.1]; Print[i], {i, 3}]

SelectionMove[EvaluationCell[], After, Cell, AutoScroll -> False]
CurrentValue[Cells[CellStyle -> "ReversePrintTag"], GeneratedCell] = Inherited;
CurrentValue[Cells[CellStyle -> "ReversePrintTag"], CellAutoOverwrite] = Inherited;
3
2
1

For someone who extensively dubug differet parts of a program in one Notebook, it may be even more efficient to use unique tag for every evaluation (and the slow CreateUUID[] isn't needed: AbsoluteTime[] is sufficient for this purpose):

SetOptions[EvaluationCell[],
 "GeneratedCellStyles" -> {"Print" -> {"Print", 
     uniqueTag = "ReversePrintAbsoluteTime: " <> IntegerString@IntegerPart@AbsoluteTime[], 
     GeneratedCell -> False, CellAutoOverwrite -> False}}]

Do[Pause[.1]; Print[i], {i, 50}]

SelectionMove[EvaluationCell[], After, Cell, AutoScroll -> False]
CurrentValue[Cells[CellStyle -> uniqueTag], GeneratedCell] = Inherited;
CurrentValue[Cells[CellStyle -> uniqueTag], CellAutoOverwrite] = Inherited;

We can package it into a function which allows to reverse printing not only for cells generated by Print, but also Message, PrintTemporary, and for the ordinary outputs. It should be called at the beginning of a Cell it is applied to (after finishing evaluation it also takes care of defaulting the options changed for current Cell):

ClearAll[SetReversePrintingForCurrentCell];
SetReversePrintingForCurrentCell[style_String: "Print"] := 
  SetReversePrintingForCurrentCell[{style}];
SetReversePrintingForCurrentCell[l : {__String}] := 
 With[{uniqueTag = "ReversePrintAbsoluteTime: " <> IntegerString@IntegerPart@AbsoluteTime[], styles = l}, 
  SetOptions[EvaluationCell[], 
   "GeneratedCellStyles" -> 
    Table[style -> {style, uniqueTag, GeneratedCell -> False, 
       CellAutoOverwrite -> False}, {style, styles}], 
   CellEpilog :> (SelectionMove[EvaluationCell[], After, Cell, AutoScroll -> False];
     CurrentValue[Cells[CellStyle -> uniqueTag], GeneratedCell] = Inherited;
     CurrentValue[Cells[CellStyle -> uniqueTag], CellAutoOverwrite] = Inherited;
     SetOptions[EvaluationCell[], 
      "GeneratedCellStyles" -> Table[style -> style, {style, styles}]];
     CurrentValue[EvaluationCell[], CellEpilog] = Inherited;)]]

An example of use:

SetReversePrintingForCurrentCell[];
Do[Pause[.1]; Print[i], {i, 50}]

In the same way we can reverse printing of Messages:

SetReversePrintingForCurrentCell["Message"]
Off[General::stop];
f::msg = "Message `1`.";
Do[Message[f::msg, i], {i, 50}];

screenshot

Set reverse printing for all supported functions:

SetReversePrintingForCurrentCell[{"Print", "Message", "Output", "PrintTemporary"}];
Off[General::stop];
f::msg = "Message `1`.";
Do[Print[i]; Message[f::msg, i], {i, 50}];

screenshot of the output

$\endgroup$
1
$\begingroup$

Here is another solution which is even more efficient than my previous one and works at least starting from Mathematica 8. Significant advantage is that it configures at once reverse printing for all output styles defined in the default stylesheet (i.e. "Output", "Print", "Echo", "PrintTemporary", "Message" and so on) independently of their names. It can easily be extended for custom/non-default stylesheets. It works by temporary changing StyleDefinitions and CellEpilog on the Notebook level for the time of evaluation, and then reverting it to the defaults. Working on the Notebook level allows to avoid undesired autoscrolling at the end of evaluation completely (the bug: [CASE:4332680]). Here is the code of the function:

ConfigureReversePrintingForCurrentCell[] := AbortProtect[
  (* Causes auto-deletion of pre-existing output under the cell *)
  NotebookDelete@PrintTemporary[""];
  (* Work on the Notebook level auto-reverting to the defaults *)
  SetOptions[EvaluationNotebook[],
   {StyleDefinitions -> 
     Notebook[{Cell[StyleData[StyleDefinitions -> "Default.nb"]], 
       Cell[StyleData[All], GeneratedCell -> False, CellAutoOverwrite -> False]}, 
      StyleDefinitions -> "PrivateStylesheetFormatting.nb"],
    CellEpilog :> AbortProtect[
      FrontEndExecute@FrontEnd`NotebookSuspendScreenUpdates[EvaluationNotebook[]];
      SetOptions[EvaluationNotebook[], StyleDefinitions -> "Default.nb"];
      CurrentValue[EvaluationNotebook[], CellEpilog] = Inherited;
      FrontEndExecute@FrontEnd`NotebookResumeScreenUpdates[EvaluationNotebook[]];
      ]}]
  ]

The function should be called at the beginning of a Cell it is applied to. Here is an example of use:

ConfigureReversePrintingForCurrentCell[]
Off[General::stop];
f::msg = "Message `1`.";
Do[Pause[.5]; Print["Print ", i]; Echo[Row[{"Echo ", i}]];
 Message[f::msg, i]; CellPrint[TextCell["CellPrint " <> ToString[i], "Output"]], {i, 3}]
Style["Final Output", Red, Bold]

screenshot


Another solution which works entirely through CellEpilog and CellProlog, and is applied automatically for all cells of a Notebook (i.e. doesn't require putting a call to any additional function into input cell) can be found here.

$\endgroup$

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.