Changing stdout, redefining puts and avoiding console show

Zipguy 2014-01-31 - You can find out my email address by clicking on Zipguy.

This started in Tkchat when I had a problem on Windows, but it does highlight some useful things, namely, nice short illustrations of transchan and refchan. I'd never even thought about channels, nevermind transchan and refchan. What I said, in chat, was something like 'I'm thinking of writing a Bwidget to replace the puts function in a namespace/package combo', and what I got back was an answer that puzzled me. It was 'Maybe you can also close stdout and then open a reflected channel to catch everything that's being puts'ed to stdout'. So instead of asking a question, I thanked everyone in chat. Then I got two teriffic answers, totally different, by two different people in chat a few minutes later.

Here's the first one which was called 'transforming stdout'. It's an example of (misusing) a transchan: We chan push a transformer onto stdout that intercepts writes and simply neglects to pass them on to the original channel.

# see (chan push)
namespace eval totk {
    proc initialize args {info procs}
    proc finalize args {}
    proc clear args {}
    proc flush {handle} {
        flush $handle
    proc write {handle data} {
        .t insert end $data
    namespace export *
    namespace ensemble create

package require Tk
pack [text .t]
chan push stdout totk 

and the second one which was called 'replacing stdout'. This is almost the same thing using a refchan. It relies on file descriptors being reused: if you close stdout and immediately create another channel, it will inherit the same fd and nobody is the wiser. Reportedly this works cross platform.

package require Tk
pack [text .t]
namespace eval refchan {
    namespace ensemble create -map {
        initialize init
        finalize close
        watch watch
        write write
proc refchan::init {id mode} {
    return {initialize finalize watch write}
proc refchan::close {id} {
proc refchan::watch {id spec} {
proc refchan::write {id data} {
    .t insert end $data
    return [string length $data]
close stdout
set fd [chan create write refchan]
fconfigure $fd -buffering line
puts "Hello, World!"
parray tcl_platform

aspect interjects much later: looking again at tcllib, there are some standard modules that can make this even simpler:

package require tcl::chan::textwindow
package require tcl::transform::observe
::tcl::transform::observe stdout [::tcl::chan::textwindow .t] [::tcl::chan::textwindow .t]

Refchans and transformers are easy, as evidenced by the implementations above, but knowing about standard components can save some bug-hunting :-).

Zipguy continues: I tried the first one. Lo and behold, I used it in my program ezsdx - a small frontend for sdx and it worked great! Needless to say, I was extremely happy with that. This is what ezsdx looked like, once I first got it working:

Notice, above, that all my puts statements are not it a separate window, but in a text item, in a frame in my main application. Way to cool! And even better, SDX's puts are there too! So I did a lot of work on ezsdx, and have a much better version of it, on Windows (7). It gets rid of the problem of havng to switch back and forth, imbetween the app's window and the console window, to see if your program worked, or for one that you haven't written, like SDX, which does uses puts completely.

Even better you can use colors in text widgets, like this [L1 ] or:

The second solution looks very interesting too! Haven't tried it yet.

Comments are welcome.

APN: Interesting. However, the second solution does not seem to work, at least on Windows. stdout gets closed and the new channel is created. But the new channel does not get "attached" to stdout. puts $fd foo works but puts foo errors out with a stdout channel not found message. chan names also does not show stdout. Possibly this issue is related to the Windows wish console which does not really seem to be a full fledged channel, or perhaps the stdout replacement does not work with reflected channels.

Zipguy 2014-05-18: I'm real happy with the first solution. I want to keep trying it, and looking for improvements, and then make it into a proper package and namespace, that I can install into each of the programs I've written. That should make them a lot more portable, and get rid of the problems on Window Vista, or up. Here's what I have so far, the console window is collapsable: