Tk Library Source Code

Attachment Details
Login
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
   182

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 ([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)
    }
}