You're currently just pushing to an array when the checkbox is clicked, no matter whether the click checks or unchecks it. Instead of doing that, I'd suggest toggling a value in array, say $checkbox[$i] for the $i-th checkbox; set it to the checkbox's text when the box is selected, and undef otherwise.
Then when the "Connect" button is clicked and you want to ensure that exactly one checkbox has been selected, use grep to count how many elements of said array are defined.
Here's an example (I've also taken the liberty of formatting your script in a slightly more readable manner):
#!/usr/bin/perl
use strict;
use warnings;
use feature qw/say/;
#use diagnostics;
use Tk;
use Tk::Checkbox;
my $mw = MainWindow->new;
$mw->geometry("150x600");
$mw->title("ConMan");
# open my $FILE, '<', "ESSID", or die "Can't open file: $!";
# my @lines = <$FILE>;
my @lines = <DATA>;
my @checkbox;
my $i = 0;
foreach my $n (@lines) {
chomp $n;
my $number = $i++; # private copy for the anonymous sub below
my $checkButton = $mw->Checkbutton(
-text => "$n",
-onvalue => 1,
-offvalue => 0,
-command => sub {
$checkbox[$number] = defined $checkbox[$number] ? undef :
+$n;
},
)->pack(
-side => 'top',
-anchor => 'nw'
);
}
my $connButton = $mw->Button(
-text => "Connect",
-command => sub {
my @networks = grep { defined } @checkbox;
if(@networks == 1) {
say $networks[0];
} else {
say "Select one network, please.";
}
}
)->pack(
-side => 'left',
-anchor => 'sw',
-padx => '5',
-pady => '5'
);
my $CancelButtons = $mw->Button(
-text => "Close",
-command => sub {
say "Closing out ConnMan.";
$mw->withdraw;
exit 0;
}
)->pack(
-side => 'right',
-anchor => 'se',
-padx => '5',
-pady => '5'
);
MainLoop;
__DATA__
line1
line2
line3
line4
line5
| [reply] [d/l] |
Thank you AppleFritter.
Where exactly did I go wrong? Could you explain please? I had a look at the doc and seen that deselect would deselect the checkbuttons but I tried and no luck. I guess I was selecting the wrong element to reference.
I see what made the difference was the button sub.
my $connButton = $mw->Button(
-text => "Connect",
-command => sub {
my @networks = grep { defined } @checkbox;
if(@networks == 1) {
say $networks[0];
} else {
say "Select one network, please.";
}
}
I didn't post with the original code, at least I dont think but here was my version of the checkbutton function. Essentially the same as yours.
open my $FILE, '<', "ESSID", or die "Can't open file: $!";
my @lines = <$FILE>;
my @Sel;
my $i = 0;
my @Selected;
my $checkButton;
foreach my $n ( @lines ) {
chomp $n;
$checkButton = $mw->Checkbutton( -text => "$n",
-onvalue => 1,
-offvalue => 0,
-command => sub{push
+(@Sel, $n)} && sub{ $Selected[$i] = defined $Selected[$i] ? undef : $
+n;},
-variable => \$Selec
+ted[$i], )->pack( -side => 'top',
+ -anchor => 'nw' );
$i=$i+1;
}
| [reply] [d/l] [select] |
Where you mostly went wrong, I think, was in only pushing to @Sel and not popping from it. In other words, if you clicked on a checkbox, you'd push its value to @Sel; and if you clicked it again to deselect it, you'd just push again. The list would grow longer and longer, and things would not work as intended.
Meanwhile, this:
-command => sub{push(@Sel, $n)} && sub{ $Selected[$i] = defined $Selec
+ted[$i] ? undef : $n;}
most likely will not do what you may think; although you can use the && operator on anonymous subroutine references (they're scalars, after all), they'll always be a true value, and the operator will return the last value evaluated, in this case the second reference (see C style Logical And in perlop). So the above line is really equivalent to
-command => sub{ $Selected[$i] = defined $Selected[$i] ? undef : $n;}
Now, this IS fairly close to what I wrote, of course, but:
- You're not pushing to @Sel at all anymore, so the check if it's got one element on it will always fail.
- Your anonymous subroutines use the $i you declared outside the loop - and $i is 5 when the loop is done, so all your checkboxes will operate on $Selected[5].
I got around the latter by introducing a new variable inside the loop that holds the current value of $i; this goes out out of scope at the end of the loop body, so in the next loop iteration, a new variable with the same name is created, and each anonymous subroutine sees a different variable, one that keeps the right value of $i. (It's worth noting that even though the variables go out of scope, their duration does not end, as they're still visible to the anonymous subroutines.)
This is perhaps a little difficult to wrap your head around at first. For more information on scope and duration, I'd recommend Mark Dominus's Higher Order Perl (available as a free PDF here, or a dead-tree edition here), pp. 71-76.
| [reply] [d/l] [select] |
Here is a simpler example.
#!/usr/bin/perl
use Tk;
use strict;
use warnings;
my $mw = new Tk::MainWindow;
my @array = qw(bannana boss ape);
my $checklist = 'List will go here';
$mw->fontCreate('big',
-weight=>'bold',
-size=> 24
);
foreach my $item (@array)
{
$mw->Checkbutton(
-font => 'big',
-bg => 'white',
-text=> $item,
-offvalue=>'',
-onvalue=>$item,
-variable=>\$item,
-command=>sub{$checklist="List: @array"})
->pack(-anchor=>'w');
}
$mw->Label(-font => 'big',
-textvariable=>\$checklist)->pack(-side=>'left');
MainLoop;
| [reply] [d/l] |