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

Dear Monks, I am trying to make a listbox with a scroll bar that returns only one value back to the main function. This value will further be used in a different part of the program. 1) My script below is not able to return a value and store it $choice 2) How can I modify this script to allow only one item to be selected. i.e When the user selects one option and hits view, the choice should be returned to the main function and the listbox should exit. Any help would be highly appreciated. Thanks!
#!/usr/bin/perl use strict; use warnings; use Tk; my @choices = qw/Apple Ball Cat Dog Elephant Fish Goat House Ink Jug K +ite/; my $choice = &myListBox(@choices); print "Selection is $choice\n"; sub myListBox{ my $choice; my @listbox_items = @_; my $mw = MainWindow->new; $mw->title("Listbox"); my $lb = $mw->Scrolled("Listbox", -scrollbars => "osoe", -height => 10, -width => 30, -selectmode => "single"); my $real_lb = $lb->Subwidget('scrolled'); $real_lb->configure(-borderwidth=>0); $lb->insert("end", @listbox_items); $lb->pack(-side => "left"); $mw->Button(-text => "Exit", -command => sub{exit; })->pack(-side => "bottom"); $mw->Button(-text=>"View", -command => sub { $choice= $lb->get('active'); my $w = $real_lb->width; my $sample = ' ' x 10; my $font_len = $lb->fontMeasure('default', $sample ); } )->pack(-side => "bottom"); $lb->selectionSet('end'); $lb->see('end'); MainLoop; return $choice; }

Replies are listed 'Best First'.
Re: Returning a value from a Perl Listbox
by beech (Parson) on Dec 11, 2015 at 00:30 UTC
Re: Returning a value from a Perl Listbox
by kcott (Archbishop) on Dec 13, 2015 at 00:56 UTC

    G'day Ppeoc,

    The general rule (not one that's written down, as far as I know, but rather something that's served me very well for many years) is to pass a reference to a variable then modify the dereferenced value within the GUI code.

    Here's a quick & dirty example (no niceties like titles, geometry, colours, borders, etc.) to demonstrate this:

    #!/usr/bin/env perl -l use strict; use warnings; use Tk; my $mw = MainWindow::->new(); my @choices = qw{A B C D}; my $chosen; my $tl; $mw->Button(-text => 'Make Choice', -command => sub { make_choice(\$mw, \$tl, \@choices, \$chosen); })->pack(); $mw->Button(-text => 'Show Choice', -command => sub { if (defined $chosen) { print "You chose: $chosen"; } else { print "Nothing chosen yet!"; } })->pack(); $mw->Button(-text => 'Exit', -command => sub { exit })->pack(); MainLoop; sub make_choice { my ($parent, $tl, $choices, $chosen) = @_; if (! Exists($$tl)) { $$tl = $$parent->Toplevel(); my $lb = $$tl->Scrolled('Listbox', -selectmode => 'single', -scrollbars => 'osoe', )->pack(); $lb->insert(end => @$choices); $$tl->Button(-text => 'Done', -command => sub { my $selection = $lb->curselection(); if (ref $selection) { $$chosen = $choices->[$selection->[0]]; } $$tl->grabRelease(); $$tl->withdraw(); })->pack(); } else { $$tl->deiconify(); $$tl->raise(); } $$tl->grab(); }

    Note that the make_choice(...) call only has references as arguments; accordingly, sub make_choice {...} is totally independent of any global variables. You could call it with a completely different set of variables and, whatever equates to $chosen in the calling code is modified by $$chosen = ... in &make_choice: there's no need to return $chosen as it's already updated with the new value in the calling code.

    As a quick example, clicking on "Show Choice", "Make Choice", "B", "Done", "Show Choice", "Exit" gives this output:

    Nothing chosen yet! You chose: B

    Now, I wasn't entirely sure from your description, but I think you want your program to do some non-GUI stuff, then some GUI stuff, then more non-GUI stuff. I achieved that by keeping &make_choice as is and wrapping nearly everything before it in a subroutine (&run_gui) with these minor changes:

    sub run_gui { my $choice = shift; ... ... sub { $mw->destroy } ... # instead of sub { exit } ... $$choice = $chosen; }

    And adding a call to run_gui(\$choice); (with a few print statements to indicate what's happening). Repeating the interaction shown in the earlier "quick example", gives this output:

    Do something before GUI. Hit <Enter> to start GUI: Nothing chosen yet! You chose: B Your choice: B Do something after GUI.

    Here's the new script in its entirety (in the spoiler):

    You can probably gain a greater understanding of what's happening here by searching for "closure" in perlref and perlsub. Also, in case you haven't already seen it, take a look at Tk::callbacks.

    — Ken