in reply to Tk: Aligning the items in seperate canvases

Without alot of fancy logic, which tests all the positions, you can't perfectly align them with a program. Why? Because of the random placement, some of the canvases will hit the scroll limit and will not be able to moved in one direction(unless you do some trickery like expanding the scrollregion).

Check out the following sub, and test it a few times. Sometimes it will align, as long as there are not any random placements in the high range. Then find the line I've commented out, and uncomment it. That will always bring all 4 into the range, but not perfectly aligned. Like I said, you might be able to expand the scrollregion, so the scrollbar dosn't get locked into the far right position.

Another option which you might try, ( I'm thru fiddling with this :-) ) is to pick a x position on the screen, then scroll all canvases to the 0 position, then in a loop, scroll them 1 unit at a time until the boxe's screen position line up.

Someone else may figure something out, but unless you expand your scrollregion, the scrolled-window sliders will lock you up occaisionally.

sub AlignAll{ my ($x1, $y1, $x2, $y2); my $i; my $location; my %pos; foreach $i (0 .. 3) { ($x1, $y1, $x2, $y2) = $sc[$i]->bbox('rect'); $location =int(($x1+$x2)/2); warn("$location"); $pos{$i} = $location; $sc[$i]->xviewMoveto( $location/$initial_width ); } my @pos; foreach my $key (keys %pos ){ push @pos, $pos{$key}; } print "@pos\n"; my $max = &max(@pos); print "$max\n"; #move them all to the most right one, #since those are already at right scroll limit foreach $i (0 .. 3) { print $i," ",join ' ',$sc[$i]->xview(),"\n"; my $diff = $pos{$i} - $max; ############################################### ############################################### #try it with this line # $sc[$i]->xviewScroll($diff, 'units'); ############################################### } } sub max { my $max; for (@_) { $max = $_ if $_ > $max } return $max; } ########################################

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

Replies are listed 'Best First'.
Re^2: Tk: Aligning the items in seperate canvases
by liverpole (Monsignor) on Nov 03, 2006 at 21:04 UTC
    Sorry, Master,

    I have to disagree with you on this one :-)

    After working on it for a while, I got what seems to be a feasible solution.  It took a little extra work to get it to function for zoom-in and zoom-out, but here's how to do it:

    First, near the top of your program with the other global variable declarations, add two variables, an array @box and a scalar $viewable_width:

    my @box; # Tracks the IDs of each rectangle my $viewable_width=400; # How wide is the viewable Canvas?

    You may also want to use $viewable_width when constructing the Canvas:

    $sc[$i] = $tile[$i]->Scrolled('Canvas', -scrollbars => 'os', -scrollregion => [0, 0, $initial_width, 125], -width => $viewable_width, -height => 125, -background => randColor() )->pack(qw/-fill both -side top -expand 1/);

    Now, when you create a rectangle, be sure to save its ID in the @box array:

    $box[$i] = $sc[$i]->createRectangle($x, 50, $x+20, 90, -fill => randColor(), -outline => 'black', -width => 2, -tags => 'rect', );

    Here's where things get interesting ...  You have to calculate what x-coordinate in the virtual window (whose width is identified by $initial_width) should be the first point to appear in the visual window (identified by $visual_window).

    To calculate this, take the midpoint of the rectangle's coordinates (($x1 + $x2) / 2), and subtract from it one half of the visual window (($x1 + $x2 - $viewable_width) / 2).

    Here's the code which implements the alignment:

    sub AlignAll{ my ($x1, $y1, $x2, $y2); foreach my $i (0 .. 3) { my $sc = $sc[$i]; # Get Canvas objec +t my $box = $box[$i]; # Get box id (rect +angle) my ($x1, $y1, $x2, $y2) = $sc->bbox($box); # Get box coordina +tes my $mid = ($x1 + $x2) / 2; # Get midpoint, mi +nus $mid -= $viewable_width / 2; # 1/2 of visible + window my $newx = $mid / $initial_width; # Get delta x-dist +ance $sc->xviewMoveto($newx); # Move Canvas obje +ct } }

    Note that this will work in the absence of zooming.

    To make it work for zoom-in and zoom-out, you'll need to make one last set of modifications.  In the anonymous subroutines defined for the first 2 buttons, when you calculate scale, you should then adjust $initial_window by that factor:

    $frame->Button( -text => " Zoom Out ", -width => 10, -command => sub { $scale*=0.8; $initial_width *= $scale; foreach $i (0 .. 3) { $sc[$i]->scale(qw/all 0 0 .8 1/); $sc[$i]->configure(-scrollregion => [0, 0, $initial_width, + 125]); } } )->pack(qw/-side left -padx 25/); $frame->Button( -text => " Zoom In ", -width => 10, -command => sub { $scale*=1.25; $initial_width *= $scale; foreach $i (0 .. 3) { $sc[$i]->scale(qw/all 0 0 1.25 1/); $sc[$i]->configure(-scrollregion => [0, 0, $initial_width, + 125]); } } )->pack(qw/-side left -padx 25/);

    Now it should all work the way you want!


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
      Congratulations. :-) I knew there was some sort of hyberbolic relation in there, but I give up easy. :-)

      In my defense, I was close enough for government work, I had

      $sc[$i]->xviewMoveto( ($location /$initial_width ); # where I needed $sc[$i]->xviewMoveto( ($location - 400/2)/$initial_width );
      Of course, your work on zooming gets you an A++.

      This definitely is a snippet to save, since it is a general purpose formula for centering a canvas item in a scrolled window.


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