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

I have aquired the book "Mastering Perl -Tk" and started to learning tk, by small examples.
Now there are some mysteries that I have no explanation for, maybee you could help me?

Tk Mystery 1.
I have a canvas which is 500 x 500. The visible region is 200 x 200.
#!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; my $c = $mw->Scrolled('Canvas', -width => 200, -height => 200, -background =>'blue', -scrollregion => [ 0, 0, 500, 500 ] ); $c->pack(-expand => -2, -fill => 'both'); $c->createRectangle(100, 100, 150, 150, -fill => 'yellow'); my $plus = $c->Button( -text => ' + ', -command => sub {$c->scale("all", 0, 0, 2, 2); } ); $plus->pack; MainLoop;
If the button just before the main loop on the canvas, is deleted this causes the canvas to appear in the normal size 200 x 200. However if the button is present the canvas will not have the normal dimensions 200 x 200, but will be microsized and centered around the button, could you explain why this is so?

Tk Mystery 2.
I have another program including a scrollable canvas, on which it is possible to draw rectangles. The program was orginaly written by mr. Liverpole, and has been modified slightly by me. When rightclicking on the canvas and drawing a rectangle the coordinates of the mouse pointer on the canvas are written in the command window. The intressting thing is that if the canvas is scrolled, coordinates at the canvas seem to move together with the scroll bar. For example the coordinate 0,0 will be at the upper left corner of the visual canvas part regardless if the canvas have been scrolled at any direction or not. The rectangles drawn will appear in wrong possitions if the canvas is scrolled. It seems therefore that the canvas registers the change in coordinates by scrolling, while the mouse pointer doesn´t do this. Could you please give me an explanation why the program seem to disregard the change of coordinates that one could suppose would happen by scrolling, and why the recangles are drawn in wrong places when the canvas is scolled?
#!/usr/bin/perl -w use Tk; use strict; # Globals my $b_button_1_down = 0; # Is the left mouse button currently down? my ($x0, $y0, $x1, $y1); # Starting and ending (x,y) coordinates my $rect_id = 0; # Last rectangle drawn # Main program / GUI setup my $mw = MainWindow->new; my $c = $mw->Scrolled('Canvas', -width => 200, -height => 200, -background =>'blue', -scrollregion => [ 0, 0, 1500, 1000 ] ); $c->pack(-expand => 1, -fill => 'both'); bind_left_mouse($c); MainLoop; # Subroutines sub bind_left_mouse { my ($c) = @_; # Create "callback"; subroutines which get called whenever the # corresponding event is triggered. Note that Ev('x') and Ev('y') # will tell us, at the time they're used, what the current (x,y) # coordinate pair was. # my $cb1 = [ \&left_mouse_down, $c, Ev('x'), Ev('y')]; my $cb2 = [ \&left_mouse_release, $c, Ev('x'), Ev('y')]; my $cb3 = [ \&left_mouse_moving, $c, Ev('x'), Ev('y')]; # Bind the callbacks $c->CanvasBind("<ButtonPress-1>", $cb1); $c->CanvasBind("<ButtonRelease-1>", $cb2); $c->CanvasBind("<Motion>", $cb3); } sub left_mouse_down { # This gets called whenever the left-mouse button is clicked my ($ev, $c, $x, $y) = @_; $b_button_1_down = 1; ($x0, $y0) = ($x, $y); print "(debug) button1 down, (x,y) => ($x,$y)\n"; # ($first, $last) = $c->get( ); #print "F $first L $last\n"; } sub left_mouse_moving { # This gets called whenever the mouse moves. It's true for ALL # mouse motion, but we're really just interested in when the mouse # is moving -and- the left mouse button is held down; hence the na +me. # my ($ev, $c, $x, $y) = @_; return unless $b_button_1_down; print "(debug) button1 moving, (x,y) => ($x,$y)\n"; $rect_id and $c->delete($rect_id); # Delete any old rectangle firs +t $rect_id = $c->createRectangle($x0, $y0, $x, $y, -outline => 'yell +ow', -dash => '. '); } sub left_mouse_release { # This gets called whenever the left-mouse button is released my ($ev, $c, $x, $y) = @_; $b_button_1_down = 0; ($x1, $y1) = ($x, $y); print "(debug) button1 release, (x,y) => ($x,$y)\n"; # Now do something with the triangle at (x0,y0,x1,y1)... $rect_id and $c->delete($rect_id); # Delete any old rectangle firs +t $rect_id = $c->createRectangle($x0, $y0, $x, $y, -outline => 'yell +ow', -dash => '. '); }

Replies are listed 'Best First'.
Re: Tk mysteries.
by liverpole (Monsignor) on Oct 03, 2006 at 18:27 UTC
    Hi again, tamaguchi,

    As for mystery #1, you don't want to use pack, you want to use createWindow instead, which is the mechanism by which you can put widgets into a Canvas.

    Try this:

    #!/usr/bin/perl -w use Tk; use strict; use warnings; + my $mw = MainWindow->new; my $c = $mw->Scrolled('Canvas', -width => 500, -height => 500, -background =>'blue', -scrollregion => [ 0, 0, 1000, 1000 ] ); + $c->pack(-expand => 1, -fill => 'both'); $c->createRectangle(100, 100, 150, 150, -fill => 'yellow'); + my $plus = $c->Button( -text => ' + ', -command => sub { $c->scale("all", 0, 0, 2, 2) } ); my $id = $c->createWindow(28, 20, -window => $plus); MainLoop;

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Tk mysteries.
by liverpole (Monsignor) on Oct 03, 2006 at 19:58 UTC
    Hi tamaguchi,

    I had to do a little research to be able to answer your Tk Mystery 2.  You see, I had noticed this behavior before, but I hadn't solved it myself, so I used your question as an excuse to go figure it out.  (I also needed a few minutes to test and verify it).

    The answer is that you have to assign a variable to the actual Canvas object which is a Subwidget of the Scrolled object, and bind to it.  Then, when you pass the (x, y) coordinate pair, you need to use the Canvas methods canvasx() and canvasy() to convert them to the transformed (x, y) pair you will use.

    Here is a rewrite of your code (well, originally mine :-)), which demonstrates this:

    #!/usr/bin/perl -w use Tk; use strict; use warnings; # Globals my $scroll_x = 0; my $scroll_y = 0; my $b_button_1_down = 0; # Is the left mouse button currently down? my ($x0, $y0, $x1, $y1); # Starting and ending (x,y) coordinates my $rect_id = 0; # Last rectangle drawn # Main program / GUI setup my $mw = MainWindow->new; my $c = $mw->Scrolled('Canvas', -width => 200, -height => 200, -background =>'blue', -scrollregion => [ 0, 0, 1500, 1000 ] ); $c->pack(-expand => 1, -fill => 'both'); bind_left_mouse($c); MainLoop; # Subroutines sub bind_left_mouse { my ($c) = @_; # Create "callback"; subroutines which get called whenever the # corresponding event is triggered. Note that Ev('x') and Ev('y') # will tell us, at the time they're used, what the current (x,y) # coordinate pair was. Note, too, that we're now getting the actu +al # Canvas object $cv from the Scrolled object $c, using the Subwidg +et() # method, and using it for the binding, and as a parameter to each + of # the 3 callbacks. # my $cv = $c->Subwidget("canvas"); my $cb1 = [ \&left_mouse_down, $cv, Ev('x'), Ev('y')]; my $cb2 = [ \&left_mouse_release, $cv, Ev('x'), Ev('y')]; my $cb3 = [ \&left_mouse_moving, $cv, Ev('x'), Ev('y')]; # Bind the callbacks $cv->CanvasBind("<ButtonPress-1>", $cb1); $cv->CanvasBind("<ButtonRelease-1>", $cb2); $cv->CanvasBind("<Motion>", $cb3); } sub left_mouse_down { # This gets called whenever the left-mouse button is clicked my ($ev, $cv, $x, $y) = @_; # Figure out what adjustments have to be made for the scroll locat +ion $x = $cv->canvasx($x); $y = $cv->canvasy($y); $b_button_1_down = 1; ($x0, $y0) = ($x, $y); print "(debug) button1 down, (x,y) => ($x,$y)\n"; } sub left_mouse_moving { # This gets called whenever the mouse moves. It's true for ALL # mouse motion, but we're really just interested in when the mouse # is moving -and- the left mouse button is held down; hence the na +me. # my ($ev, $cv, $x, $y) = @_; return unless $b_button_1_down; # Figure out what adjustments have to be made for the scroll locat +ion $x = $cv->canvasx($x); $y = $cv->canvasy($y); print "(debug) button1 moving, (x,y) => ($x,$y)\n"; $rect_id and $cv->delete($rect_id); # Delete any old rectangle fir +st my @opts = (-outline => 'yellow', -dash => '. '); $rect_id = $cv->createRectangle($x0, $y0, $x, $y, @opts); } sub left_mouse_release { # This gets called whenever the left-mouse button is released my ($ev, $cv, $x, $y) = @_; $b_button_1_down = 0; # Figure out what adjustments have to be made for the scroll locat +ion $x = $cv->canvasx($x); $y = $cv->canvasy($y); ($x1, $y1) = ($x, $y); print "(debug) button1 release, (x,y) => ($x,$y)\n"; # Now do something with the triangle at (x0,y0,x1,y1)... $rect_id and $c->delete($rect_id); # Delete any old rectangle firs +t my @opts = (-fill => 'purple'); $rect_id = $c->createRectangle($x0, $y0, $x, $y, @opts); }

    I changed the result to a solid purple rectangle (just to make it a little more interesting, as well as further visual confirmation of success).  As you can see, the program finally does the right thing, even when you've scrolled the view.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Tk mysteries.
by zentara (Cardinal) on Oct 03, 2006 at 18:49 UTC
    There are 3 things you need to consider. First, the scrollregion will define how big the real Canvas actually is. Second, the -width and -height will specify how big the Scrolled window will be. ( Assuming the window it is packed in will allow it). Without a $mw->geometry(), the $mw will fill itself according to the widgets packed in it.

    If you comment out the plus button, as below, you will see this. Placing a button directly into the canvas widget is wrong, you use a window. When you place it onto the canvas you get that weird sizing behavior. Don't do that. :-)

    #!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; #$mw->geometry('50x50'); #try this my $c = $mw->Scrolled('Canvas', -width => 200, -height => 200, -background =>'blue', -scrollregion => [ 0, 0, 500, 500 ] )->pack(-expand => -2, -fill => 'both'); $c->createRectangle(100, 100, 150, 150, -fill => 'yellow'); #my $plus = $c->Button( # -text => ' + ', # -command => sub {$c->scale("all", 0, 0, 2, 2); } ); # $plus->pack;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Tk mysteries.
by jdporter (Paladin) on Oct 03, 2006 at 16:47 UTC

    Re: mystery #1: I think you've got the sense of -scrollregion and -width/-height confused. Try this:

    my $c = $mw->Scrolled('Canvas', -width => 500, -height => 500, -background =>'blue', -scrollregion => [ 0, 0, 200, 200 ] );

    And try resizing the window to something large (larger than 500x500) before pressing the '+' button.

    Not only did I not answer the OP question, I supplied bogus info. Tk::Canvas is complicated!

    We're building the house of the future together.