Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Entry Widget - validatecommand

by shortyfw06 (Beadle)
on Jul 23, 2012 at 13:36 UTC ( #983172=perlquestion: print w/replies, xml ) Need Help??
shortyfw06 has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to set a validate command allowing for only positive or negative integers. I also want a reset button to clear the entry. This doesn't seem to be working properly. Can anyone offer some advice? Thanks.

#!usr/bin/perl use Tk; use strict; use warnings; # my $value; my $mw=new MainWindow; my $ent = $mw -> Entry(-textvariable=> \$value, -validate=>'key', -validatecommand=>sub { $_[0] =~ /-{0,1}\d+$/ }, -invalidcommand=> \&lam_num_error)->pack(); my $print_button = $mw->Button(-text=>"Print", -command=> \&print, -font=>"ansi 10 bold")->pack(); # my $reset_frm = $mw -> Frame(); $reset_frm->pack(-fill=>'both'); my $reset_button = $reset_frm->Button(-text=>"Reset", -command=> \&do_reset, -font=>"ansi 10 bold")->pack(); MainLoop; sub print { print "$value"; } sub do_reset { $ent->delete(0,'end'); } sub lam_num_error { $mw->messageBox(-message=>"The input must be an integer."); }

Replies are listed 'Best First'.
Re: Entry Widget - validatecommand
by choroba (Chancellor) on Jul 23, 2012 at 13:50 UTC
    Quoting the documentation:
    In general, the textVariable and validateCommand can be dangerous to mix.
    Also, you probably should only run the validation on lost focus (otherwise, negative integers cannot be entered from the beginning), i.e. change 'key' to 'focusout'.
Re: Entry Widget - validatecommand
by zentara (Archbishop) on Jul 23, 2012 at 14:49 UTC
    This example might help:
    #!/usr/bin/perl use warnings; use Tk; use strict; #by Thomas Koenig -- free to use my($mw) = MainWindow->new; my(@realvar, @e); $mw->Label(-text => "Input real numbers")->pack; foreach my $i (0..4) { $e[$i] = $mw->Entry(-textvariable => \$realvar[$i], -validate =>'all', -validatecommand =>\&check_real)->pack; } $mw->Button(-text => "Quit", -command => sub { print join (" ",@realvar),"\n"; exit ;})->pack; MainLoop; sub check_real { my($proposed, $changes, $current) = @_; my($ok); if ($changes) { $ok = $proposed =~ /^ [+-]? ( ((\d+(\.\d*)?)|(\.\d+)) ([eE][+-]?\d*)? )? $/x; } else { $ok = $proposed =~ /^[+-]?((\d+(\.\d*)?)|(\.\d+))([eE][+-]?\d+)?$/; } print "current $current changes $changes proposed $proposed: " . ($ok ? "" : "not" ) . "OK\n"; $ok; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Entry Widget - validatecommand
by Athanasius (Chancellor) on Jul 23, 2012 at 15:28 UTC

    The OP’s requirements can be met by making two smallish changes:

    my $ent = $mw->Entry(-textvariable => \$value, -validate => 'key', -validatecommand => sub { $_[0] =~ /^(?:|-|\d+|- +\d+)$/ }, # <== new regex -invalidcommand => \&lam_num_error)->pack(); ... sub print { print $value unless $value eq '-'; }

    The strategy here is to pass as valid not only the final data entry, but also all intermediate states reached during its construction (including, especially, the empty string, which is the starting state. This allows Reset to work without error). The print sub then applies a final validation, to exclude partially-constructed data.

    Incidentally, why does this print sub work? I have verified that it does work as required, but why doesn’t it simply call itself recursively??

    Athanasius <°(((><contra mundum

      sub print { print $value unless $value eq '-'; }

      Incidentally, why does this  print sub [not] simply call itself recursively??

      Its a namespace problem (feature? artifact?). Your new  print is defined in  main and the built-in  print is defined in  CORE and the  CORE namespace has search precedence. Try the following:

      >perl -wMstrict -le "sub print { CORE::print('foo') } ::print; " foo >perl -wMstrict -le "sub print { ::print('foo') } ::print; " Deep recursion on subroutine "main::print" at -e line 1. Terminating on signal SIGINT(2)

        Thanks, AnomalousMonk, for the explanation. Your statement:

        the CORE namespace has search precedence.

        raises a new question: How, then, does the original code ever find the print sub defined in namespace main if the sub isn’t explicitly qualified with its namespace (i.e., main::print)? Why doesn’t Perl always find CORE::print?

        I think I found the answer: the original code works only because it uses a reference to the sub:

        -command=> \&print

        and in this case the reference is disambiguated using lexical scope. I found an explanation of the latter in the Camel Book, 4th Edition, “Name Lookups,” pages 62–65. Is there any documentation on how Perl looks up subroutine names when a subroutine is actually called (as opposed to being referenced)?


        Athanasius <°(((><contra mundum

      Thank you. I've tried this code and it is allowing inputs other than numbers (positive or negative). I'd like to limit the entry options to only positive or negative integers.
        this code ... is allowing inputs other than numbers

        Not so! For the record, here is the exact code I am running (using Strawberry perl v5.16.0 built for MSWin32-x86-multi-thread-64int, running under Windows Vista 32-bit, and with Tk 804.030):

        #! perl use strict; use warnings; use Tk; my $value; my $mw = new MainWindow; my $ent = $mw->Entry(-textvariable => \$value, -validate => 'key', -validatecommand => sub { $_[0] =~ /^(?:|-|\d+|-\ +d+)$/ }, -invalidcommand => \&lam_num_error)->pack(); my $print_button = $mw->Button(-text => "Print", -command => \&printx, -font => "ansi 10 bold")->pack(); my $reset_frm = $mw->Frame(); $reset_frm->pack(-fill => 'both'); my $reset_button = $reset_frm->Button(-text => "Reset", -command => \&do_reset, -font => "ansi 10 bold")->pac +k(); MainLoop; sub printx { print $value unless $value eq '-'; } sub do_reset { $ent->delete(0, 'end'); } sub lam_num_error { $mw->messageBox(-message => "The input must be an integer."); } __END__

        This lets me enter -42, 763, etc., but if I try to enter a letter, or a punctuation symbol, or a minus sign anywhere after the first character, or even a space, I immediately get the message “The input must be an integer.” and my input never makes it into the Entry box. In other words, the code is working exactly as required.

        Athanasius <°(((><contra mundum

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (3)
As of 2017-09-23 14:57 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (272 votes). Check out past polls.