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

Hi folks,
I'm attempting to use Tk::Text, and looking for a way to get it to tell me when it's contents have changed. Unless I'm reading the docs wrong, then binding a callback to the <<Modified>> virtual event should do the trick, however, this appears to either not run my callback at all, or only do it once. Test code:
use warnings; use strict; use Tk; 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, @_) } ); MainLoop; sub getText { my ($t, @args) = @_; my $text = $t->get('0.0', 'end'); print "Got: $text\n"; return if(!$text); return 1; }
If I run this, and type in the text box, I only get a "Got: x" for the first letter I type, the rest of them seem to get ignored. In my real code, it doesnt seem to get called at all.

I'm probably going to work around this by binding to <FocusOut> instead, but its still strange and annoying.. Or am I misinterpreting the docs?

C.

Replies are listed 'Best First'.
Re: Using Tk::Text and '<<Modified>>'
by zentara (Cardinal) on Nov 20, 2004 at 14:01 UTC
    Yeah, you need to reset the $text->editModified(0) flag. The "gotcha" comes when you try to reset the flag automatically at the end of the <<Modified>> callback. You will get an infinite loop. So you need to manually reset it somehow, maybe with a timer?
    #!/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' ); print "Got: $text\n"; return if ( !$text ); return 1; }

    I'm not really a human, but I play one on earth. flash japh
      you're right.

      But to clear said flag, you could use something like following: (I checked, it worked):

      my $allowed=1; $text->bind('<<Modified>>' => sub { getText($text, @_); do {$allowed=0;$text->editModified(0);$allowed=1} if $ +allowed; } );

      unfortunately this is not simple, so not very Perlish.

      addition: surprisingly, afterIdle(...) do not resolve that, and its usage restores initial problem...

      Best regards,
      Courage, the Cowardly Dog

      *Boggle* That's um, weird.. One of these days someone is going to write a consistent interface for all the Tk:: modules. (Or, pigs might fly..)

      Is it just me, or does that all seem just a little strange?

      C.

        "a consistent interface for all the Tk:: modules."

        What is inconsistent here? I agree that it would be much better if they can add some examples here and there in Tk documents, so that we can reach the proper use more quickly. The entire Tk document is long but poor.

        But your issue really does not point me to anything that is "inconsistent", but rather some sort of learning curve.

        If you want me to point out the inconsistancy, I would say that they better support editModified() in widgets that are really similar to Tk::Text, for example Tk::Entry, but it does not.

        use Tk; use Tk::Dialog; use warnings; use strict; my $MW = MainWindow->new(-title => "Tk::Text test", -width => 200, -height => 200); my $text = $MW->Entry( -width => 40); $text->pack(-side => 'top', -fill => 'both'); $text->bind( '<FocusOut>' => \&callback); my $text2 = $MW->Text(-height => 10, -width => 40, -wrap => 'word'); $text2->pack(-side => 'top', -fill => 'both'); MainLoop; sub callback { if ($text->editModified()) { $text->Dialog(-title=>"Modified",-text=>"\$text has been modif +ied")->Show();#or whatever you want $text->editModified(0); } }
Re: Using Tk::Text and '<<Modified>>'
by rrwo (Friar) on Nov 20, 2004 at 13:18 UTC

    If you want your routine to be called with every keystroke, then bind every keystroke.

    Your callback was called once when it was modified. Since you now know it's been modified, there's no reason to call it again.

    I don't have the Tk docs with me here, but I recall that you have to reset some kind of flag when modified has been called.

      "If you want your routine to be called with every keystroke, then bind every keystroke."

      That was my first thought too, but that does not seem to work either.

      use warnings; use strict; use Tk; 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, @_) } ); MainLoop; sub getText { my ($t, @args) = @_; my $str = $t->get('0.0', 'end'); print "Got: $str\n"; $text->bind('<<Modified>>' => sub { getText($str, @_) } ); return if(!$str); return 1; }
Re: Using Tk::Text and '<<Modified>>'
by zentara (Cardinal) on Nov 20, 2004 at 19:31 UTC
    Like I said in my first reply, I like to check md5sums. If you want to only call a save routine, if a modification is made, you have those 2 options. One, if the editModified flag is 1, call a save routine upon exiting. Or you can do a get('0.0','end') on loading the file, and do an md5sum on the string and store it away. Then have something like this in you code:
    $SIG{__DIE__} = sub {&save_it(); exit}; $SIG{INT} = sub {&save_it(); exit}; #have your exit button call &save_it too sub save_it{ # do another get('0.0','end') # take an md5sum of the text and compare # it to your original md5sum # save your text if they differ }
    Otherwise you will be auto-saving on every keystroke. You can also work out an auto-save scheme if the text box goes out of focus.

    I'm not really a human, but I play one on earth. flash japh
Re: Using Tk::Text and '<<Modified>>'
by zentara (Cardinal) on Nov 20, 2004 at 10:58 UTC
    I asked a similar question awhile back, and some of the experts had some pointers, but I've yet to fully explore them. Go to http://groups.google.com and search for " Re: Tk:Text "has been changed" flag? ".

    At the time I decided it was easier to just take md5sums of the whole text and compare them. :-)


    I'm not really a human, but I play one on earth. flash japh

      If the OP wants to capture key stroke, then should use KeyRelease etc. If what the OP cares is whether the context has been modified, then:

      use Tk; use Tk::Dialog; use warnings; use strict; 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( '<FocusOut>' => \&callback); my $text2 = $MW->Text(-height => 10, -width => 40, -wrap => 'word'); $text2->pack(-side => 'top', -fill => 'both'); MainLoop; sub callback { if ($text->editModified()) { $text->Dialog(-title=>"Modified",-text=>"\$text has been modif +ied")->Show();#or whatever you want $text->editModified(0); } }