Extending the eTcl console

Richard Suchenwirth 2006-02-05 - As I've decided to build my PocketPC "IDE patchwork", under the ceremonial name of Sepp, based on eTcl, here are notes on how components can be plugged in for easy reach: in the console menus, and in the toolbar.

WikiDbImage ext_console.jpg

First it is important to know that we're dealing with two "sibling" interpreters here, call them "Main" and "Console" neither being the slave of the other, and each being able to run code in the other:

   set mainres [console eval $consolecode]
   console eval {set consoleres [consoleinterp $maincode]}

This makes coding for both just a little bit more complicated.

Example, to add an item into an existing menu of the console (from htext as eTcl plugin):

 console eval {.menubar.help add command -label Help \
               -command {consoleinterp eval {htext::htext .h}}}

If used more often, here's a wrapper proc for the same purpose:

 proc menu+ {head label cmd} {
   # Add an item to the console menu.
   set cmd2 [list consoleinterp eval $cmd]
   console eval [list .menubar.$head add command -label $label -command $cmd2]
 }

Create a new main item in the console menu:

Here is a window selector that lists in an extra "Windows" menu the toplevels that are currently there, and raises the one selected. The menu is recreated every time a window gets a <Map> or <Destroy> event, so that it always stays up-to-date.

 proc toplevels {} {
    set res {}
    foreach w [winfo children .] {
       if {[winfo toplevel $w] eq $w} {
          lappend res $w [wm title $w]
       }
    }
    set res
 }
 console eval {
    if ![winfo exists .menubar.w] {
      .menubar insert 4 cascade -menu [menu .menubar.w] -label Windows
      proc winsel m {
         $m delete 0 end
         $m add command -label Console \
             -command {wm dei .; focus .}
         $m add separator
         foreach {w title} [consoleinterp eval toplevels] {
            $m add command -label $title -command \
                "consoleinterp eval {wm dei $w; focus $w}"
         }
      }
   }
 }
 proc WinSel'update {} {
     console eval {winsel .menubar.w}
 }
 bind all <Map>      WinSel'update
 bind all <Destroy> {after 1 WinSel'update}

2006-03-19 HE I am missing the toplevel '.'.

    $m add command -label {.} -command "consoleinterp eval {wm deiconify .; focus .}"

after the separator and we can select this window, too.

For another menu added to the console, see Visual cd.

With good old introspection I also found out how to add items to the toolbar that appears below the main console window. You just need to hand a 16x16 GIF icon (e.g. picked from iconview as eTcl plugin) to the console interp, and pack a button with it into the toolbar (see screenshot above). As proof of concept, I picked the "house" icon and associated it with the trivial operation "change to HOME directory", which cd without arguments does anyway:

 proc home.button {} {
   console eval {
    image create photo  gohome-16 -data {
   R0lGODlhEAAQAIUAAPwCBDw6PBQWFCQiJAQCBFxeXMTCxJyanDwyLDQqLFRS
   VLSytJSSlISChCQmJERGRFRWVGxubKSmpJyenGRmZLy+vOzq7OTi5Ly6vGRi
   ZPTy9Pz6/OTm5ExOTPT29BwaHNza3NS6tJRqRGQqBNy6pIyKjDwGBPTe1JSW
   lDQyNOTGrNRiBGwmBIRaLNymdLxWBHxGFNySXCwqLKyqrNR6LKxGBNTS1NTW
   1Jw+BEweDDQ2NAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAao
   QIBwCAgIiEjAgAAoGA6I5DBBUBgWjIZDqnwYGgVIoTGQQgyRiGRCgZCR1nTF
   csFkHm9hBp2paDYbHAsZHW9eERkYGh4eGx4ag3gfSgMTIBshIiMkGyAlCCZT
   EpciJyQjGxcoKUQBEhcbIiorLB4XEltDrhcaLS4vtbcJra8bMDHAGrcyrTMX
   HjA0NSypEsO6EzY3IzU4OdoTzK0BCAkDMgkIOjJlAH5BACH+aENyZWF0ZWQg
   YnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcs
   MTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxj
   b3IuY29tADs=
    }
    pack [button .toolbar.home -image gohome-16 -bg #C0C0EE -relief groove\
      -highlightthickness 0 \
      -command {consoleinterp eval {cd ~; puts [pwd]}}] \
   -side left
    }
 }

RLH 2006-02-21: You almost make me want to get a PocketPC. : )


2006-03-19 HE: I like the concept of sepp. Most times I'm struggling with testing code inside an namespace. Today I tried to create an menuitem to change the namespace where console executes the inserted code. By the way: I create an major menuitem sepp to collect all additional menuitems. my pocketPC is to small for more then 5 major menuitems ;-)

You can start/stop the usage of the namespace selecting with the entry 'on/off'. I think the code below needs some more testing.

 console eval {
        set ::ns ::
        set ::nsOnOff 1
        # ::tk::ConsoleInvoke original comes from tk/console.tcl
        proc ::tk::ConsoleInvoke {args} {
                set ranges [.console tag ranges input]
                set cmd ""
                if {[llength $ranges]} {
                        set pos 0
                        while {[string compare [lindex $ranges $pos] ""]} {
                                set start [lindex $ranges $pos]
                                set end [lindex $ranges [incr pos]]
                                append cmd [.console get $start $end]
                                incr pos
                        }
                }
                if {[string equal $cmd ""]} {
                        ConsolePrompt
                } elseif {[info complete $cmd]} {
                        .console mark set output end
                        .console tag delete input
                        if {$::nsOnOff} {
                                set result [consoleinterp record [list namespace eval $::ns $cmd]]
                                consoleinterp eval {history change [string trimright [lindex [history info 1] 4]] [lindex [history info 1] 0]}
                        } else {
                                set result [consoleinterp record $cmd]
                        }
                        if {[string compare $result ""]} {
                                puts $result
                        }
                        ConsoleHistory reset
                        ConsolePrompt
                } else {
                        ConsolePrompt partial
                }
                .console yview -pickplace insert
        }

        proc ::nsListCon {{s {::}}} {
                set erg {}
                foreach el [lsort [consoleinterp eval "namespace children $s"]] {
                        lappend erg $el
                        if {[set erg1 [::nsListCon $el]] ne {}} {
                                set erg [concat $erg $erg1]
                        }
                }
                return $erg
        }
        proc ::menuNsList {} {
                .menubar.heide.ns delete 0 end
                .menubar.heide.ns add checkbutton -label "on/off" -onvalue 1 -offvalue 0 -variable ::nsOnOff
                .menubar.heide.ns add command -label {::} -command "set ::ns {::}; puts {::}"
                foreach el [::nsListCon] {
                        .menubar.heide.ns add command -label $el -command "set ::ns {$el}; puts {$el}"
                }
        }

        .menubar insert 4 cascade -menu .menubar.heide -label sepp
        menu .menubar.heide -tearoff 0
        .menubar.heide add cascade -menu .menubar.heide.ns -label namespace
        menu .menubar.heide.ns -tearoff 0 -postcommand ::menuNsList
 }

MB 2008-11-20 A small update about extending the eTcl console. On my etcl rc30 for Windows CE, the above scripts does not work because the path of the widgets have changed since 2006. The following startup script works for me. The small "ls" command is a rough and ugly unix-like command for poor windows CE users like me... It was just an attempt, to see if I am able to create a command which is available at the startup of the Etcl console.

    console eval {
      image create photo  gohome-16 -data {
        R0lGODlhEAAQAIUAAPwCBDw6PBQWFCQiJAQCBFxeXMTCxJyanDwyLDQqLFRS
        VLSytJSSlISChCQmJERGRFRWVGxubKSmpJyenGRmZLy+vOzq7OTi5Ly6vGRi
        ZPTy9Pz6/OTm5ExOTPT29BwaHNza3NS6tJRqRGQqBNy6pIyKjDwGBPTe1JSW
        lDQyNOTGrNRiBGwmBIRaLNymdLxWBHxGFNySXCwqLKyqrNR6LKxGBNTS1NTW
        1Jw+BEweDDQ2NAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAao
        QIBwCAgIiEjAgAAoGA6I5DBBUBgWjIZDqnwYGgVIoTGQQgyRiGRCgZCR1nTF
        csFkHm9hBp2paDYbHAsZHW9eERkYGh4eGx4ag3gfSgMTIBshIiMkGyAlCCZT
        EpciJyQjGxcoKUQBEhcbIiorLB4XEltDrhcaLS4vtbcJra8bMDHAGrcyrTMX
        HjA0NSypEsO6EzY3IzU4OdoTzK0BCAkDMgkIOjJlAH5BACH+aENyZWF0ZWQg
        YnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcs
        MTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxj
        b3IuY29tADs=
      }
      set tb .frames.app1.toolbar
      button $tb.home \
        -image gohome-16 \
        -bg #C0C0EE -relief groove\
        -highlightthickness 0 \
        -command runapp
      pack $tb.home -side left
      .frames.app1.menubar.file add command  \
        -label "bla" \
        -command runapp
      proc runapp {} {
        cd ~
        puts [pwd]
      }
    }
    proc ls {{dir .}} {
      foreach d [lsort [glob -type d *]] {
        puts "d:$d"
      }
      foreach f [lsort [glob -type f *]] {
        puts "f:$f"
      }
    }

To get the names of the widgets, I used introspection, based on the following script, which reveal
the hierarchy of widgets used in the console :

    console eval {
      proc children {{win .}} {
        set res {}
        foreach w [winfo children $win] {
          puts "w:$w ([winfo class $w])"
          children $w
        }
        return ""
      }
      children
    }

RS 2013-01-05 Almost seven years later, I'm back to playing with eTcl - my hardware is now a Sony Ericsson Xperia X2 with Win Mobile 6.5 and a tiny physical keyboard - and eTcl runs on it just like it should ;^)

Well, almost. The bottom menu bar is now higher, due to a larger keyboard logo. Introspection shows that the geometry of the console (landscape aspect, when keyboard is slid open) is now 800x391+0+36. With

   console eval {wm geometry . 800x385+0+36}

I reduced the height slightly, and now the toolbar is fully visible. (This has to be repeated whenever the console is freshly displayed).

Next thing to do: the keyboard is eurocentric in that it has Pound Sterling and Euro symbols, but not the Dollar :-( The Evolane guys already provide support for braces and brackets on the toolbar (which equally are not on the keyboard), so I'll add another button on the toolbar to insert a dollar sign - roughly along the line of

 console eval {
      set w .frames.app1
      pack [button $w.toolbar.usd -text $ -command [list $w.content.text insert insert \$]] -side left
 }

... but it's getting very late here - more tomorrow.