Motivation: The BWidget ListBox insert command isn't a speed demon. This is not too noticable for small lists, but it gets rather slow for big ones E.g. inserting 500 entries with just -text and -image attributes set takes approx. 1 second on a Pentium III 550MHz. Problem analysis: The bulk of the time in ListBox::insert gets eaten by a call to Widget::init. In Widget::init, most of the time is used by initializing and parsing from standard options database. The example from above uses ~1250msec for building up the std. options and ~250msec to apply specific options for the item. Proposed patch: Two functions to speed up inserts of many items that are all almost equal. First, Widget::copyinit. This works almost like the normal init, but saves time by copying standard option initialization from a given template. It then applies specific item options (just like init). Second, ListBox::multipleinsert. This also works like its pendant ListBox::insert, but knows about the aforementioned Widget::copyinit function. I shortly thought of changing the original 'insert', but the effects of a change there would not be backward compatible. Sideeffects: None to existing code. A widget function more to document for the user (ListBox::multipleinsert). Results: The speedup of using multipleinsert is a factor of 2 when used almost exactly like a multiple call to insert. That is: set l foreach n {name1 name2 name3 ... namen} { lappend l $n "-text $n -image something.gif" } $list multipleinsert end $l is 2 times faster than its pendant foreach n {name1 name2 name3 ... namen} { $list insert end $n -text $n -image something.gif } Assuming that, e.g. all images used are the same (as in the example above, the speedup is factor 3 by using: set l foreach n {name1 name2 name3 ... namen} { if {[llength $l] == 0} { lappend l $n "-text $n -image something.gif" } else { lappend l $n "-text $n" } $list multipleinsert end $l Code: In listbox.tcl: # Bastien Chevreux (bach@mwgdna.com) # The multipleinsert command performs inserts several items at once into # the list. It is faster than calling insert multiple times as it uses the # Widget::copyinit command for initializing all items after the 1st. The # speedup factor is between 2 and 3 for typical usage, but could be higher # for inserts with many options. # # Syntax: path and index are as in the insert command # args is a list of even numbered elements where the 1st of each pair # corresponds to the item of 'insert' and the second to args of 'insert'. # ------------------------------------------------------------------------------ # Command ListBox::multipleinsert # ------------------------------------------------------------------------------ proc ListBox::multipleinsert { path index args } { variable $path upvar 0 $path data # If we got only one list as arg, take the first element as args # This enables callers to use # $list multipleinsert index $thelist # instead of # eval $list multipleinsert index $thelist if {[llength $args] == 1} { set args [lindex $args 0] } set count 0 foreach {item iargs} $args { if { [lsearch $data(items) $item] != -1 } { return -code error "item \"$item\" already exists" } if {$count==0} { Widget::init ListBox::Item $path.$item $iargs set firstpath $path.$item } else { Widget::copyinit ListBox::Item $firstpath $path.$item $iargs } if { ![string compare $index "end"] } { eval lappend data(items) $item } else { set data(items) [linsert $data(items) $index $item] } set data(upd,create,$item) $item incr count } _redraw_idle $path 2 return $item } In widget.tcl: # Bastien Chevreux (bach@mwgdna.com) # # copyinit performs basically the same job as init, but it uses a # existing template to initialize its values. So, first a perferct copy # from the template is made just to be altered by any existing options # afterwards. # But this still saves time as the first initialization parsing block is # skipped. # As additional bonus, items that differ in just a few options can be # initialized faster by leaving out the options that are equal. # This function is currently used only by ListBox::multipleinsert, but other # calls should follow :) # ------------------------------------------------------------------------------ # Command Widget::copyinit # ------------------------------------------------------------------------------ proc Widget::copyinit { class templatepath path options } { upvar 0 ${class}::opt classopt upvar 0 ${class}::$path:opt pathopt upvar 0 ${class}::$path:mod pathmod upvar 0 ${class}::$path:init pathinit upvar 0 ${class}::$templatepath:opt templatepathopt upvar 0 ${class}::$templatepath:mod templatepathmod upvar 0 ${class}::$templatepath:init templatepathinit if { [info exists pathopt] } { unset pathopt } if { [info exists pathmod] } { unset pathmod } # We use the template widget for option db copying, but it has to exist! array set pathmod [array get templatepathmod] array set pathopt [array get templatepathopt] array set pathinit [array get templatepathinit] set Widget::_class($path) $class foreach {option value} $options { if { ![info exists classopt($option)] } { unset pathopt unset pathmod return -code error "unknown option \"$option\"" } set optdesc $classopt($option) set type [lindex $optdesc 0] if { ![string compare $type "Synonym"] } { set option [lindex $optdesc 1] set optdesc $classopt($option) set type [lindex $optdesc 0] } set pathopt($option) [$Widget::_optiontype($type) $option $value [lindex $optdesc 3]] set pathinit($option) $pathopt($option) } }