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

I ran into this problem the other day, and seek enlightenment:
#!/usr/bin/perl -w use strict; use Tk; my @SLIDERVALUES = (0, 0, 0, 0); my @slider; my $i; sub show_array { print "\@SLIDERVALUES = ($SLIDERVALUES[0], $SLIDERVALUES[1], $SLIDER +VALUES[2], $SLIDERVALUES[3])\n"; } # window setup my $mw = MainWindow->new; foreach my $i (0 .. 3) { $slider[$i] = $mw->Scale (-from => 30, -to => 0, -orient => 'vertical', -variable => \$SLIDERVALUES[$i], )->pack ( -side => 'left', ); } show_array(); # This breaks things! @SLIDERVALUES = (5, 10, 15, 20); show_array(); $SLIDERVALUES[0] = 7; $SLIDERVALUES[1] = 14; $SLIDERVALUES[2] = 21; $SLIDERVALUES[3] = 28; show_array(); MainLoop;
When the line

    @SLIDERVALUES = (5, 10, 15, 20);

is commented out, the command line output is

@SLIDERVALUES = (0, 0, 0, 0) @SLIDERVALUES = (0, 0, 0, 0) @SLIDERVALUES = (7, 14, 21, 28)
and the sliders take up position at 7, 14, 21, and 28, as expected.

If, however, that line is not commented out, we get

@SLIDERVALUES = (0, 0, 0, 0) @SLIDERVALUES = (5, 10, 15, 20) @SLIDERVALUES = (7, 14, 21, 28)
at the command line, and the sliders all stay at 0.

I'd like to understand how the -variable option is being ignored, and I'd also like tips on how I could have further diagnosed this problem.

Many thanks...

Replies are listed 'Best First'.
Re: Perl/Tk and the "-variable => \$var" option
by graff (Chancellor) on Jun 19, 2005 at 18:20 UTC
    I'm not sure about the best way to explain this, except to show by example:
    use strict; my @array = ( 1, 2, 3, 4 ); print \@array,$/; my $ar0a = \$array[0]; @array = ( 5, 6, 7, 8 ); print \@array,$/; my $ar0b = \$array[0]; print "$ar0a :: $ar0b\n"; # will stringify memory address
    When I run that (macosx, perl 5.8.1), I find that address of the array ("\@array") remains the same, but the two instances of $array[0] have different memory addresses.

    Obviously, when you assign a scalar to  $array[$index] (given that this array element already has a value), you end up using the original memory address and overwriting the value that is stored there. When you assign a new list to the array, the act of instantiating a list (using the parens) on the rhs of the assignment involves allocating new memory to hold that list, and the lhs array now refers to that new allocation, instead of any previous one.

    update: So, the way to make your Perl/Tk script do what you want is to always use a for loop to reset values in the array, so as to preserve the original memory addresses of the elements -- e.g. if you add these lines to the test case above, and print the address of element 0, you'll see that it doesn't change:

    my $i = 0; $array[$i++] = $_ for ( 5, 6, 7, 8 );
Re: Perl/Tk and the "-variable => \$var" option
by davidrw (Prior) on Jun 19, 2005 at 18:18 UTC
    While i can't seem to come up with a simple example to demonstrate it, my feeling is that this line: @SLIDERVALUES = (5, 10, 15, 20); results in the 5,10,15,20 being stored in a different place (address).. i.e. that you've pulled the rug out from uder the \$SLIDERVALUES[$i] pointers.. That answer is consistent with direct changes like $SLIDERVALUES[1] = 14; working, but i realize it's still kind of hand-waving... Hopefully another monk can clarify from here..

    Here's some code that shows in short the funniest coding on:
    my @x = ( 1, 2, 3, 4); my $v = \$x[2]; warn $v; warn "v = " . $$v; warn " " . $_ . " " . \$_ for @x; @x = ( 5, 12, 13, 14); warn $v; warn "v = " . $$v; warn " " . $_ . " " . \$_ for @x; __END__ SCALAR(0x80fbc5c) at /tmp/arr line 6. v = 3 at /tmp/arr line 7. 1 SCALAR(0x80fbb0c) at /tmp/arr line 8. 2 SCALAR(0x80fbc14) at /tmp/arr line 8. 3 SCALAR(0x80fbc5c) at /tmp/arr line 8. 4 SCALAR(0x8120634) at /tmp/arr line 8. SCALAR(0x80fbc5c) at /tmp/arr line 11. v = 3 at /tmp/arr line 12. 5 SCALAR(0x80fbb0c) at /tmp/arr line 13. 12 SCALAR(0x80fbc14) at /tmp/arr line 13. 13 SCALAR(0x8120634) at /tmp/arr line 13. 14 SCALAR(0x810531c) at /tmp/arr line 13.
Re: Perl/Tk and the "-variable => \$var" option
by zentara (Cardinal) on Jun 20, 2005 at 12:02 UTC
    I'd like to understand how the -variable option is being ignored, and I'd also like tips on how I could have further diagnosed this problem.

    Well you will find in Tk, that it IS NOT ALWAYS AN ELEGANT LANGUAGE. Tk widgets are written and ported by different people, and sometimes what should work... dosn't. You could look at the Scale widget's source code and find out where the glitch is, but more often than not, you just want to find a hack to work around the problem.

    There are a few widgets that I have noticed will NOT automatically update themselves from variables which are referenced(or from other module objects). I'm guessing here, but whomever wrote the scale widget, probably concentrated his effort to make it work in the other direction, that is, you set the slider, and the reference value is changed. Or he ignored arrays.

    When I first ran your code, and saw it didn't respond, the first thing I did was manually make it update, like in the code below. Even $mw->update wouldn't do it. Also there is the '-command' option for the Scale widget, and quite often it is the key to automating a callback to update widgets. In the code below, I manually update the scale. In the readmore which follows, I show that the scale widgets dosn't like references in arrays, and works fine with straight scalar references. I didn't try a hash, instead of array, but I'll bet a hash fails too.

    #!/usr/bin/perl use warnings; use strict; use Tk; my @SLIDERVALUES = (0,0,0,0); my @slider; my $i; sub show_array { print "\@SLIDERVALUES = ($SLIDERVALUES[0], $SLIDERVALUES[1], $SLIDERVA +LUES[2], $SLIDERVALUES[3])\n"; } # window setup my $mw = MainWindow->new; foreach my $i (0 .. 3) { $slider[$i] = $mw->Scale( -from => 30, -to => 0, -orient => 'vertical', -variable => \$SLIDERVALUES[$i], # -command => undef, )->pack(-side => 'left'); } show_array(); # This breaks things! @SLIDERVALUES = (5, 10, 15, 20); show_array(); slider_update(); $SLIDERVALUES[0] = 7; $SLIDERVALUES[1] = 14; $SLIDERVALUES[2] = 21; $SLIDERVALUES[3] = 28; show_array(); slider_update(); $mw->update; MainLoop; ############################################# sub slider_update{ foreach my $i (0 .. 3) { $slider[$i]->set( $SLIDERVALUES[$i] ); } }
    In this readmore, I show that Scale will work with plain scalar refs.

    I'm not really a human, but I play one on earth. flash japh
Re: Perl/Tk and the "-variable => \$var" option
by zentara (Cardinal) on Jun 20, 2005 at 12:28 UTC
    Well just for jollies, I decided to see if a hash reference works with a Scale, and it DOES. So I guess the Scale widget likes scalar refs and hash refs, but NOT array refs.
    #!/usr/bin/perl use warnings; use strict; use Tk; my %svals =( 1 =>{ 'object' => undef, 'value' => 0, }, 2 =>{ 'object' => undef, 'value' => 0, }, 3 =>{ 'object' => undef, 'value' => 0, }, 4 =>{ 'object' => undef, 'value' => 0, }, ); # window setup my $mw = MainWindow->new; foreach my $i (1 .. 4) { $svals{$i}{'object'} = $mw->Scale( -from => 30, -to => 0, -orient => 'vertical', -variable => \$svals{$i}{'value'}, )->pack(-side => 'left'); } foreach my $i (1 .. 4) { $svals{$i}{'value'} = 7*$i; } MainLoop;

    I'm not really a human, but I play one on earth. flash japh