JOB - 2016-07-19 08:13:11
Purpose: Utility package to store & retrieve user settings. The package requires inifile and fileutil from tcllib.
# ----------------------------------------------------------------------------- # savedefault.tcl --- # ----------------------------------------------------------------------------- # (c) 2016, Johann Oberdorfer - Engineering Support | CAD | Software # johann.oberdorfer [at] gmail.com # www.johann-oberdorfer.eu # ----------------------------------------------------------------------------- # This source file is distributed under the BSD license. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the BSD License for more details. # ----------------------------------------------------------------------------- # Purpose: # Package to store & retrieve user settings. # # ----------------------------------------------------------------------------- package provide savedefault 1.3 namespace eval ::savedefault { # public interface namespace export \ savedefault \ readsettings \ savesettings variable ini variable ini_defaults # ini sections declarations array set ini { DEFAULT_SECTION "Settings" COMMENTS "Comments" INI_FILE "" } proc GetTempDirectory {} { # return the temporary directory set tempDir [fileutil::tempdir] set subDir ".tcl" set cpath [file join $tempDir $subDir] if { ![file isdirectory $cpath] && [catch {file mkdir $cpath}] != 0 } { # no write access ?!..., so we use standard: return $tempDir } return $cpath } proc savedefault {fname ini_list} { # # this function needs to be called the 1st time to set up defaults # arguments: # fname - ini file, where to store the settings # ini_list - a dictionary list with the default ini declarations # variable ini variable ini_defaults # file declaration set ini(INI_FILE) [file join [GetTempDirectory] $fname] # default settings declaration set ini_defaults {} foreach item $ini_list { set name [lindex $item 0] set value [lindex $item 1] lappend ini_defaults [list $name $value] } } proc savesettings {ini_list} { # # save settings to file # ini_list - dictionary list containing {name value} sub-lists # for each setting declaration # variable ini variable ini_defaults # file must at least be there / erase previous file using "w" option set buff [open $ini(INI_FILE) "w"] close $buff set istream [::ini::open $ini(INI_FILE)] # comments: set section $ini(COMMENTS) ::ini::set $istream $section "DATE" \ [clock format [clock seconds] -format "%Y-%m-%d-%H:%M"] ::ini::comment $istream $section "DATE" \ "This file was generated by: savedefault, User: $::tcl_platform(user)" \ "*** DO NOT MODIFY ***" set section $ini(DEFAULT_SECTION) foreach item $ini_list { ::ini::set $istream $section [lindex $item 0] [lindex $item 1] } ::ini::commit $istream ::ini::close $istream return } proc readsettings {ini_array} { # # read settings from the ini file # # ini_array - array handling all values retrieved from the configuration file, # or otherwise use the given default values (ini_defaults) # # Hint: When reading in the option values from the configuration file, # each option is validated against the corresponding default option # if the name is not matching, it is not taken into account # upvar $ini_array arr variable ini variable ini_defaults array set arr {} if {![file exists $ini(INI_FILE)] || ![file readable $ini(INI_FILE)] } { foreach item $ini_defaults { set name [lindex $item 0] set val [lindex $item 1] set arr($name) $val } return } set istream [::ini::open $ini(INI_FILE)] set sections [::ini::sections $istream] set cnt 0 foreach s $sections { if {[string trim $s] == $ini(DEFAULT_SECTION)} { # retrieve all available key values ... # (returns a list of all they key names in the # section and file specified) set keyval_lst [::ini::keys $istream $s] foreach item $ini_defaults { set sec_name [string trim [lindex $item 0]] # puts "--> $keyval_lst :: $sec_name :: [lsearch $keyval_lst $sec_name] <--" if {[set idx [lsearch $keyval_lst $sec_name]] != -1} { set keyval [lindex $keyval_lst $idx] set sec_value [::ini::value $istream $s $keyval] # puts "==> $keyval :: $sec_value" if {$sec_value == ""} { # set default value, just in case the value is empty (?!...) set arr($keyval) [lindex $item end] } else { set arr($keyval) $sec_value } incr cnt } } } } ::ini::close $istream # one more comparison: if {$cnt != [llength $ini_defaults]} { array set arr {} foreach item $ini_defaults { set name [lindex $item 0] set arr($name) [lindex $item 1] } } return } }
Demo Code:
# ----------------------------------------------------------------------------- # savedefault_test.tcl --- # ----------------------------------------------------------------------------- # where to find required tcllib packages: set dir [file dirname [info script]] lappend auto_path [file join $dir "."] lappend auto_path [file join $dir "../tcllib"] package require fileutil package require inifile package require savedefault # test starts here ... package require Tk catch {console show} # initializing default settings set ini_defaults { {"geometry" "950x570+140+170"} {"fontsize" 10} {"paneorient" "vertical"} {"test" ""} {"test1" ""} {"test2" ""} } ::savedefault::savedefault \ "SaveDefault_Test.ini" \ $ini_defaults # overwrite existing array names (if any) # - with the values retrieved from the configuration file # - otherwise use the given default values (ini_defaults) array set this_array {} ::savedefault::readsettings this_array parray this_array puts "------------------------" set ini_list {} lappend ini_list [list "geometry" "800x500"] lappend ini_list [list "fontsize" 12] lappend ini_list [list "paneorient" horizontal] lappend ini_list [list "test" test] lappend ini_list [list "test1" 123] lappend ini_list [list "test2" XYZ] ::savedefault::savesettings $ini_list ::savedefault::readsettings this_array parray this_array