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

I am working on a Tk application and ran into a problem with a "missing" <FocusOut> event. I have the main window with some buttons and menu and a frame with an grid of 9 x 9 text widgets. On the text widgets, I have bound the arrows to force the focus to move to different grid cell. So far, so good.

I also bind the <FocusOut> event from the text class to post process any changes that occurred in that text cell. Each cell needs to be processed as soon as the change is made (actually when you leave the cell). It works fine except if you change a text cell then press a button or menu selection, there is not a <FocusOut> event. Moving around with the arrows or using the mouse to change cells works fine.

I came up with a kludge fix, but I wonder if there might be a better way:

my $current_focus = 0; #widget that is currently in focus ... #main window bindings $main_window->bind( '<ButtonPress>'=> \&button_press); ... #text widget class bindings (9 x 9 = 81 text widgets) $text_widget->bind($text_class, '<FocusOut>'=> \&focus_out); $text_ +widget->bind($text_class, '<FocusIn>' => \&focus_in); ... #get focus on a cell - save the widget to be processed by button_press + later if a button is pressed sub focus_in { my($widget) = @_; $current_focus = $widget; #stash it away } #generate a fake focus_out on a cell when a button is pressed sub button_press { if ($current_focus ) { focus_out($current_focus); #fake a missing <FocusOut> event } } #lose focus on a cell - update master data and redraw cell/screen sub focus_out { my($widget) = @_; my $e = $widget->XEvent; # get event object # do stuff here... $current_focus = 0; #there is no current focus right now. <Foc +usIn> will create a new one... return 0; }

This works, but there could be problems with it long term if other event situations come up. Any other ideas?

Also is there any good documentation of the inner working of Tk, especially dealing with events? I have "mastering perl/Tk" and most of the O'Reilly books, but they don't go deep enough. Thanks. -Eugene

Replies are listed 'Best First'.
Re: Dealing with missing TK FocusOut event
by zentara (Cardinal) on Dec 09, 2006 at 12:12 UTC
    Hi, this sounds like the type of question only Slaven Rezic can answer :-). From your description, I think "eventGenerate" may be of use to you.
    #!/usr/bin/perl use warnings; use strict; use Tk; my $mw = new Tk::MainWindow; my $btn = $mw->Button(-text => 'Hit Return', -command=> sub{ print 'Used Button' } )->pack(); my $btn_eg = $mw->Button(-text => 'Generate Return', -command=> sub{ $mw->eventGenerate('<Return>'); } )->pack(); # # Note that we *usually* bind to the main window, # not the button - it's a focus thing. # $mw->bind('<Return>', \&_on_rtn); MainLoop; exit(0); sub _on_rtn { $mw->messageBox( -message=>'Thanx for using the keyboard!'); }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Dealing with missing TK FocusOut event
by zentara (Cardinal) on Dec 09, 2006 at 12:44 UTC
    Check out Using Tk::Text and '<<Modified>>'. Some of the pitfalls of using <<Modified>> are discussed. I did this once before and ended up using md5sums to detect changes. You can bind to <Enter> and <Leave> on widgets, and do md5sums for comparison.

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Dealing with missing TK FocusOut event
by Anonymous Monk on Dec 09, 2006 at 12:09 UTC
Re: Dealing with missing TK FocusOut event
by NatureFocus (Scribe) on Dec 10, 2006 at 02:35 UTC

    I researched out  <<Modified>> and did some tests, but it does not quite do what I need. It will detect if a change is made to the text field at the instant the change is made, and generate a  <<Modified>> virtual event, but if several changes are made to the same field, no other events are generated. For example if I type "123" in the text field, the event occurs at the "1", but I get no events at the "3". I need an event when the user is leaving the text field so I can make several things happen. The $widget->editModify() flag return the correct count of changes, but only one event. I tried  $widget->editModify(0); to clear the flag in the callback fuction, but it got into an infinite loop and crashed.

    The problem with generateEvent() is that it would have to be attached to every way that the user could leave the grid of edit fields, which is the same problem with my kludge approach above. It would work, but way down the road if a change is made it could introduce a weird intermittent bug. I was hoping for a generic event that occurs on every widget on either exit (lose focus) or activate that I could bind to.

    Maybe I do not have a problem? Does ButtonPress event cover every way out of my "grid of edits"? I know it covers buttons and menus. Is there any other "action" the user can do that would not be covered?

    Thanks for the input. -Eugene