Overview
| Artifact ID: | 7b3a64cf6a2d532aa644bfe4488f85d33c60ad15 |
|---|---|
| Ticket: | 458446ffffffffffffffffffffffffffffffffff |
| Date: | 2001-09-04 23:56:26 |
| User: | bach |
| Artifact Attached: | 5ecb5eaafa14b092c4a9cbf11b21ac6bd344e42b |
| Filename: | lb_patch.txt |
| Description: | the patch as described for widget.tcl and listbox.tcl |
Content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182Motivation: 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 ([email protected]) # 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 ([email protected]) # # 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) } }
