Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Efficiently changing colours of Tk::Listbox items

187 views
Skip to first unread message

Stephen

unread,
Dec 3, 2006, 6:09:52 AM12/3/06
to
Hello,

I have an application that is using a Tk::Listbox to display the status
of some remote devices. I would like to change the background colours
of list items to indicate various states however I have found that
using itemconfigure to set the colour of each item takes a very long
time.

Using the example code at the end of this message reveals that changing
the colour of 500 items of a 1000 item list takes 4.815s on my laptop.
Changing the colour of 1000 items of a 2000 item list takes 19.649s so
the length of the list dramatically affects the amount of time to set
the colour of an individual item.

Does anyone know why it is so slow? Is there a more efficient way of
doing this?

I suspect that itemconfigure is doing something nasty like redrawing
the list each time I modify an item.

I would be very grateful for any advice that anyone could offer.

Kind regards,

Stephen


#!/usr/bin/perl -w

use strict;
use Time::HiRes qw(gettimeofday);
use Tk;

my @list_data = (1..1000);

my $mw = new MainWindow;
my $list = $mw->Listbox(-listvariable => \@list_data)->pack;

# Set colour of every even numbered item in the list

my $start_time = gettimeofday;

for(my $i = 0 ; $i < @list_data ; $i += 2)
{
$list->itemconfigure($i, -background => "green");
}

# Print elapsed time for changing colours

printf("Elapsed: %.03fs\n", gettimeofday - $start_time);

MainLoop;

# EOF

zentara

unread,
Dec 3, 2006, 9:34:34 AM12/3/06
to
On 3 Dec 2006 03:09:52 -0800, "Stephen" <steph...@hotmail.com> wrote:


>Does anyone know why it is so slow? Is there a more efficient way of
>doing this?

>I suspect that itemconfigure is doing something nasty like redrawing
>the list each time I modify an item.

>I would be very grateful for any advice that anyone could offer.

>Stephen

I get the same results as you. Maybe you should use a faster widget?
This HList example works fast.

#!/usr/bin/perl
use warnings;
use strict;
use Tk;
use Tk::HList;
use Tk::ItemStyle;

my $mw = MainWindow->new();

my $green = $mw->ItemStyle(
'text',
-stylename => 'green',
-bg => 'green',
-fg => 'black'
);

my $hlist = $mw->HList(
-itemtype => 'text',
-selectmode => 'single',
)->pack;

for (1..1000) {
$hlist->add($_,
-text=>$_);
}

for (my $i = 1 ; $i < 1000 ; $i += 2){
$hlist->entryconfigure( $i, -style => 'green' );
}

MainLoop;
__END__


--
I'm not really a human, but I play one on earth.
http://zentara.net/japh.html

Stephen

unread,
Dec 3, 2006, 10:31:14 AM12/3/06
to
Hello Zentara,

Thank you very much for the advice. Indeed, HList seems to be much
faster and I think I will switch to using it. Do you know whether it
is possible to make the background colour fill the entire width of the
row? It seems to be just a little wider than the entry text by
default.

Regards,

Stephen

Slaven Rezic

unread,
Dec 3, 2006, 11:49:01 AM12/3/06
to
"Stephen" <steph...@hotmail.com> writes:

> Hello,
>
> I have an application that is using a Tk::Listbox to display the status
> of some remote devices. I would like to change the background colours
> of list items to indicate various states however I have found that
> using itemconfigure to set the colour of each item takes a very long
> time.
>
> Using the example code at the end of this message reveals that changing
> the colour of 500 items of a 1000 item list takes 4.815s on my laptop.
> Changing the colour of 1000 items of a 2000 item list takes 19.649s so
> the length of the list dramatically affects the amount of time to set
> the colour of an individual item.
>
> Does anyone know why it is so slow? Is there a more efficient way of
> doing this?
>
> I suspect that itemconfigure is doing something nasty like redrawing
> the list each time I modify an item.
>
> I would be very grateful for any advice that anyone could offer.
>

Here's your script rewritten to show the increased computation time
depending on the count of listbox elements.

#!/usr/bin/perl -w

use strict;
use Time::HiRes qw(gettimeofday);
use Tk;

for my $max (100,500,1000,2000,5000,10000) {
my @list_data = (1..$max);

my $mw = new MainWindow;

my $list = $mw->Listbox->pack; # (-listvariable => \@list_data)->pack;
$list->insert("end", @list_data);

# Set colour of every even numbered item in the list

my $start_time = gettimeofday;

for (my $i = 0 ; $i < @list_data ; $i += 2) {


$list->itemconfigure($i, -background => "green");
}

# Print elapsed time for changing colours

printf("Elapsed for $max: %.03fs %.05fs\n", gettimeofday - $start_time, (gettimeofday - $start_time)/$max);
$mw->update;
$mw->after(1000);
$mw->destroy;
}
__END__

On my machine the output is (my machine seems to be faster, or maybe
it's just a different display system --- X11 vs. Windows?):

Elapsed for 100: 0.003s 0.00003s
Elapsed for 500: 0.083s 0.00017s
Elapsed for 1000: 0.345s 0.00034s
Elapsed for 2000: 1.457s 0.00073s
Elapsed for 5000: 9.537s 0.00191s
Elapsed for 10000: 40.050s 0.00400s

The same problem exists in Tcl/Tk, though Tcl is generally somewhat
faster than perl:

foreach max {100 500 1000 2000 5000 10000} {
set list_data {}
for {set i 0} {$i < $max} {incr i} {
lappend list_data $i
}

pack [listbox .lb -listvar list_data]

puts [time {
for {set i 0} {$i < $max} {set i [expr {$i + 2}]} {
.lb itemconfigure $i -background green
}
}]
destroy .lb
}

The output is, using the FreeBSD port of tk 8.4.11:

1937 microseconds per iteration
46152 microseconds per iteration
211750 microseconds per iteration
802445 microseconds per iteration
5312198 microseconds per iteration
21630483 microseconds per iteration

It seems that the problem occurs because of very frequent calls of the
internal function ListboxWorldChanged() for every itemconfigure call,
which seems to be very costly and recalculates the whole listbox
context. If this call is replaced by EventuallyRedrawRange() then the
results look much better:

Elapsed for 100: 0.001s 0.00001s
Elapsed for 500: 0.002s 0.00000s
Elapsed for 1000: 0.005s 0.00001s
Elapsed for 2000: 0.010s 0.00000s
Elapsed for 5000: 0.024s 0.00000s
Elapsed for 10000: 0.048s 0.00000s

Here's the patch against Tk804.027-p4:

--- tkListbox.c (revision 1333)
+++ tkListbox.c (revision 1334)
@@ -390,7 +390,7 @@ static int ConfigureListbox _ANSI_ARGS_
int flags));
static int ConfigureListboxItem _ANSI_ARGS_ ((Tcl_Interp *interp,
Listbox *listPtr, ItemAttr *attrs, int objc,
- Tcl_Obj *CONST objv[]));
+ Tcl_Obj *CONST objv[], int index));
static int ListboxDeleteSubCmd _ANSI_ARGS_((Listbox *listPtr,
int first, int last));
static void DestroyListbox _ANSI_ARGS_((char *memPtr));
@@ -942,7 +942,7 @@ ListboxWidgetObjCmd(clientData, interp,
}
} else {
result = ConfigureListboxItem(interp, listPtr, attrPtr,
- objc-3, objv+3);
+ objc-3, objv+3, index);
}
break;
}
@@ -1696,13 +1696,14 @@ ConfigureListbox(interp, listPtr, objc,
*/

static int
-ConfigureListboxItem(interp, listPtr, attrs, objc, objv)
+ConfigureListboxItem(interp, listPtr, attrs, objc, objv, index)
Tcl_Interp *interp; /* Used for error reporting. */
register Listbox *listPtr; /* Information about widget; may or may
* not already have values for some fields. */
ItemAttr *attrs; /* Information about the item to configure */
int objc; /* Number of valid entries in argv. */
Tcl_Obj *CONST objv[]; /* Arguments. */
+ int index; /* Index of the listbox item for EventuallyRedrawRange */
{
Tk_SavedOptions savedOptions;

@@ -1713,7 +1714,8 @@ ConfigureListboxItem(interp, listPtr, at
return TCL_ERROR;
}
Tk_FreeSavedOptions(&savedOptions);
- ListboxWorldChanged((ClientData) listPtr);
+ /* ListboxWorldChanged((ClientData) listPtr); -- too slow if a lot of items are in the listbox! */
+ EventuallyRedrawRange(listPtr, index, index); /* this is much faster */
return TCL_OK;
}

Zentara already pointed out to Tk::HList as an alternative. Another
alternative could be Tk::TextList, which comes with standard Tk804 and
is a Listbox implementation based on Tk::Text. But Tk::TextList
implementation is still lacking many Tk::Listbox features and also
seems to be buggy.

Regards,
Slaven

--
Slaven Rezic - slaven <at> rezic <dot> de

tksm - Perl/Tk program for searching and replacing in multiple files
http://ptktools.sourceforge.net/#tksm

Stephen

unread,
Dec 3, 2006, 12:12:32 PM12/3/06
to
Hello Slaven and Zentara,

Thank you very much for your help. I will go with HList for now as it
seems to perform much better that Listbox and having played with it a
little it seems well suited to my needs.

I suspected that there was some operation being applied to the entire
list on each itemconfigure just as you confirmed Slaven.

Thank you again for your assistance. I expect your suggestions to make
a considerable difference to my application.

Regards,

Stephen

zentara

unread,
Dec 4, 2006, 6:28:50 AM12/4/06
to

>Stephen

Yes, set the column width

$hlist->columnWidth(0, -char, 90); # or some big number

HList is pretty powerful, but there are some tricky aspects to it.
You can go to groups.google.com and type in "perl tk hlist"
to find all the snippets that have been discussed here before.

Don't forget, "perldoc Tk::HList"

zentara

0 new messages