Version 16 of A minimal console

Updated 2006-11-06 15:10:45

Richard Suchenwirth -- The following 14-command codelet gives you a window with entry and text widget, where you type commands into the entry, hit Return, and get the results (or error message) of evaluating the entry content in the text widget:

 entry .e -textvar cmd
 bind .e <Key-Return> {go %W}
 text .t -wrap word
 proc go {w} {
    global cmd
    .t insert end "% $cmd\n"
    catch {eval $cmd} res
    .t insert end $res\n
    set cmd ""
 }
 eval pack [winfo children .] -fill both -expand 1
 focus .e

which allows you to source any file and call any Tcl command you might want. Of course, this can be expanded with colors to distinguish stdin/out/err, an entry with a history, menus (see Menus made easy), whatever.


Nov-4-2006 GWM however if you enter into this console's entry:

  set b 1
  set c 2
  expr $b+$c

you do not get 3 as the result but "can't read "b": no such variable", as the commands were all executed internal to proc go, and were destroyed on exit. Change the "eval $cmd" line to:

      catch {uplevel #0 eval $cmd} res

and the expr works (as does puts "B is $b" and so on).


Nov-3-2006 JM Richard, it is probably very simple, but, how can I distinguish between stdin/out/err? ...or, just give me a hint.thanks.

Nov-6-2006 HE This version differs between stdin/stdout/stderr (or better it differs between the emulation of stdin/stdout/stderr).

 entry .e -textvar cmd
 bind .e <Key-Return> {go %W}
 text .t -wrap word
 .t tag configure stdout -foreground blue
 .t tag configure stdin -foreground black
 .t tag configure stderr -foreground red
 proc go {w} {
    global cmd
    .t insert end "% $cmd\n" stdin
    if {[catch {uplevel #0 eval $cmd} res] == 1} {
        .t insert end "$res\n" stderr
    } else {
        .t insert end "$res\n" stdout
    }
    set cmd ""
 }
 eval pack [winfo children .] -fill both -expand 1
 focus .e

But, in all simplicity, this requires the user to know what he's doing. Nothing prevents this code from evaluating "exec format c:"...

Eh? Why would you care if somebody did that? Does the widget make that do something other than this:

 pehrens@marfik ~:>>tclsh
 % exec format c:
 No permission (or no disks found)!
 %

%-)


See console for Unix for Donald Porter's more elaborated console. Tk for Windows has a built-in console that you can have come up just with the command

 console show

See Windows wish console for hints how to extend that.


Jeffrey Hobbs -- On the subject of consoles, if you don't need lightweight, you might want to check out http://tkcon.sourceforge.net/ for a cross-platform console environment with lots of features. There is a megawidget version at http://www.purl.org/net/hobbs/tcl/ .

See the wiki page for Tkcon for more details.


As an alternative how about

 proc execute_command {} {
     set selection [.main tag nextrange sel 0.0]
     if {[llength $selection] > 0} {
         set command [.main get [lindex $selection 0] [lindex $selection 1]]

         .main insert insert [uplevel #0 $command]
         .main see insert
     }
 }

 text .main
 pack .main -side top -fill both -expand true

 # Bind f12 to execute the current selection
 #
 bind .main <KeyPress-F12> execute_command

Since this uses the selection multiple lines can be evaluated making it easy to edit/redefine procedures eg. unknown can be redefined to allow unix commands to be used - simplest way is to edit the results of info body unknown.


This one is almost as short as the examples above, but more intuitively to use:

 package require Tk

 destroy .console
 toplevel .console
 pack [text .console.cmd -wrap word] -expand yes -fill both
 bind .console.cmd <Return> evalConsoleCmd
 bind .console.cmd <Return> +break
 .console.cmd tag configure success -foreground \#008800
 .console.cmd tag configure failure -foreground red

 proc evalConsoleCmd {} {
    set c .console.cmd
    if {[$c compare {insert + 1 lines} < end]} then {
        set l [$c get {insert linestart} {insert lineend}]
        $c insert {end - 1 chars} \n[string trimright $l]
        $c mark set insert end
        $c see insert
    } else {
        if {[catch {
            set result [uplevel 1 [$c get end-1lines end-1chars]]
        } err]} then {
            $c insert end \n {} $err failure \n
        } else {
            $c insert end \n {} $result success \n
        }
    }
 }

Arts and crafts of Tcl-Tk programming