Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
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
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 avoiding work at the Monastery: (4)
As of 2014-07-28 22:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (210 votes), past polls