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/stdout/stderr? ...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 e.g., 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 intuitive 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 } } }