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

Hello,
I'd like to display an overwiew of the entries of a database table with perl tk.

The overview should:

* be sorted by clicking on one of the columnnames according to this columns entries. It would be very good if clicking once again on the same columnname would reverse the sorting order.
* have rows with different backgroundcolors (or different foregroundcolors) according to some criteria e.g. days of the week.

As far as I know it is not possible to sort with listbox nor is it possible to have different colors for the rows.
Sorting seems to function with Hlist but I found nothing for the different colored rows.

Please give me some hint how to solve the sorting and the rows with differnt colors.

PS: Alternatively I could imagine using an additional column where a traffic light picture is inserted (red, yellow, green circle). But I have no idea how to do this.

Replies are listed 'Best First'.
Re: list overview sorted and colored rows
by liverpole (Monsignor) on Sep 22, 2007 at 15:21 UTC
    Hi hudo,

    I've had a lot of good luck using the Text and ROText widgets for this kind of thing.  Both of your requirements, sorting and displaying in different colors, are quite easy using either widget (but should be easy to do anyway, regardless of which widget you choose).

    Here's a short example I've written to show you how to to sort items and display them in a ROText widget:

    use strict; use warnings; use Tk; use Tk::ROText; # User-defined my $textfont = "Helvetica 12"; my @colors = qw( peachpuff lightyellow ); my @items = ( 'blue', 'orange', 'white', 'red', 'gray', 'yellow', 'purple', 'tan', 'pink', 'green', 'black', ); # Globals my @all_items = ( ); my $item_count = 0; # Main program my $mw = new MainWindow; my $f1 = $mw->Frame()->pack(-expand => 0, -fill => 'x'); my $f2 = $mw->Frame()->pack(-expand => 1, -fill => 'both'); my $ps = sub { $mw->destroy() }; my $b1 = button($f1, 'Exit', 'right', $ps, "Escape"); my $b2 = button($f1, 'Clear', 'left', sub { clear_all() }); my $b3 = button($f1, 'Insert', 'left', sub { insert_all() }); my $txt = $f2->Scrolled('ROText', -scrollbars => 'osoe')->pack(); insert_all(); MainLoop; # Subroutines sub button { my ($w, $text, $side, $psub, $bind) = @_; $bind ||= 0; $bind and $text .= " ($bind)"; my $btn = $w->Button(-bg => 'skyblue', -text => $text); if ($psub || 0) { $btn->configure(-command => $psub); $bind and $mw->bind("<$bind>" => sub { $btn->invoke() }); } $btn->pack(-side => $side); return $btn; } sub clear_all { # Delete all items in the Text widget @all_items = ( ); $txt->delete("1.0", "end"); $item_count = 0; } sub insert_all { # Delete the contents of the Text widget first $txt->delete("1.0", "end"); $item_count = 0; # Sort the items, adding each to the Text widget push @all_items, @items; my @sorted = sort @all_items; foreach (@sorted) { insert_item($_); } } sub insert_item { my ($text) = @_; my $tag = "item_" . ++$item_count; my $bg = $colors[$item_count % @colors]; $txt->tagConfigure($tag, -font => $textfont, -background => $bg); $txt->insert("end", "$text\n", $tag); }

    The trick is that, each time you click on the "Insert" button, it clears the ROText widget first, then adds the list of colors into the full list @all_items, sorts the full list, and displays it.

    The multiple colors are achieved by keeping track of how many items have been added to the list (with $item_count), and displaying one or the other of the colors in @colors.

    Hopefully, the above example will give you some ideas about how to get started.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: list overview sorted and colored rows
by liverpole (Monsignor) on Sep 22, 2007 at 21:10 UTC
    To give you another example, the following simple Perl/Tk program demonstrates a ROText widget containing information about 20 famous companies (name, address and homepage), with column headings which can be clicked on to sort by the data in that column.

    I'm working on a project of my own, which will ultimately have a very similar forward-sort/reverse-sort capability:

    #!/usr/bin/perl -w # Strict use strict; use warnings; # Libraries use Tk; use Tk::ROText; # User-defined my $linux_browser = 'firefox'; my $win32_browser = 'C:\\PROGRA~1\\MOZILL~1\\FIREFOX.EXE'; my $headerfont = "Courier 10 bold"; my $textfont = "Courier 10"; my $urlfont = "Courier 10 italic"; my @colors = qw( peachpuff lightyellow ); my @columns = qw( Company Address Homepage ); my $p_col_widths = { 'Company' => 20, 'Address' => 20, 'Homepage' => + 28 }; my $pdata = [ ['General Electric', 'Fairfield, CT', 'www.ge.com' + ], ['FedEx', 'Memphis, TN', 'www.fedex.com' + ], ['Southwest Airlines', 'Dallas, TX', 'www.southwest.com' + ], ['Procter & Gamble', 'Cincinnati, OH', 'www.pg.com' + ], ['Starbucks', 'Seattle, WA', 'www.starbucks.com' + ], ['Johnson & Johnson', 'New Brunswick, NJ', 'www.jnj.com' + ], ['Berkshire Hathaway', 'Omaha, NE', 'www.berkshirehathaway +.com'], ['Dell', 'Round Rock, TX', 'www.dell.com' + ], ['Toyota', 'Toyota, Japan', 'www.toyota.co.jp' + ], ['Microsoft', 'Redmond, WA', 'www.microsoft.com' + ], ['Apple Computer', 'Cupertino, CA', 'www.apple.com' + ], ['Wal-Mart', 'Bentonville, AR', 'www.walmartstores.com +' ], ['UPS', 'Atlanta, GA', 'www.ups.com' + ], ['Home Depot', 'Atlanta, GA', 'www.homedepot.com' + ], ['PepsiCo', 'Purchase, NY', 'www.pepsico.com' + ], ['Costco', 'Issaquah, WA', 'www.costco.com' + ], ['American Express', 'New York, NY', 'www.americanexpress.c +om' ], ['Goldman Sachs', 'New York, NY', 'www.gs.com' + ], ['IBM', 'Armonk, NY', 'www.ibm.com' + ], ['3M', 'St. Paul, MN', 'www.3m.com' + ], ]; # Main program # Create the GUI my $mw = new MainWindow; my $f1 = $mw->Frame()->pack(-expand => 0, -fill => 'x'); my $f2 = $mw->Frame()->pack(-expand => 1, -fill => 'both'); button($f1, 'Exit', 'right', sub { $mw->destroy() }, "Escape"); my $txt = $f2->Scrolled('ROText', -scrollbars => 'osoe'); $txt->configure(-width => 90, -height => 32, -selectforeground => 'bla +ck'); $txt->pack(-expand => 1, -fill => 'both'); sort_by_column(1, 0); MainLoop; # Subroutines sub button { my ($w, $text, $side, $psub, $bind) = @_; ($bind || 0) and $text .= " ($bind)"; my $btn = $w->Button(-bg => 'skyblue', -text => $text); if ($psub || 0) { $btn->configure(-command => $psub); ($bind || 0) and $mw->bind("<$bind>" => sub { $btn->invoke() } +); } $btn->pack(-side => $side); return $btn; } sub sort_by_column { my ($idx, $order) = @_; # Delete all items in the Text widget $txt->delete("1.0", "end"); my $item_count = 0; # Display each column of the header for (my $i = 1; $i <= @columns; $i++) { my $text = my $column = $columns[$i - 1]; my $b_this_col = ($idx == $i)? 1: 0; $b_this_col and $text .= (0 == $order)? " (+)": " (-)"; my $width = $p_col_widths->{$column}; my $line = sprintf "| %-${width}.${width}s", $text; my $tag = "column_$column"; ($i == @columns) and $line .= "\n"; my $new_idx = $i; my $psub = sub { my $new_order = $b_this_col? 1 - $order: 0; sort_by_column($new_idx, $new_order); }; insert_item($line, '#ff7fef', $tag, $headerfont, $psub); } # Sort all data by the given field index my @sorted = $order? sort { $b->[$idx-1] cmp $a->[$idx-1] } @$pdat +a: sort { $a->[$idx-1] cmp $b->[$idx-1] } @$pdat +a; # Display each line of the data for (my $i = 0; $i < @sorted; $i++) { my $pcompany = $sorted[$i]; ++$item_count; my $color = $colors[$item_count % @colors]; for (my $j = 1; $j <= @columns; $j++) { my $column = $columns[$j-1]; my $data = $pcompany->[$j-1]; my $width = $p_col_widths->{$column}; my $line = sprintf "| %-${width}.${width}s", $data; ($j == @columns) and $line .= "\n"; my $tag = "item_${item_count}_$j"; if ($column eq 'Homepage') { my $psub = sub { launch_url($data) }; insert_item($line, $color, $tag, $urlfont, $psub); } else { insert_item($line, $color, $tag, $textfont); } } } } sub insert_item { my ($text, $bg, $tag, $font, $psub) = @_; $txt->tagConfigure($tag, -font => $font, -background => $bg); ($psub || 0) and make_text_hot($tag, $psub); $txt->insert("end", $text, $tag); } sub make_text_hot { my ($tag, $psub) = @_; my $p_enter_sub = sub { $txt->configure(-cursor => 'hand2') }; my $p_leave_sub = sub { $txt->configure(-cursor => 'xterm') }; $txt->tagBind($tag, "<Button-1>", $psub); $txt->tagBind($tag, "<Any-Enter>", $p_enter_sub); $txt->tagBind($tag, "<Any-Leave>", $p_leave_sub); } sub launch_url { my ($url) = @_; if ($^O =~ /win32/i) { # Startup the preferred Windows browser system("start $win32_browser \"$url\""); } else { # Child process runs the preferred Linux browser, parent retur +ns fork or exec "$linux_browser '$url'"; } }

    The column which is sorted by changes its text to include either "(+)" for a forward sort, or "(-)" for a reverse sort.  Furthermore, any URL in the homepage address column can be clicked on to bring that page up in a browser (the browsers are set according to the variables $linux_browser and $win32_browser).


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/