Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Tk -textvariable doesn't change when Entry widget changes

by Petras (Friar)
on Sep 20, 2005 at 14:09 UTC ( #493456=perlquestion: print w/replies, xml ) Need Help??
Petras has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to read from two Entry widgets and pass their values to a sub. It's not doing what I would expect. Here's the relevant code:
#!perl use Tk; use strict; use warnings; my $original_x = 3; my $original_y = 4; my $mw = MainWindow -> new; my $f1 = $mw -> Frame; my $user_x = $f1 -> Entry ( -textvariable => $original_x, -width => 3, ) -> pack; my $user_y = $f1 -> Entry ( -textvariable => $original_y, -width => 3, ) -> pack; $f1 -> pack; $mw -> Button ( -text => "Print Entries", -command => [\&print_entries, $user_x -> get(), $original_y], ) -> pack; MainLoop; sub print_entries { print "@_\n"; }
I would expect the shell output to be the entry in $user_x, a space, and the entry in $user_y. Instead the output is consistantly the hard-coded default value of $original_x, a space, and the hard-coded default value of $original_y (please don't respond and ask if I changed the values in the Entry widgets ;~) Of course I did!).

You can see in the -command option I tried two different methods for getting the values of Entry widgets. Neither seem to work, which puzzels me. Quoth the Emu, "Nevermore!" Wait, that was a different bird. Mastering Perl/Tk says:

5.2.13. Getting the Contents of an Entry Widget
There are two ways to determine the content of the Entry widget: the get method or the variable associated with the -textvariable option. Using the get method, $entry_text = $entry->get() will assign the entire content of the Entry widget into $entry_text.

The code tries both, but doesn't get what I ask for. Any suggestions as to why? After looking a little deeper I changed -command => [\&print_entries, $user_x -> get(), $y_size], to -command => [\&print_entries, $user_x -> cget(-textvariable), $y_size], an still get the same results.

Update: I made one other change and the results changed, but still to something unexpected. I turned -command => [\&print_entries, $user_x -> get(), $y_size],
into -command => [\&print_entries, \$user_x -> get(), \$y_size],
(note the back-slashes before the $ in the variable names). Now, the shell output is SCALAR(some memory location), a space, and SCALAR(some other memory location). How do I extract the user entry from an Entry widget? Is it relevant that the Entry widget is in a Frame that is a child of the widget that the Button widget is in?

Thanks for your help!


Don't worry about people stealing your ideas. If your ideas are any good, you'll have to ram them down people's throats.
-Howard Aiken

Replies are listed 'Best First'.
Re: Tk -textvariable doesn't change when Entry widget changes
by jdporter (Canon) on Sep 20, 2005 at 14:51 UTC

    Using the array-ref style of -command is tricky and has limited usefulness, so I'll show you how to do it using an Entry with -textvariable, and using a Button -command with a real sub.

    First, when using -textvariable, you want to give a reference to the variable, like so:

    my $original_x = 3; my $user_x = $f1->Entry( -textvariable => \$original_x )->pack;

    Then, whenever $original_x is tested/printed/etc., it contains whatever is currently in the entry box.

    Here's a good way to use -command:

    $mw->Button( -command => \&print_entries )->pack; sub print_entries { my $val = $user_x->cget('-text'); print "$val\n"; }

    The salient facts are these:

    1. You should call cget on '-text', not '-textvariable'.
    2. The call to cget should be "delayed" until the time the button is pressed, not when it is created. In your form, cget was being called at creation time.
    You could call cget('-textvariable'), but that returns a reference to the variable, not the current value. So you'll need to dereference it, like so:

    sub print_entries { my $var_ref = $user_x->cget('-textvariable'); my $val = $$var_ref; print "$val\n"; }
    Of course, in this simple program you showed, you don't need to do either of those things, because you already have the variable in scope:
    sub print_entries { print "$original_x\n"; }
    (assuming you've used the -textvariable option as above.)
      Thank you, jdporter for a right solution! I was trying to use @_ in the sub and not getting what I thought I would. I was passing variables to the sub because, coming from another language, I thought scope would be an issue referencing the variables that were modified in a different block. Thanks!


      Don't worry about people stealing your ideas. If your ideas are any good, you'll have to ram them down people's throats.

      -Howard Aiken
Re: Tk -textvariable doesn't change when Entry widget changes
by JediWizard (Deacon) on Sep 20, 2005 at 14:51 UTC

    I believe that you need to pass a reference to a (Update: Scalar) variable into the -textvariable parameter for an entry widget.

    use Tk; require Tk::Entry; require Tk::Button; my $mw = MainWindow->new(); my($var1, $var2) = ('val1', 'val2'); my $entry1 = $mw->Entry(-textvariable=> \$var1)->pack(); my $entry2 = $mw->Entry(-textvariable => $var2)->pack(); my $but = $mw->Button(-text=>'print', -command=> sub{print "$var1, $va +r2\n"; } )->pack(); MainLoop; __END__ # Output upon pressing print after changeing both entry widget's: entry1, val2

    Update: Please note that I intentioally left off the backslash on $val2 to demonstrate the different behavior when passing a simple value rather than a scalar reference.

    They say that time changes things, but you actually have to change them yourself.

    —Andy Warhol

Re: Tk -textvariable doesn't change when Entry widget changes
by Util (Priest) on Sep 20, 2005 at 15:39 UTC

    When you create a PerlTk callback that will take arguments, using the [] syntax, the Tk::callbacks documentation says:

    the contents of the [] are evaluated by perl when the callback is created

    Therefore, your code,

    $mw->Button ( -command => [\&print_entries, $user_x->get(), $original_y], );
    is roughly the equivalent of this:
    my $command_sub; { my $x_at_time_of_sub_definition = $user_x->get(); my $y_at_time_of_sub_definition = $original_y; $command_sub = sub { print_entries( $x_at_time_of_sub_definition, $y_at_time_of_sub_definition, ); } } $mw->Button ( -command => $command_sub, );

    Instead of telling PerlTk to create the callback, create it yourself with an anonymous sub. This should work for you:

    -command => sub { print_entries( $user_x->get(), $user_y->get(), $original_x, $original_y, ); },

      For reasons in the *real* application I need to create a call back to a real sub. Thank you, though for your insight on when variables are evaluated in brackets and sub calls.


      Don't worry about people stealing your ideas. If your ideas are any good, you'll have to ram them down people's throats.

      -Howard Aiken

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://493456]
Approved by ww
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (13)
As of 2018-01-22 15:29 GMT
Find Nodes?
    Voting Booth?
    How did you see in the new year?

    Results (235 votes). Check out past polls.