14
$\begingroup$

Can I pipe an OutputStream object created by OpenWrite (or similar) to a notebook instead of a file? I am aware that I can use NotebookWrite, but I want to have all the Write statements in my code to be redirected to file/notebook by simply switching a variable.

$\endgroup$

2 Answers 2

16
$\begingroup$

I am not sure that this is possible. The $Output and $Messages variables hold the output stream to where the standard output (and the message output) from the kernel goes. If you check these, you'll see that they're simply set to stdout.

If you remove ReadProtected from NotebookWrite, you'll see that it is passing data to the front end instead of writing to an output stream.

All this suggest that it's not possible to redirect an output stream to an arbitrary notebook.


Instead of using output streams to switch the output "device", I'd suggest switching the output function. You could have a function write which can be set to write = Write[outputChannel, #]& or to write = NotebookWrite[nb, #]&.

If you already have a lot of code using Write then it would be inconvenient to rewrite it to use an alternative write function. But you can temporarily redefine Write using a block:

Block[{ Write = NotebookWrite[nb, #2]& },

 .... (* code called here *)

]

If you need to temporarily redefine Write with something that itself uses Write, then you can use the trick described here to "wrap" it with extra code.


Note: As @Heike said below, NotebookWrite[nb, Cell[BoxData[ToBoxes[#2]], "Output"]] & is better for writing into notebooks than the simple NotebookWrite[nb, #2]& I used above.

$\endgroup$
8
  • 2
    $\begingroup$ +1 for using block, although you would probably need to define Write = NotebookWrite[#1, Cell[BoxData[ToBoxes[#2]], "Output"]] & or something to make it work. $\endgroup$
    – Heike
    Jan 31, 2012 at 13:42
  • $\begingroup$ I was afraid of this. Isn't the purpose of streaming/piping to provide means of saving data by easy redirection? Considering, that this bug is still not resolved, piping is really not that easy in Mathematica... $\endgroup$ Jan 31, 2012 at 13:54
  • 2
    $\begingroup$ @Istvan I agree with Szabolcs. Streams are a pretty low-level I/O tool, suitable also for reading/writing of unstructured data (e.g., not even representing valid Mathematica expressions). Notebooks are structured documents, and writing to them assumes, to my mind, higher-level I/O abstraction. If you feel a need to redirect there, it might mean that your current design is too low-level (regarding logging or whatever purpose it is you use Write for), so you will be better off by redesigning this part of your code to use higher-level constructs. $\endgroup$ Jan 31, 2012 at 15:10
  • $\begingroup$ @IstvánZachar, I did not realize that that bug was that old. $\endgroup$
    – rcollyer
    Jan 31, 2012 at 16:00
  • $\begingroup$ @István "bump" doesn't really work on a newsgroup as the old message may very well be lost in most newsreaders. Funny the moderator even let it through .. $\endgroup$
    – Szabolcs
    Jan 31, 2012 at 16:03
6
$\begingroup$

Here's a way that allows you to actually redirect output to a notebook by using the newish DefineStreamOutputMethod function (which dates to 9.0). It's a bit hacky by its very nature, and the version I have here creates a new notebook for output, but I think it's a reasonable place to start. There's certainly room for refinement!

DefineOutputStreamMethod["Notebook",
 {
  "ConstructorFunction" ->
   Function[{name, isAppend, caller, opts},
    With[{state = Unique["NotebookOutputStream"]},
     state["notebook"] =
      CreateNotebook[FilterRules[opts, Options[Notebook]]];
     {True, state}]],

  "CloseFunction" -> Function[state, ClearAll[state]],

  "WriteFunction" -> Function[{state, bytes},
    (* Since we're writing to a cell, we don't want that trailing newline. *)
    With[{out = bytes /. {most___, 10} :> FromCharacterCode[{most}]},
     With[{nb = state["notebook"]},
      If[NotebookInformation@nb === $Failed || out === "",
       {0, state},
       NotebookWrite[nb, Cell[out, "Output"], After];
       {Length@bytes, state}]]]]
  }];

Try it out like this:

With[{stream = OpenWrite[Method -> "Notebook"]},
 Block[{$Output = stream},
  Do[Print[i], {i, 10}];
  Close[stream]]] 
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.