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


Hello people,

I'm facing a problem with perl and Tk.
I'm trying to code an explorer like interface for manipulating
my bookmark collection

I've submitted a part of my code (readable I hope!)
After a particular folder is double-clicked, I display the corresponding child folders.
Initially, I disply the root folder which has a hover effect using the tagBind method.
On Double-clicking the root folder, dclick is invoked. I've hard-coded the child folders right now.
For each child folder printed, I create a tag of the same name,
and to that tag I associate an event using the tagBind method

The problem I'm facing is that when the child folders are displayed, the hover effect is getting
applied for only one link, irrespective of which child link I move the mouse over.
And it's driving me absolutely crazy.
I've been with stuck with this, for more that a month now!

The code below should run on any computer having perl and Tk.
And i would seriously appreciate any help I can get!

#!/usr/bin/perl -w use strict; use Tk; use Tk::ROText; my $mw; # Main Window my $fview; # Text box (Read only) # Functions sub hover($$); sub dclick($); # Create Main Window and textbox $mw = new MainWindow; $fview = $mw->Scrolled( 'ROText', -scrollbars => "oe", -cursor => "hand2" ); $fview->pack; # Print "Root" Folder $fview->insert('end', "\n"); $fview->insert('end', "Root" , "Root"); $fview->insert("Root.last", "\n", "Root_newline"); # Hover over $fview->tagBind("Root", "<Enter>" => sub { hover("Root", 1); }); # Hover out $fview->tagBind("Root", "<Leave>" => sub { hover("Root", 0); }); # Double click $fview->tagBind("Root", "<Double-Button-1>" => sub { dclick("Root"); }); # Wait For Christmas! MainLoop; # make folder hot! sub hover($$) { my $tag = shift; my $do = shift; if ($do == 1) { $fview->tagConfigure($tag, -foreground => "red" ); } elsif ($do == 0) { $fview->tagConfigure($tag, -foreground => "black" ); } } # expand folder sub dclick($) { my $tag = shift; my @folders; my $fullpath; @folders = ( "Linux", "Mail", "Perl" ); foreach my $folder (@folders) { $fullpath = "/$folder"; # Hover over $fview->tagBind($fullpath, "<Enter>" => sub { hover($fullpath, 1); }); # Hover out $fview->tagBind($fullpath, "<Leave>" => sub { hover($fullpath, 0); }); # Diplay the folder link $fview->insert( 'Root_newline.last', $folder, $fullpath ); $fview->insert( $fullpath.".last", "\n", $fullpath."_newline" ); } }

Thanx!

--
arc_of_descent

edited: Sun Jan 19 00:51:36 2003 by jeffa - major edit, removed HTML markup inside code

Replies are listed 'Best First'.
Re: Tk tagBind problem
by castaway (Parson) on Jan 18, 2003 at 18:14 UTC
    Change
    $fullpath = "/$folder";
    to
    my $fullpath ="/$folder";
    then it works.. Although I can't figure out why it's always doing the hover function with the last value you used..
    C.
      It has to do with that thing that I have a hard time describing in my post (see below yours). Since these callbacks are generated within a callback, I don't believe the anonymous subs are eval'ed until the dbclick callback is complete. Since the $fullpath is global to the sub all three callbacks end up with the same value for $fullpath when they are created.

      This is why I think using anonymous subs for callbacks is a bad thing - especially when created dynamically like this. Too much potential for action at a distance. I prefer the nice, predictable method of passing an anonymous array to the callback mechanism and letting Tk deal with that. Besides, the fact that the callback then passes the parent widget's ref to the subroutine with this method is immensly useful sometimes. (For example using dynamic callbacks with multiple canvas methods, each of which could use the same callback sub and potential share the same tag name.)

      {NULE}
      --
      http://www.nule.org

Re: Tk tagBind problem
by {NULE} (Hermit) on Jan 18, 2003 at 18:19 UTC
    Hi,

    The problem is with the way callbacks work in TK. They like to receive an anonymous array with the first element being the sub to be called and subsequent elements are passed to that sub. Here is your code, modified to work.

    #!/usr/bin/perl -w use strict; use Tk; use Tk::ROText; my $mw; # Main Window my $fview; # Text box (Read only) # Create Main Window and textbox $mw = new MainWindow; $fview = $mw->Scrolled( 'ROText', -scrollbars => "oe", -cursor => "hand2" ); $fview->pack; # Print "Root" Folder $fview->insert('end', "\n"); $fview->insert('end', "Root" , "Root"); $fview->insert("Root.last", "\n", "Root_newline"); # Hover over $fview->tagBind("Root", "<Enter>" => [ \&hover, "Root", 1 ]); # Hover out $fview->tagBind("Root", "<Leave>" => [ \&hover, "Root", 0 ]); # Double click $fview->tagBind("Root", "<Double-Button-1>" => [ \&dclick, "Root", 0 ]); # Wait For Christmas! MainLoop; # make folder hot! sub hover { shift; my $tag = shift; my $do = shift; if ($do == 1) { $fview->tagConfigure($tag, -foreground => "red" ); } elsif ($do == 0) { $fview->tagConfigure($tag, -foreground => "black" ); } } # expand folder sub dclick { shift; my $tag = shift; my @folders; my $fullpath; @folders = ( "Linux", "Mail", "Perl" ); foreach my $folder (@folders) { $fullpath = "/$folder"; # Hover over $fview->tagBind($fullpath, "<Enter>" => [ \&hover, $fullpath, 1 ]); # Hover out $fview->tagBind($fullpath, "<Leave>" => [ \&hover, $fullpath, 0 ]); # Diplay the folder link $fview->insert( 'Root_newline.last', $folder, $fullpath ); $fview->insert( $fullpath.".last", "\n", $fullpath."_newline" ); } }
    I got ride of the sub prototypes ('cause I don't like them. :) ) and change the callbacks to the way that TK expects them. Note that I have to shift the first element off because it passes the ref of the widget performing the callback.

    I conceptually understand why this behaves this way, but words fail me when I try to explain it. Hopefully one of our fellow monks will step up to the plate here. To experiment with callbacks I suggest you do a callback to a sub in this manner (Tk::Canvas style shown here) and have that sub just do this:

    $w->bind("tag", '<1>', [ \&bind, "one", "two" ]); sub stuff { my @a = @_; my $i = 0; print "Callback received this stuff:\n"; for (@a) { print "\targ($i) : $_\n"; $i++ } }
    Hope this helps - sorry if it's not terribly clear,

    {NULE}
    --
    http://www.nule.org

Re: Tk tagBind problem
by arc_of_descent (Hermit) on Jan 18, 2003 at 18:38 UTC


    Well....

    I think I get the general Idea here

    Although, not completely, it will suffice enough to let me
    continue with my project

    Thank You fellow monks!

    --
    arc_of_descent