cmv has asked for the wisdom of the Perl Monks concerning the following question:

Folks-

I have realtime data streaming into my perl script, and I want to keep a sorted listbox of some element of this stream. The attached code shows a snippet of the first thing that jumped into my mind (that works), but I don't like the fact that if the user selects a listbox item, it is lost when the listbox is next updated.

Then I started fiddling with being able to tie the listbox to a perl list variable, but got hopelessly confused (and figured you'd loose the selection in this scenario as well).

Now I'm thinking that I'll have to do a binary-search-and-insert on the listbox, instead of my bull-in-the-china-shop method of removing, sorting, and restoring the whole list.

I feel like I'm missing something obvious... Any thoughts are much appreciated!

Thanks

-Craig

use strict; use Tk; use Data::Dumper; my @data = (qw( ccc eee ddd aaa aaa ccc ddd ddd bbb ddd eee ggg fff)); my %seen; # Create & configure text widget... my $top = MainWindow->new; my $list = $top->Scrolled('Listbox')-> pack(-side=>'right', -fill=>'both', -expand =>1); my $ref; $ref = $top->repeat(700, \&addItem); sub addItem { my $item = shift(@data); if(!$item) { $ref->cancel; return }; print STDERR "$item\n"; if(! $seen{$item}) { $list->delete(0, 'end'); $list->insert('end', sort(keys(%seen))); } $seen{$item}++; print STDERR "SEEN DUMP:\n", Dumper(\%seen), "\n"; } MainLoop();

Replies are listed 'Best First'.
Re: Sorted Realtime Tk Listbox
by Roy Johnson (Monsignor) on Mar 10, 2008 at 17:48 UTC
    You need to take note of which items are selected (by name), then find those names in the new ordered list, and select them.
    use strict; use Tk; use Data::Dumper; my @data = (qw( ccc eee ddd aaa aaa ccc ddd ddd bbb ddd eee ggg fff)); my %seen; # Create & configure text widget... my $top = MainWindow->new; my $list = $top->Scrolled('Listbox')-> pack(-side=>'right', -fill=>'both', -expand =>1); my $ref; $ref = $top->repeat(700, \&addItem); sub addItem { my $item = shift(@data); if(!$item) { $ref->cancel; return }; print STDERR "$item\n"; # Selected items (by name) my @selected_items = map {$list->get($_)} $list->curselection; print "Selected items: @selected_items\n"; if(! $seen{$item}) { my @new_selection_order = sort keys %seen; # Map names to list position my %new_selection = map {($new_selection_order[$_] => $_)} 0.. +$#new_selection_order; $list->delete(0, 'end'); $list->insert('end', @new_selection_order); # Set selection(s) print "New indexes should be @new_selection{@selected_items +}\n"; $list->selectionSet($_) for @new_selection{@selected_items} +; } $seen{$item}++; } MainLoop();

    Caution: Contents may have been coded under pressure.
Re: Sorted Realtime Tk Listbox
by almut (Canon) on Mar 10, 2008 at 18:51 UTC
    I'm thinking that I'll have to do a binary-search-and-insert

    I think you don't need to get too fancy with the inserting algorithm. I simple linear search should scale well up to at least several thousand items in the listbox — and more items isn't particularly good GUI design anyway...  (also, my guess would be that reconstructing the listbox content from scratch for every insert operation isn't necessarily faster (I haven't benchmarked it, though)).

    So, for all practical purposes, something like this might do (but you could of course implement a binary search, too, if you like):

    ... sub addItem { my $item = shift(@data); if(!$item) { $ref->cancel; return }; if(! $seen{$item}) { INSERT: { for my $idx (0 .. $list->size()-1) { if ($list->get($idx) ge $item) { $list->insert($idx, $item); last INSERT; } } $list->insert('end', $item); } } $seen{$item}++; }

    Moreover, not erasing the listbox content every time would also save you from having to readjust the current scroll position.

Re: Sorted Realtime Tk Listbox
by zentara (Cardinal) on Mar 11, 2008 at 12:42 UTC
    Whenever you want to manipulate listbox data, it's probably easiest to use the -listvariable option to store the tied array, then just manipulate the array( add to, then sort the array?). This should show you the idea:
    #!/usr/bin/perl -w use strict; use Tk; my $mw = Tk::MainWindow->new( -title => 'Listbox' ); my @array = ('A'..'Z'); my $lb = $mw->Scrolled('Listbox', -listvariable => \@array, )->pack(); my $b = $mw->Button( -text => 'Delete B', -command => sub{ del_it('B') }, )->pack(); MainLoop; sub del_it { my $remove = shift; @array = grep { $_ ne 'B' } @array; }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum