Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Tk::Entry selection problem

by grizzley (Chaplain)
on Apr 21, 2008 at 09:14 UTC ( #681885=perlquestion: print w/ replies, xml ) Need Help??
grizzley has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to write simple application, which allows autocompletion while typing text. There is very good widget Tk::MatchEntry, but allows only autocompletion of whole string. I am aiming at autocompletion on every word I type. Below is what I did to this moment: app, widgets, binding. The problem is with selection management in the OnKeyPress() function. If I could extract properly whole content from textbox and extract selection text I would handle the rest.

  • $txtBox->tagRanges('sel'); returns begin/end coordinates of selection in <x,y> 2D format and if I extract data from widget via get() method it will be 1D string. Is there simple method to convert coordinates to 1D?
  • When I select anything, I get error 'Tk::Error: wrong # args: should be ".frame1.text get index1 ?index2 ...?" at C:/Perl/lib/Tk.pm line 252'

I've read Tk manual, am aware of 'sel.first', 'sel.last'. It seems to me I won't have to translate from 2D to 1D format if I use 'delete', 'insert' (and other Tk::Entry commands) in some clever way.

In the comment at the end of the code is the idea of what I want to achieve. Any help/comment is welcome.

#!/usr/local/bin/perl use Tk; use Tk ':variables'; use strict; use warnings; # Main Window my $mw = new MainWindow; #GUI Building Area my $frm_name = $mw -> Frame(); #Text Area my $textarea = $mw -> Frame(); my $txt = $textarea -> Text(-width=>40, -height=>10); my $srl_y = $textarea -> Scrollbar(-orient=>'v',-command=>[yview => $t +xt]); my $srl_x = $textarea -> Scrollbar(-orient=>'h',-command=>[xview => $t +xt]); $txt -> configure(-yscrollcommand=>['set', $srl_y], -xscrollcommand=>[ +'set',$srl_x]); my $log = $textarea -> Text(-width=>40, -height=>2); #Geometry Management $frm_name -> grid(-row=>1,-column=>1,-columnspan=>2); $txt -> grid(-row=>1,-column=>1); $log -> grid(-row=>6,-column=>1); $srl_y -> grid(-row=>1,-column=>2,-sticky=>"ns"); $srl_x -> grid(-row=>2,-column=>1,-sticky=>"ew"); $textarea -> grid(-row=>5,-column=>1,-columnspan=>2); $txt->bind('<KeyPress>', [\&OnKeyPress, $txt]); my @dictionary = qw/rewelation reduction renowation reckonesance reval +uation reply/; $txt->focus; MainLoop; sub OnKeyPress { my $txtBox = $_[0]; # print 'Keysym=', $Tk::event->K, ', numeric=', $Tk::event->N, "\n +"; my @r = $txtBox->tagRanges('sel'); if(@r) { my $str = $txtBox->get(); my $sel = substr $str, $r[0], $r[1]-$r[0]; print 'selected: ', $sel; } } =the rest sub OnKeyPressed { if(key is <ENTER>) { accept_auto_complete_version() } elsif(matches_first_char_in_autocompletion_version()) { unselect this char; } else { unselect this char; find_other_autocompletion; } } =cut

Comment on Tk::Entry selection problem
Select or Download Code
Replies are listed 'Best First'.
Re: Tk::Entry selection problem
by zentara (Archbishop) on Apr 21, 2008 at 15:53 UTC
    This gets to be pretty tricky from what I remember from past attempts at it. It's a groggy Monday morning for me to be too inspired. I have some pointers though:

    You might want to look at the "editModified" flag of the text box. See Using Tk::Text and '<<Modified>>' or Tk::Text with Vi; Silly or not?

    The problem with the <<Modified>> tag, is that it will only report the first letter, and can cause deep recursion errors, as shown here.

    #!/usr/bin/perl use warnings; use strict; use Tk; use Tk::Text; my $MW = MainWindow->new( -title => "Tk::Text test", -width => 200, -height => 200 ); my $text = $MW->Text( -height => 10, -width => 40, -wrap => 'word' ); $text->pack( -side => 'top', -fill => 'both' ); $text->bind( '<<Modified>>' => sub { getText( $text, @_ ); } ); $MW->Button(-text=>'Clear Modified Flag', -command => sub{ $text->editModified(0) } )->pack(); MainLoop; sub getText { my ( $t, @args ) = @_; my $text = $t->get( '0.0', 'end' ); # will cause deep recursion error # $t->editModified(0); print "Got: $text\n"; return if ( !$text ); return 1; }
    Or look at this, to circumvent the deep recursion problem.

    #!/usr/bin/perl use strict; use warnings; use Tk; use Tk::TextUndo; my $top = MainWindow->new; my $text = $top->TextUndo->pack( -expand => 'yes', -fill => 'both' ); $text->bindtags( [ $text, ref($text), $text->toplevel, 'all' ] ); $text->bind( '<KeyPress>', [ \&callback, Ev('K') ] ); MainLoop; sub callback { my ( $widget, $keysym ) = @_; print "\n$keysym key pressed"; return if ( ( length($keysym) > 1 and $keysym =~ /\p{Upper}/ and $keysym !~ /Delete|BackSpace|Return|Tab/ ) or ( $keysym eq 'Delete' and $widget->index('insert') eq $widget->index('end -1c') +) or ( $keysym eq 'BackSpace' and $widget->index('insert') eq '1.0' ) ); print ' - text is edited.'; }

    and a way to get current tags positions

    #!/usr/bin/perl use warnings; use strict; use Tk; #subs borrowed from widget demo my $usage = "USAGE: $0 filename searchstring (regexp|exact)\n"; my $mw = MainWindow->new( -bg => 'black' ); $mw->geometry('100x30+100+15'); my ($file_name, $search_string, $kind) = @ARGV; if(! -e $file_name){print $usage; die "No file found $!\n"} if($search_string eq ''){print $usage; die "Need a search string";} my $file; open (FH,"< $file_name"); read( FH, $file, -s FH ); close FH; $kind ||= 'exact'; my $text = $mw->Scrolled(qw/Text -setgrid true -scrollbars e/); $text->tagConfigure( 'search', -foreground => 'red',-background => ' +white' ); $text->insert('0.0', $file); $text->mark(qw/set insert 0.0/); $text->pack(qw/-expand yes -fill both/); &search_text($text,\$search_string,'search',$kind); MainLoop; ###################################################################### +#3 sub search_text { # The utility procedure below searches for all instances of a give +n # string in a text widget and applies a given tag to each instance + found. # Arguments: # # w - The window in which to search. Must be a text widget. # string - Reference to the string to search for. The search i +s done # using exact matching only; no special characters. # tag - Tag to apply to each instance of a matching string. my ( $w, $string, $tag, $kind ) = @_; #print "@_\n"; return unless ref($string) && length($$string); $w->tagRemove( $tag, qw/0.0 end/ ); my ( $current, $length ) = ( '1.0', 0 ); my ($current_last, $length_last); while (1) { $current = $w->search( -count => \$length, "-$kind", $$string, $current +, 'end' ); last if not $current; warn "Posn=$current count=$length\n", $w->tagAdd( $tag, $current, "$current + $length char" ); $current = $w->index("$current + $length char"); } } # end search_text

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Tk::Entry selection problem
by thundergnat (Deacon) on Apr 21, 2008 at 16:36 UTC
    $txtBox->tagRanges('sel'); returns begin/end coordinates of selection in <x,y> 2D format and if I extract data from widget via get() method it will be 1D string. Is there simple method to convert coordinates to 1D?

    $txtBox->tagRanges('sel'); doesn't return 2D coordinates, it returns the line.column of the selected text start and end point(s). If you get() the text between those endpoints, you will have the selected text.

    my @ranges = $txtBox->tagRanges('sel'); my $text = $txtBox->get(@ranges[0,1]);
    When I select anything, I get error 'Tk::Error: wrong # args: should be ".frame1.text get index1 ?index2 ...?" at C:/Perl/lib/Tk.pm line 252'

    Because you are not giving the get() method any indicies to work with. Try:

    if(@r) { my $str = $txtBox->get(@r);

    Note that @r may have more than two coordinates. You may need to test and allow for them.

    ...if I use 'delete', 'insert' (and other Tk::Entry commands)...

    Be aware that Tk::Entry methods != Tk::Text methods for the most part. There are many similarities, but many differences too. Do you want to do this with a Tk::Text box or a Tk::Entry? It would probably be easier with the Tk::Entry, it doesn't already have a defined action bound to the Return key. Tk::Text does, so you'll have to try to figure out what action the user meant when they hit Return. Not impossible, but a larger can of worms than I would want to open.

      That's exactly what I needed. Thanks!

      From my point of view, type of control doesn't matter. Tk::Entry works fine.

Re: Tk::Entry selection problem
by grizzley (Chaplain) on Apr 28, 2008 at 09:25 UTC
    Final version:
    #!/usr/local/bin/perl use Tk; use Tk ':variables'; use strict; use warnings; my @choices = qw/seeing something important reply reduction renowation + reconnaissance/; # Main Window my $mw = new MainWindow; #GUI Building Area my $frm_name = $mw -> Frame(); #Text Area my $textarea = $mw -> Frame(); my $txt = $textarea -> Text(-width=>40, -height=>10); my $srl_y = $textarea -> Scrollbar(-orient=>'v',-command=>[yview => $t +xt]); my $srl_x = $textarea -> Scrollbar(-orient=>'h',-command=>[xview => $t +xt]); $txt -> configure(-yscrollcommand=>['set', $srl_y], -xscrollcommand=>[ +'set',$srl_x]); my $log = $textarea -> Text(-width=>40, -height=>2); #Geometry Management $frm_name -> grid(-row=>1,-column=>1,-columnspan=>2); $txt -> grid(-row=>1,-column=>1); $log -> grid(-row=>6,-column=>1); $srl_y -> grid(-row=>1,-column=>2,-sticky=>"ns"); $srl_x -> grid(-row=>2,-column=>1,-sticky=>"ew"); $textarea -> grid(-row=>5,-column=>1,-columnspan=>2); $txt->bind('<KeyPress>', [\&OnKeyPress, $txt]); $txt->focus; MainLoop; sub OnKeyPress { my $txtBox = $_[0]; my $currentWord = $txtBox->get('insert -1c wordstart', 'insert'); my $wordEnd = $txtBox->get('insert', 'insert -1c wordend'); $log->delete('0.0', 'end'); $log->insert('end', "wordEnd: <$currentWord, $wordEnd>"); # do not want autocompletion in the middle of the word if(length $wordEnd == 0 # want autocomplete only when typing, not on key up, down, enter, +etc. && $Tk::event->K =~/^[\w]$/) { for(@choices) { if(/^($currentWord)(.*)/) { my $theRest = $2; my $len = length $theRest; my $selFrom = $txtBox->index("insert"); $txtBox->insert('insert', $theRest); $txtBox->tag('add', 'sel', "insert - $len chars", "ins +ert"); $log->insert('end', "matched: <$theRest>"); last; } } } return; }

    It took a while searching Google and testing variations of SelectFrom(), SelectTo(), tagging, etc. Until I found tag('add', 'sel', "insert - $len chars", "insert") in sources of Tk::SuperText and voila! I still don't know why this code doesn't work without '-1c', but it seems to be a good start for creating crystal ball.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://681885]
Approved by McDarren
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (11)
As of 2015-07-29 03:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (260 votes), past polls