http://www.perlmonks.org?node_id=1002083

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

I am having difficulty attempting to destroy widgets I have bound to mouse click events. All the documentation I read seems to indicate the problem is likely related to internal tables maintained for destroyed widgets, however none seem to elaborate on how to clear them, or how to "detach" or "unbind" events from widgets so they may be safely destroyed and re-created from scratch.

The program I am writing reaches a point where it needs to delete all dynamically created "buttons" (widgets with mouse binds on them) and then re-create the first of them by invoking a sub-routine to basically start over. Below is a sample of the code, to show the basic concept

Basically what it does is set up the container structure, then invoke &scan1start; which is a subroutine to build the first fake widget button, and bind it to right and left mouse (not the reset button, leave that alone for now!) I am using canvas as the bind widget because I want to give it a background image without inheriting the functionality of a regular button.

Clicking this first fake widget button button runs the next two subroutines to create two more buttons (and also changes the first button's color). Clicking either of these new buttons will change their color, and right-clicking will change it back. Right-clicking the first button hides the two higher buttons, but only if they are "off" (black in this example.)

You can test all of this and it works fine, until you click the reset button. This button is supposed to destroy all the buttons, then run the initial subroutine to re-create the first button and set up it's initial bindings again, basically starting all over. However, as you can see perl complains about a bound event and the button is never re-created.
#!/usr/bin/perl use warnings; use Tk; use strict; my $mw = new MainWindow; $mw-> geometry('640x480'); $mw-> resizable( 0, 0 ); my $numscan1 = 0; my $numscan2 = 0; my $numscan3 = 0; my $leftframe = $mw-> Frame(); $leftframe->form(-left => '%0', -right => '%25', -bottom => '%99', -to +p => '%1'); my $resetb = $leftframe-> Button(-text => 'Reset', -command => \&reset + )->form(-left => '%50', -top => '%50'); my $rightframe = $mw-> Frame(-relief => 'groove', -borderwidth => 1); $rightframe-> form(-left => $leftframe, -right => '%99', -bottom => '% +99', -top => '%1'); my $maincanvas = $rightframe-> Canvas(-background => 'blue', -highligh +tthickness => 0); $maincanvas-> form(-left => '%1', -right => '%99', -bottom => '%99', - +top => '%1'); #Here are some fake buttons using sub-canvas: my $scan1 = $rightframe-> Canvas(-width => 64, -height => 54, -highlig +htthickness => 0); my $scan2 = $rightframe-> Canvas(-width => 64, -height => 54, -highlig +htthickness => 0); my $scan3 = $rightframe-> Canvas(-width => 64, -height => 54, -highlig +htthickness => 0); &scan1start; MainLoop; sub reset { my @buttons = ($scan1,$scan2,$scan3); foreach (@buttons) { $_-> destroy; } &scan1start; } sub scan1start { if ($numscan1 == 0) { $scan1-> form(-left => '187', -top => '319'); $scan1-> configure(-background => 'black'); $scan1-> Tk::bind('<Button-1>' => sub { $numscan1 = 1; &scan2start; &scan3start; &scan1start; } ); $scan1-> Tk::bind('<Button-3>' => sub { }); } elsif ($numscan1 == 1) { $scan1-> form(-left => '187', -top => '319'); $scan1-> configure(-background => 'white'); $scan1-> Tk::bind('<Button-3>' => sub { if (($numscan2 == 0) && ($numscan3 == 0)) { $scan2->formForget; $scan3->formForget; $numscan1 = 0; &scan1start; } } ); $scan1-> Tk::bind('<Button-1>' => sub { }); } } sub scan2start { if ($numscan2 == 0) { $scan2-> form(-left => '102', -top => '211'); $scan2-> configure(-background => 'black'); $scan2-> Tk::bind('<Button-1>' => sub { $numscan2 = 1; &scan2start; } ); $scan2-> Tk::bind('<Button-3>' => sub { }); } elsif ($numscan2 == 1) { $scan2-> form(-left => '102', -top => '211'); $scan2-> configure(-background => 'white'); $scan2-> Tk::bind('<Button-3>' => sub { $numscan2 = 0; &scan2start; } ); $scan2-> Tk::bind('<Button-1>' => sub { }); } } sub scan3start { if ($numscan3 == 0) { $scan3-> form(-left => '202', -top => '211'); $scan3-> configure(-background => 'black'); $scan3-> Tk::bind('<Button-1>' => sub { $numscan3 = 1; &scan3start; } ); $scan3-> Tk::bind('<Button-3>' => sub { }); } elsif ($numscan3 == 1) { $scan3-> form(-left => '202', -top => '211'); $scan3-> configure(-background => 'white'); $scan3-> Tk::bind('<Button-3>' => sub { $numscan3 = 0; &scan3start; } ); $scan3-> Tk::bind('<Button-1>' => sub { }); } }

Sorry if this seems similar to another question I asked recently about canvas items, however that example was somewhat unclear and presented in a misleading context.

This is specifically an issue with unbinding/destroying bound widgets. I have attempted it with numerous other widget types (buttons, images), inside numerous container types and had the same result with all of them.

I know I am not the most well-versed in the language but this particular snag is really starting to recede my hairline!

EDIT: Just thought I would add, this same "command bound to event" error crops up when I attempt to use ->delete('all') and formForget instead of destroy. Very very frustrating.