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.
- 47,032
- 20
- 143
- 291
2 Answers
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.
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]]]
- 18,498
- 2
- 46
- 92
Write = NotebookWrite[#1, Cell[BoxData[ToBoxes[#2]], "Output"]] &or something to make it work. – Heike Jan 31 '12 at 13:42Writefor), so you will be better off by redesigning this part of your code to use higher-level constructs. – Leonid Shifrin Jan 31 '12 at 15:10