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

I have the following snippet of code to a larger Tk GUI application I'm working on enhancing. This particular code here generates a widget, accepts a button click and from there gives you a browse entry to pick an option from. Problems arise because when you hit the button many times it generates as many browse entry widgets which I don't want.
#!/usr/local/bin/perl use strict; use warnings; use Tk; require Tk::BrowseEntry; ######################### #Hitting the button casuses many instances of listBrowse widget ######################## my $main= MainWindow->new(); my $listButton=$main->Button( -text=> 'list', -height=> 2, -width=> 10, -relief=> 'ridge', -command=> \&listBrowse, )->pack(); my $choice="number"; #the default list item. sub listBrowse{ my $listTag=$main->BrowseEntry( -variable=>\$choice, -browsecmd=>\&Verify )->pack(); $listTag->insert("end","Number"); $listTag->insert("end","Bullet"); } MainLoop;

The button method of Tk doesn't have an option like "-variable" that can make me control this behavior in a better way. Though, I suspect an option like "-default" can be handy, I couldn't get hold of documentation for it when I checked around. So I've tried to manually introduce a flag which changes value when the button is pushed, I couldn't figure out how to implement this flag into some useful logic that could allow the list button to be hit only once. Ergo my reversion to the revered panel of Monks for guidance on how to be able to hit a button and activate the consequent action only once and not more than once.

UPDATE: I've used the tip provided by lamprecht and could extend on it to re-enable the disabled button and refresh the entire widget anew. SuicideJunkie, the second paragraph from your post was an inspiration. I was trying to use the flag the same way and introducing the same extra scope before I posted this question and it couldn't work in this block though another part of the application used a flag effectively. Thank ya'all.

#the steps in the refresh were to enable a disabled widget and destroy + a created #widget, these two behaviors can be kept under a button to + work simultaneously $listButton->configure(-state=>'normal'); $listTag->destroy if Tk::Exists($listTag);


Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.

Replies are listed 'Best First'.
Re: better control of Tk::Button
by lamprecht (Friar) on Oct 07, 2009 at 13:54 UTC
    Hi,

    You can disable the Button from inside the -command callback like so:

    use strict; use warnings; use Tk; require Tk::BrowseEntry; ######################### #Hitting the button casuses many instances of listBrowse widget ######################## my $listButton; my $main= MainWindow->new(); $listButton=$main->Button( -text=> 'list', -height=> 2, -width=> 10, -relief=> 'ridge', -command=> sub{listBrowse(); $listButton->configure(-state => 'disabled'); }, )->pack(); my $choice="number"; #the default list item. sub listBrowse{ my $listTag=$main->BrowseEntry( -variable=>\$choice, -browsecmd=>\&Verify )->pack(); $listTag->insert("end","Number"); $listTag->insert("end","Bullet"); } MainLoop;

    Cheers, Christoph

      One problem with doing the disable alone is that it does not completely prevent multiple click events from occurring. If the app is busy, a second click could be done before the first click is handled and the button is disabled. Scripts such as Autohotkey for windows can also send the events despite the disabled-ness of the button.

      The better way to go about this, is to check whether there is already a listBrowse widget in existence. If one already exists, you can destroy the old one and replace it, or skip the creation of a new one, whichever makes the most sense.

      As for storing the flag as in the OP's original plan, all you should need is a variable that is scoped to cover the right region:

      ... { # Extra scope to keep $menuPopped contained to just this region. my $menuPopped = 0; sub thatReadsFlag { ... if ($menuPopped) {} ... } sub thatWritesFlag { ... $menuPopped = shift; ... } } ...

        'One problem with doing the disable alone is that it does not completely prevent multiple click events from occurring. If the app is busy, a second click could be done before the first click is handled and the button is disabled.'

        I could have sworn, that this second click would be discarded by the Button instance, unless you insert an update() call in between the listBrowse() call and the setting of disabled state. And I'm still sure that this is true for Button-Click events. However there is a bug in Tk::Buttons implementation of the '<Return>' event handler Invoke() (note the capitalization) which can lead to a race condition where -state is not handled correctly in case of multiple '<Return'> events in the queue.
        So +++ to your reply.


        Cheers Christoph

        update: https://rt.cpan.org/Ticket/Display.html?id=50330