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

I want to start a listbox as a subroutine and return the choice to the main program which is otherwise a command line program (don't laugh please).

How do I return the picked value to the main program.

The code below shows the value in the command line window when clicked on but does not display the output of:

print "From top level: selection is $choice\n";

in the command line window when the listbox is closed.

#!/usr/bin/perl use strict; use Tk; my @listbox_items = qw/frodo bilbo rabbits orcs smaug gandolf/; my $choice = &myListBox(@listbox_items); print "From top level: selection is $choice\n"; sub myListBox{ my @listbox_items = @_; my $mw = new MainWindow; $mw->geometry('400x300'); my $lb = $mw->Listbox(-selectmode => "single")->pack( ); $lb->insert('end', @listbox_items); $lb->bind('<Button-1>'); $mw->Button( -text => "Close Window", -command => sub { $mw->withdraw(); return $choice;})->pack; ##double click on item from listbox $lb -> bind('<Double-1>'=> sub { $choice = $_[0]->get($_[0]->curselection), print "in loop selection is $choice\n"; }, ); MainLoop; }

Replies are listed 'Best First'.
Re: perl tk listbox how to return choice to main program
by kcott (Archbishop) on Jun 27, 2013 at 04:49 UTC

    G'day bdalzell,

    I tried the code you posted but it didn't compile. I added a closing brace (}) after MainLoop;: it now compiled but didn't run as you described. I tried a few other things to fix the code you provided but, ultimately, I gave up on it.

    Luckily, I already had a script using Tk::Listbox which wasn't too dissimilar to the look-and-feel of your GUI. I used a temporary file to capture the user selection and forked a process to run the GUI. This seems to provide the functionality you're after. [Portability is potentially an issue: see fork, perlport - fork and perlfork for caveats.]

    #!/usr/bin/env perl use strict; use warnings; use autodie qw{:all}; use Time::HiRes; use Tk; use Tk::LabFrame; print 'Hit enter when ready to make selection: '; (undef) = scalar <>; my $no_selection_msg = 'No item selected'; my $filename = join '.' => Time::HiRes::time, $$, 'tmp'; open my $pre_fork_fh, ">", $filename; print $pre_fork_fh $no_selection_msg; close $pre_fork_fh; my $pid = fork; if ($pid == 0) { my $mw = MainWindow->new; $mw->geometry('200x300+50+50'); my $app_F = $mw->Frame(); $app_F->pack(-padx => 10, -pady => 10, -fill => 'both', -expand => + 1); my $command_LF = $app_F->LabFrame( -label => 'Commands', -labelside => 'acrosstop' )->pack(-side => 'bottom', -fill => 'x', -expand => 0); my $selections_LF = $app_F->LabFrame( -label => 'Selections', -labelside => 'acrosstop' )->pack(-side => 'top', -fill => 'both', -expand => 1); my $selections_Lb = $selections_LF->Scrolled(Listbox => -scrollbars => 'osoe', -selectmode => 'single' )->pack(-padx => 5, -pady => 5, -fill => 'both', -expand => 1); $selections_Lb->insert(end => map { "$_ selected" } 'A' .. 'Z'); my $save_B = $command_LF->Button( -text => 'Save Selection', -command => sub { my $selection_index = $selections_Lb->curselection(); my $selection = length $selection_index ? $selections_Lb->get($selection_index) : $no_selection_msg; open my $child_fork_fh, ">", $filename; print $child_fork_fh $selection; close $child_fork_fh; exit; } )->pack(-padx => 5, -pady => 5); MainLoop; exit; } waitpid $pid => 0; open my $post_fork_fh, "<", $filename; my $selection = <$post_fork_fh>; close $post_fork_fh; unlink $filename; print "Selection made: $selection\n";

    When you run this, you'll start with a commandline prompt and no GUI. Hitting <Return> brings up the GUI and allows a selection to be made. Once selected, the GUI shuts down and you're back to the commandline with the selection displayed. It should be reasonably idiot-proof inasmuch as it cleanly handles users not making a selection or finding interesting ways to kill the GUI without actually interacting with it.

    -- Ken

Re: perl tk listbox how to return choice to main program
by Anonymous Monk on Jun 27, 2013 at 00:11 UTC

      Thank you so much. I always think one should post the working solution once found so I have posted the working script below. I am running Xubuntu 12.04 - a Linux system

      AnonyMonk's post at first seemed too minimalistic but it actually led me finally to the solution. Here is a working script that runs from the command line and opens a listbox that is contained in a subroutine, allows one to select an item and closes the listbox when selection is done with a double click returning the id of the selected item to the command line program where further actions can use the information.

      #!/usr/bin/perl use strict; use Tk; my @listbox_items = qw/frodo bilbo rabbits orcs smaug gandolf/; my $choice = &myListBox(@listbox_items); print "TOP: selection is $choice\n"; sub myListBox{ my $choice; my @listbox_items = @_; my $mw = MainWindow->new; $mw->geometry('400x300'); $mw->Label(-text => "Click once to select item")->pack(); $mw->Label(-text => "Double-click selects item and closes window")- +>pack(); my $lb = $mw->Listbox(-selectmode => "single", -exportselection =>1,)->pack( ); $lb->insert('end', @listbox_items); $lb -> bind('<Button-1>'=> sub { $choice = $_[0]->get($_[0]->curselection), print "in loop selection is $choice\n";}, ); $lb -> bind('<Double-Button-1>'=> sub {$mw->destroy();} ); MainLoop; return $choice; }#endsub

      One of my errors with the intitial script was not being aware of how to distinguish between single click and double click. (sigh).

      I am also very grateful to those who created the thread: Tk event bindings - single vs. double click at:

      node 351793