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

Hi our Perl writers,

Would you please help me on this subject that has driven me crazy. My goal is to make sure the users always enter the word "go " before anything else to be entered in my Entry. For example, "go east" or "go west" is OK. At first, I thought I could do:

-validatecommand => ( $_1 =~ /^go / ) ;

But it won't work. It looks like -validatecommand doesnot like to match the whole word of "go".

Is there any way in Perl Tk that allows us to validate string that the users enter in Entry box?

For your reference, my simple code is below:

require Tk; use Tk; $mw = MainWindow->new(); $mw->Entry( -textvariable => \$content , -validate => 'key' , -validatecommand => ( $_[1] =~ /^go / ), -invalidcommand => sub { print "ERROR.\n", $mw->bell() } ) +->pack() ; MainLoop;
Thanks very much for your help,
Danny

Replies are listed 'Best First'.
Re: how to use validatecommand for string checking
by jdporter (Paladin) on Aug 25, 2007 at 01:05 UTC

    well, first of all, -validatecommand takes a sub ref (or one of the other valid callback forms), just as -invalidcommand does. So you'd write something like

    $mw->Entry( -textvariable => \$content , -validate => 'key' , -validatecommand => sub { $_[0] =~ /^go / }, -invalidcommand => sub { print "ERROR.\n", $mw->bell() } )->pack() ;

    However, for your purpose, it doesn't make sense to reject the edit entirely if the resulting string doesn't meet the criteria. Because if the entry starts out empty, the user doesn't even get a chance to type a g, let alone the following o. And I, as a user, don't like an interface which requires, without reason, that I type the command in a certain order. I should be able to enter most of the command line, and then go back and insert "go" at the beginning.

    So, instead, what you should do is use the validity of the new value to enable/disable an "OK" (submit) command button.

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
Re: how to use validatecommand for string checking
by zentara (Cardinal) on Aug 25, 2007 at 13:39 UTC
    Here is something I had in my library, written by someone else, called entry_auto_complete.pl. I adapted it to your problem, you may like it.
    #!/usr/bin/perl -w #entry_auto_complete.pl use Tk; use strict; my $mw = MainWindow->new; #list of recognized words/phrases #perhaps read in from a file of recognized words/phrases my (@list) = ( "apo\'strophe", "Upper", "this one", "that one", "the other one", "the something", "go west", "go east", "go north", "go south", ); @list = sort(@list); #show the list print "Upper, lower, space, apostrophe and all case insensitive\n"; print "\n"; foreach (@list) { print "$_\n"; } print "\n"; my $text = ''; my $entry = $mw->Entry( -textvariable => \$text, -width => 60 )->pack( +); $entry->bind( '<Key>' => sub { my ($w) = @_; #widget referenced my $ev = $w->XEvent; #get the event object my $A = $ev->A; #get ascii char from the event print "---------------------$A---------------\n"; #if the character entered is [lowercase] OR #[uppercase] OR [a space] OR [an apostrophe] ... if ( $A =~ m/[a-z]|[A-Z]| |'/ ) { foreach (@list) { #compare the entered text to the items in the list if ( $text ne '' and $_ =~ m/^$text/i ) { print "$text -- $_\n"; my $input_length = length($text); my $match_length = length($_); $text = $_; #highlight/select the tail end of the entry field +text $entry->selectionFrom($input_length); $entry->selectionTo($match_length); } #if } #foreach } #end if } #sub ); #end bind $mw->Button( -text => 'Quit', -command => sub { exit } )->pack( -fill +=> 'x' ); $entry->focus(); MainLoop;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Thanks for your response Monks jdporter and zentara.
      Your responses have confirmed my guess that the validatecommand doesn't check for string without us to do some fancy things. By the way, the reason I have to check for "go " in the beginning but avoid appendding "go " after reading the content of the Entry is because in fact I have to check for two keywords, not just one, "go " and "fly ". Depeding on which keyword the user enters, I will treat the data entered after these two keywords differently. I only mentioned "go " in the original post just for brevity.

      So I based on your code and crafted one to hopefully do what I want. The code is written to the best of my knowledge below. However, it appears that the validatecommand only check ONCE in this case. For example, if I type "gh" it will flag error as expected. But after I delete "gh" and retype "gn" it won't flag error anymore! The print command inside the sub is even not invoked at the 2nd mistake I intended to make! Would you please help if you see if there's something wrong here?

      #!/usr/bin/env perl use English; require Tk; use Tk; use Tk; my $mw = MainWindow->new(); my $entry = $mw->Entry()->pack(); $entry->configure( -textvariable => \$content , -validate => 'key' , -validatecommand => sub { my $newvalue = shift; my $changedchars = shift; my $currentvalue = shift; my $index = shift; my $type = shift; print "newval-> $newvalue\nchangedchars-> $changedchars\n". "curvalue-> $currentvalue\n index-> $index\n\n\n"; if( ($index == 0) and ($newvalue ne 'g')){return 0} if( ($index == 1) and ($newvalue ne 'go')){return 0} if( ($index == 2) and ($newvalue ne 'go ')){return 0} return 1; }, -invalidcommand => [ \&errorsub , \$entry ] ) ; MainLoop; sub errorsub { print "ERROR.\n"; $content = "" ; $entry->focus; $entry->icursor(0); }
      Thanks,

      Danny

        Another alternative which you may be overlooking, is to forget the -validate stuff, and just bind on the Return keypress, so you get the whole string at once. Then you can regex it.
        #!/usr/bin/perl use warnings; use strict; use Tk; my $mw = MainWindow->new(); my $content = ''; my $entry = $mw->Entry(-textvariable => \$content)->pack(); $entry->bind('<Return>',sub{ my $val = $entry->get; print "check with regex here and do what is needed $val\n" }); $entry->focus; MainLoop;

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: how to use validatecommand for string checking
by zentara (Cardinal) on Aug 25, 2007 at 13:32 UTC
    It gets tricky on the validate command, because it dosn't do exactly what you think. This example shows what it actually does, and gives a possible solution. Some clever regex guru may be able to make a regex, but this works.
    #!/usr/bin/perl use warnings; use strict; use Tk; my $mw = MainWindow->new(); my $content = 'go '; my $entry = $mw->Entry( -textvariable => \$content , -validate => 'key' , -validatecommand => sub { my $newvalue = shift; my $changedchars = shift; my $currentvalue = shift; my $index = shift; my $type = shift; print "newval-> $newvalue\nchangedchars-> $changedchars\n". "curvalue-> $currentvalue\n index-> $index\n"; if( ($index == 0) and ($newvalue ne 'g')){return 0} if( ($index == 1) and ($newvalue ne 'go')){return 0} if( ($index == 2) and ($newvalue ne 'go ')){return 0} return 1; }, -invalidcommand => sub { print "ERROR.\n", $mw->bell() } )->pack() ; $entry->icursor(3) $entry->focus; MainLoop;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum