Version 4 of envsave.tcl

Updated 2006-02-23 23:20:57

Shin The Gin This is my first attempt to save a running TclTk environment, to be sourced later.

Shin, you should be more careful about generating tcl code. For instance, this line of code is asking for trouble:

    set ln "set $g \{$v\}"

The first time you have a global variable with a space or special character, or a value with unbalanced braces, you're going to have a problem. Since tcl statements are lists of words, use list and all those problems will vanish in a puff of smoke:

    set ln [list set $g $v]

 catch {package require Tk}


 # GLOBALS
 #
 proc codeForGlobals {} {
          set result ""
         foreach g [info globals] {
                 set isArray [array exists ::$g]
                 if {$isArray==1} {
                         set ln ""
                         set keys [array names ::$g]
                         foreach k $keys {
                                 append ln "set $g"
                                 append ln "("
                                 append ln $k
                                 append ln ") "
                                 append ln "{[lindex [array get ::$g $k] 1]}"
                                 append ln "\n"
                         }
                 } else {
                         upvar ::$g v
                         if {[string length "$v"]>0} {
                                 set ln "set $g \{$v\}"
                         }
                 }

                 append result "$ln\n"
         }
         return $result
 }


 # PROCEDURES
 #
 proc codeForProc {p} {
         set args [info args $p]
         set body [info body $p]
         return "
 proc $p {$args} {
 $body
 }
 "
 }

 proc codeForAllProcs {} {
         set result ""
         foreach p [info proc] {
                 append result "[codeForProc $p]\n\n"
         }
         return $result
 }


 # WIDGETS
 #
 proc traverseWidgetTree {{win .}} {
         set result "$win "
         foreach w [winfo children $win] {
                 append result "[traverseWidgetTree $w] "
         }
         return $result
 }

 proc codeForWidget {{win .}} {
         set class [winfo class $win]
         set class [string replace $class 0 0 [string tolower [string index $class 0]]]
         set cfg [$win configure]
         set result ""
         if {"$win"!="."} {
                 set result "$class $win\n"
         }
         foreach o {aspect attributes client colormapwindows focusmodel geometry iconbitmap iconmask iconname maxsize minsize overrideredirect positionfrom resizable sizefrom state title} {
                 if {[string length [wm $o $win]] > 0} {
                         if {"$o"=="title"} {
                                 append result "wm $o $win \"[wm $o $win]\"\n"
                         } else {
                                 append result "wm $o $win [wm $o $win]\n"
                         }
                 }
         }
         return $result        
 }

 proc codeForWidgetTree {{win .}} {
         set result ""
         foreach w [traverseWidgetTree $win] {
                 append result [codeForWidget $w]
         }
         return $result
 }

 proc environmentCode {} {
         codeForGlobals
         codeForAllProcs
         codeForWidgetTree .
 }

Calling environmentCode gives You a string containing the loadable tcl code to be saved or evaluated in another interp, which would clone the current environment.

In a next step, I'd try to regard namespaces so one could build an application interactively in a namespace and save it to a file of tcl code. A Namespace Browser would be nice here...