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

Hi!

I have made a quick convertion from TclTk to PerlTk. I didn't use Perl many years so I forgot almost all and so my code isn't perfect. Please do your remarks where appropriate.

TCL works relatively fast on my Core i7. But Perl variant works too slow.

Please help me understand what's is basically wrong?

Thanks very much in advance

#!/usr/bin/env perl use Tk; use strict; use POSIX; my $draw_items = 1; sub update_fileview { scroll_off(); hscroll_off(); our $canv; our $vscroll; our $hscroll; our $item_height; my $num_items = 2000; my $canv_width = $canv->width; my $canv_height = $canv->height; my $canv_width2 = $canv_width - $vscroll->width; my $canv_scroll_width = $canv_width2; my $xpad = $item_height / 20; my $ypad = $item_height / 20; my $item_box_height = $item_height + $ypad * 2; my $item_box_width = $item_height + $xpad * 2; my $nitems_in_row = $canv_width2 / $item_box_width; if ($nitems_in_row < 1) { $nitems_in_row = 1; if ($item_box_width > $canv_width2) { $canv_scroll_width = $item_box_width + $xpad + $vscroll->width; hscroll_on(); } } my $num_rows = ceil("$num_items.0" / "$nitems_in_row.0"); my $canv_scroll_height = $num_rows * $item_box_height; if ($canv_scroll_height > $canv_height) { scroll_on(); $canv->configure(-scrollregion => [0, 0, $canv_scroll_width, $canv +_scroll_height] ); } else { $canv->configure(-scrollregion => [0, 0, $canv_scroll_width, $canv +_height] ); } my $i = my $r = my $c = 0; for (; $i < $num_items; $i++) { our $y = $r * $item_box_height; our $x = $c * $item_box_width; if ($x + $item_box_width + $xpad > $canv_width2) { $r++; if ($c != 0) { $c = 0; $y = $r * $item_box_height; $x = $c * $item_box_width; $c++; } } else { $c++; } if ($y + $item_box_height + $ypad > $canv_height) { scroll_on(); } if ($draw_items) { $canv->create('rectangle', $x+$xpad, $y+$ypad, $x+$xpad+$item_he +ight, $y+$ypad+$item_height, -width => '1m', -outline => '#aaaaff', -fill => '#6666aa', -tags => "item$i" ); } else { $canv->coords("item$i", $x+$xpad, $y+$ypad, $x+$xpad+$item_heigh +t, $y+$ypad+$item_height); } } $draw_items = 0; } sub scroll_on { our $vscroll; $vscroll->raise($vscroll); } sub scroll_off { our $vscroll; $vscroll->lower(); } sub hscroll_on { our $hscroll; $hscroll->grid(); } sub hscroll_off { our $hscroll; $hscroll->gridRemove(); } our $top = new MainWindow; our $canv = $top->Canvas(-background => "white"); our $vscroll = $top->Scrollbar(-command => ['yview', $canv]); our $hscroll = $top->Scrollbar( -command => ['xview', $canv], -orient => 'horizontal'); $canv->configure( -yscrollcommand => ['set', $vscroll], -xscrollcommand => ['set', $hscroll]); $canv->grid( -row => 0, -column => 0, -sticky => 'nsew'); $vscroll->grid( -row => 0, -column => 0, -sticky => 'nse'); $hscroll->grid( -row => 1, -column => 0, -sticky => 'we'); $top->gridRowconfigure( 0, -weight => 1); $top->gridColumnconfigure(0, -weight => 1); $top->gridRowconfigure(1, -weight => 0); $vscroll->lower(); $top->bind('<Configure>' => sub { update_fileview(); }); our $screen_height = $top->screenheight; our $item_height = $screen_height / 10; our $min_item_offset = $item_height / 10; our $top_height = $screen_height / 2; our $top_width = ($top_height * 4) / 3; $top->geometry("=${top_width}x$top_height"); $top->bind('all', '<Button-1>', sub { scroll_on() }); $top->bind('all', '<Button-3>', sub { scroll_off() }); MainLoop;
ORIGINAL CODE ON TCL
#!/bin/sh # \ exec tclsh "$0" ${1+"$@"} package require Tk set draw_items 1 proc update_fileview {} { variable item_height variable draw_items scroll_off hscroll_off set num_items 2000 set canv_width [winfo width .canv] set canv_height [winfo height .canv] set canv_width2 [expr $canv_width - [winfo width .vscroll]] set canv_scroll_width $canv_width2 set xpad [expr {$item_height / 20}] set ypad [expr {$item_height / 20}] set item_box_height [expr $item_height + $ypad * 2] set item_box_width [expr $item_height + $xpad * 2] set nitems_in_row [expr {$canv_width2 / $item_box_width}] if {$nitems_in_row < 1} { set nitems_in_row 1 if {$item_box_width > $canv_width2} { set canv_scroll_width [expr $item_box_width + $xpad + [winfo wid +th .vscroll]] hscroll_on } } set num_rows [expr {ceil("$num_items.0" / "$nitems_in_row.0")}] set canv_scroll_height [expr {$num_rows * $item_box_height}] if {$canv_scroll_height > $canv_height} { scroll_on .canv configure -scrollregion "0 0 $canv_scroll_width $canv_scroll +_height" } else { .canv configure -scrollregion "0 0 $canv_scroll_width $canv_height +" } set i 0 set r 0 set c 0 for {} {$i < $num_items} {incr i} { set y [expr $r * $item_box_height] set x [expr $c * $item_box_width] if {$x + $item_box_width + $xpad > $canv_width2} { incr r if {$c != 0} { set c 0 set y [expr $r * $item_box_height] set x [expr $c * $item_box_width] incr c } } else { incr c } if {$y + $item_box_height + $ypad > $canv_height} { scroll_on } if {$draw_items} { .canv create rectangle [expr $x + $xpad] [expr $y + $ypad] [expr + $x + $xpad + $item_height] [expr $y + $ypad + $item_height] \ -width 1m -outline #aaaaff -fill #6666aa -tags "item$i" } else { .canv moveto "item$i" [expr $x + $xpad] [expr $y + $ypad] } } set draw_items 0 # set files [glob -nocomplain .* *] # set i [lsearch $files .] # set files [lreplace $files $i $i] # set i [lsearch $files ..] # set files [lsort [lreplace $files $i $i]] } proc scroll_on {} { raise .vscroll } proc scroll_off {} { lower .vscroll } proc hscroll_on {} { grid .hscroll } proc hscroll_off {} { grid remove .hscroll } canvas .canv -yscrollcommand ".vscroll set" -xscrollcommand ".hscroll +set" -background white scrollbar .vscroll -command ".canv yview" scrollbar .hscroll -command ".canv xview" -orient horizontal grid .canv -row 0 -column 0 -sticky nsew grid .vscroll -row 0 -column 0 -sticky nse grid .hscroll -row 1 -column 0 -sticky we grid rowconfigure . 0 -weight 1 grid columnconfigure . 0 -weight 1 grid rowconfigure . 1 -weight 0 lower .vscroll bind .canv <Configure> update_fileview set screen_height [winfo screenheight .] set item_height [expr $screen_height / 10] set min_item_offset [expr $item_height / 10] set top_height [expr $screen_height / 2] set top_width [expr {($top_height * 4) / 3}] wm geometry . =${top_width}x$top_height bind all <Button-1> scroll_on bind all <Button-3> scroll_off

Replies are listed 'Best First'.
Re: Simple PerlTk program working slow
by zentara (Cardinal) on Nov 28, 2017 at 18:33 UTC
    Hi, I'm not sure what you mean by "working slow"? I ran your code and it built and ran fast enough so that there didn't seem to be a problem. However, alI I got was 4000 squares which didn't have a scrollbar, but scrolled well with my mousewheel.

    When I added "use warnings", I got

    Argument "5.88383838383838384.0" isn't numeric in division (/) at ./12 +04438.pl line 42
    I'm not going to try and figure out what your program does, but you might want to look at Re: I seek illumination and knowledge for a sample canvas program that works.

    Or, a simpler version:

    #!/usr/bin/perl use warnings; use strict; use Tk; my $mw = new MainWindow(); $mw->geometry("600x400+200+200"); #for xscroll, must be packed before midframe to be visible my $botframe = $mw->Frame(-bg=>'grey45') ->pack(-fill=>'x',-side=>'bottom'); my $midframe = $mw->Frame(-bg=>'grey45')->pack(); my $midframel = $midframe->Frame(-bg=>'grey45') ->pack(-side=>'left',-expand=>1,-fill=>'y'); my $midframer = $midframe->Frame(-bg=>'grey45') ->pack(-side=>'right'); my $num_channels = 40; my $canvasp; my $canvast = $midframer->Canvas( -bg =>'pale goldenrod', -width=>2400, -height=>25, #need to set scrollregion with a bit extra to ensure #endpoint accuracy. See xscrollit sub -scrollregion=>[-10,0,7250,25], -xscrollincrement => 1, )->pack(-side=>'top'); #for canvasp and yscroll my $midframer1 = $midframer->Frame(-bg=>'grey45') ->pack(-side=>'top'); my $yscroll = $midframer1->Scrollbar( -orient => 'vertical', -command => \&yscrollit, -troughcolor =>'grey45', -activebackground =>'lightseagreen', -background => 'lightseagreen', )->pack(-side=>'right',-fill=>'y'); my $canvasxsd = $botframe->Canvas( #dummy filler -bg =>'grey45', -width=>75, -height=>25, -borderwidth=>0, ) ->pack(-side=>'left'); my $xscroll = $botframe->Scrollbar( -orient => 'horizontal', -command => \&xscrollit , -troughcolor =>'grey45', -activebackground =>'lightseagreen', -background => 'lightseagreen', )->pack(-side=>'right', -fill=>'x',-expand =>1); $canvasp = $midframer1->Canvas( -bg =>'lightsteelblue', -width=>2400, -height=> 50 * $num_channels, -scrollregion=>[-10,0,7250,(33 * $num_channels)], -xscrollincrement => 1, -yscrollincrement => 1, -xscrollcommand => [ 'set', $xscroll ], -yscrollcommand => [ 'set', $yscroll ], ) ->pack(-side=>'left');#,-fill=>'both'); my $canvasd = $midframel->Canvas( #top of left frame dummy filler -bg =>'grey45', -width=>75, -height=>25, -borderwidth =>0, )->pack(-side=>'top'); my $canvass = $midframel->Canvas( #left frame canvas -bg =>'lightseagreen', -width=>75, -height=> 50 * $num_channels, -scrollregion=>[0,0,75,(33 * $num_channels)], -yscrollincrement => 1, ) ->pack(-side=>'top'); #fill in some sample data to see scrolling action for( 0 .. 33 * $num_channels){ $canvass->createText(38, 10 + $_ * 33, -text => "C $_" , ); } #set up top frame canvas... a timeline for(0..7200){ if( $_ % 50 == 0){ $canvast->createLine($_,0,$_,12,-width=> 4,-tags=>['tick'] ); $canvast->createText($_, 20, -text=> $_,-tags=>['tick'] ); } } #set up main frame some canvas data foreach my $y (0..39){ foreach my $x (0..7200){ next unless ( $x % 5 == 0); next if 20*$x > 7200; $canvasp->createText($x * 20, 8 + $y * 33, -text=> 20*$x.'-'.$y, -tags=>['data'] ); } } my ($xt,$yt,$xt1,$yt1)=$canvast->bbox('all'); my ($xs,$ys,$xs1,$ys1)=$canvass->bbox('all'); my ($xp,$yp,$xp1,$yp1)=$canvasp->bbox('all'); print "t-> $xt,$yt, $xt1,$yt1\n"; print "s-> $xs,$ys, $xs1,$ys1\n"; print "p-> $xp,$yp, $xp1,$yp1\n"; $canvast->configure( -scrollregion=> [$canvasp->bbox('all')] ); MainLoop; ################################### sub xscrollit{ my $fraction = $_[1]; $canvast->xviewMoveto($fraction); $canvasp->xviewMoveto($fraction); } ###################################################################### sub yscrollit{ my $fraction = $_[1]; $canvass->yviewMoveto($fraction); $canvasp->yviewMoveto($fraction); } ####################################################################

    I'm not really a human, but I play one on earth. ..... an animated JAPH

      Sorry I didn't give enough details about the issue. Really program starts and runs fast. You can scroll down with mouse wheel. It works fine.

      The problem appears when I change main window size by dragging main window border with mouse. It issues 'Configure' event that is bound to a anonymous subroutine that just calls update_fileview() subroutine. This subroutine redraws canvas with rectangles.

      I must note that in TCL version 'Configure' event is bound on canvas widget and it works rather fine. In Perl when I bind 'Configure' event on canvas it doesn't call the bound subroutine when I change toplevel window size. So I decided to bind 'Configure' event on $top (toplevel) widget, that is a parent of canvas $canv. And it works as expected but redrawing during window resize is very slow.

      Now I know that the slowness is caused by binding to a toplevel ($top) widget instead of canvas ($canv). When I changed binding in TCL from canvas to toplevel ('.') it has shown the same issue.

      So by now I only try to figure out how to force 'Configure' event to come to canvas. Current binding only works for toplevel, that, as we know, shows very slow redraw issue. The main question - how to make canvas to receive 'Configure' event??

Re: Simple PerlTk program working slow
by dexahex (Novice) on Nov 28, 2017 at 23:22 UTC

    Ookey I have found the mistake. To summarize, binding of 'Configure' event on canvas didn't work and vertical scrollbar wasn't displayed. The reason is the little exception in Canvas API according to which I can't call canvas binding as usual like

    $canv->bind(...);

    According to http://search.cpan.org/dist/Tk/pod/bind.pod#CAVEATS:

    " Note that for the Canvas widget, the call to bind has to be fully qualified. This is because there is already a bind method for the Canvas widget, which binds individual canvas tags. $canvas->Tk::bind "

    After fixing everything works fine.

    Here is a fixed script:

    #!/usr/bin/env perl use Tk; use strict; use POSIX; my $draw_items = 1; sub update_fileview { scroll_off(); hscroll_off(); our $canv; our $vscroll; our $hscroll; our $item_height; my $num_items = 2000; my $canv_width = $canv->width; my $canv_height = $canv->height; my $canv_width2 = $canv_width - $vscroll->width; my $canv_scroll_width = $canv_width2; my $xpad = $item_height / 20; my $ypad = $item_height / 20; my $item_box_height = $item_height + $ypad * 2; my $item_box_width = $item_height + $xpad * 2; my $nitems_in_row = $canv_width2 / $item_box_width; if ($nitems_in_row < 1) { $nitems_in_row = 1; if ($item_box_width > $canv_width2) { $canv_scroll_width = $item_box_width + $xpad + $vscroll->width; hscroll_on(); } } my $num_rows = ceil("$num_items.0" / "$nitems_in_row.0"); my $canv_scroll_height = $num_rows * $item_box_height; if ($canv_scroll_height > $canv_height) { scroll_on(); $canv->configure(-scrollregion => [0, 0, $canv_scroll_width, $canv +_scroll_height] ); } else { $canv->configure(-scrollregion => [0, 0, $canv_scroll_width, $canv +_height] ); } my $i = my $r = my $c = 0; for (; $i < $num_items; $i++) { our $y = $r * $item_box_height; our $x = $c * $item_box_width; if ($x + $item_box_width + $xpad > $canv_width2) { $r++; if ($c != 0) { $c = 0; $y = $r * $item_box_height; $x = $c * $item_box_width; $c++; } } else { $c++; } if ($y + $item_box_height + $ypad > $canv_height) { scroll_on(); } if ($draw_items) { $canv->create('rectangle', $x+$xpad, $y+$ypad, $x+$xpad+$item_he +ight, $y+$ypad+$item_height, -width => '1m', -outline => '#aaaaff', -fill => '#6666aa', -tags => "item$i" ); } else { $canv->coords("item$i", $x+$xpad, $y+$ypad, $x+$xpad+$item_heigh +t, $y+$ypad+$item_height); } } $draw_items = 0; } sub scroll_on { our $vscroll; $vscroll->raise(); } sub scroll_off { our $vscroll; $vscroll->lower(); } sub hscroll_on { our $hscroll; $hscroll->grid(); } sub hscroll_off { our $hscroll; $hscroll->gridRemove(); } our $top = new MainWindow; our $canv = $top->Canvas(-background => "blue"); our $vscroll = $top->Scrollbar(-command => ['yview', $canv]); our $hscroll = $top->Scrollbar( -command => ['xview', $canv], -orient => 'horizontal'); $canv->configure( -yscrollcommand => ['set', $vscroll], -xscrollcommand => ['set', $hscroll]); $canv->grid( -row => 0, -column => 0, -sticky => 'nsew'); $vscroll->grid( -row => 0, -column => 0, -sticky => 'nse'); $hscroll->grid( -row => 1, -column => 0, -sticky => 'we'); $top->gridRowconfigure( 0, -weight => 1); $top->gridColumnconfigure(0, -weight => 1); $top->gridRowconfigure(1, -weight => 0); $vscroll->lower(); $canv->Tk::bind('<Configure>' => sub { update_fileview(); }); our $screen_height = $top->screenheight; our $item_height = $screen_height / 10; our $min_item_offset = $item_height / 10; our $top_height = $screen_height / 2; our $top_width = ($top_height * 4) / 3; $top->geometry("=${top_width}x$top_height"); $canv->Tk::bind('<Button-1>', sub { scroll_on() }); $canv->Tk::bind('<Button-3>', sub { scroll_off() }); MainLoop;
      It feels good to figure out your own problem, dosn't it? :-) Good luck to you.

      I'm not really a human, but I play one on earth. ..... an animated JAPH
        Yeah ;) Thanks!