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

Hello monks

This is a basic question regarding calling subrutines with \& ... but I have problems in finding an answer. Given the Perl Tk code below (basically a file "dropper") I want to pass to the subrutine one more variable, in my example the dummy $SomeVariable. How can I achive it? And: where does the $selection in the subrutine comes from (it was not explicitally passed to it)? Thanks in advance to illuminate me.

#!/usr/local/bin/perl -w use Tk; use Tk::DropSite; use strict; use vars qw($top $drop); $top = new MainWindow; $top->Label(-text => "The drop area:")->pack; $drop = $top->Scrolled('Listbox', -scrollbars => "osoe", )->pack; my $SomeVariable="xxx"; $drop->DropSite (-dropcommand => [\&accept_drop, $drop], -droptypes => ($^O eq 'MSWin32' ? 'Win32' : ['KDE', 'XDND', 'Sun']) ); MainLoop; sub accept_drop { my($widget, $selection) = @_; my $filename; eval { if ($^O eq 'MSWin32') { $filename = $widget->SelectionGet(-selection => $selection, 'STRING'); } else { $filename = $widget->SelectionGet(-selection => $selection, 'FILE_NAME'); } }; if (defined $filename) { $widget->insert(0, $filename); } } __END__

Replies are listed 'Best First'.
Re: Calling subroutine with \& and passing variables
by Discipulus (Canon) on Oct 28, 2017 at 18:34 UTC
    Hello IB2017,

    this is covered in the Tk::callbacks basically you can pass any arguments you want as in: -dropcommand => [\&accept_drop, $drop, $another, $more] and they arrive in the sub accept_drop in @_ as always.

    The same can be accomplished with:

    [ \&subname, arg1, arg2, ..] # or [ sub { ... },arg1, arg2, ..]

    Note the [..] around sub and args: if more than one element is passed Tk will expect an anonymous array with the code reference as first element and arguments following.

    Also note that the contents of the [..] are evaluated by perl when the callback is created.

    PS see it in action:

    perl -MTk -e "tkinit->Button(-text=>'print',-command=>[sub{print join'-',@_},1, 2,3])->pack;MainLoop"

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

      Thank you for your suggestions. But I had already tried both your solutions and they did not work. See the following - code which I slighly modified to use your proposed solution \&subname, arg1, arg2, .. . PS: the code should print in the subrutine the variable $SomeVariable.

      #!/usr/local/bin/perl -w use Tk; use Tk::DropSite; use strict; use vars qw($top $drop); $top = new MainWindow; $top->Label(-text => "The drop area:")->pack; $drop = $top->Scrolled('Listbox', -scrollbars => "osoe", )->pack; my $SomeVariable="xxx"; # The variable I want to pass to the subrutine $drop->DropSite (-dropcommand => [\&accept_drop, $drop, $SomeVariable], -droptypes => ($^O eq 'MSWin32' ? 'Win32' : ['KDE', 'XDND', 'Sun']) ); MainLoop; sub accept_drop { my($widget, $selection, $SomeVariable) = @_; print $SomeVariable;#printing the variable passed from the main my $filename; eval { if ($^O eq 'MSWin32') { $filename = $widget->SelectionGet(-selection => $selection, 'STRING'); } else { $filename = $widget->SelectionGet(-selection => $selection, 'FILE_NAME'); } }; if (defined $filename) { $widget->insert(0, $filename); } } __END__

        Of course it works! It was the order of the passed variables that was wrong! For the sake of it, here it is the working code.

        #!/usr/local/bin/perl -w use Tk; use Tk::DropSite; use strict; use vars qw($top $drop); $top = new MainWindow; $top->Label(-text => "The drop area:")->pack; $drop = $top->Scrolled('Listbox', -scrollbars => "osoe", )->pack; my $SomeVariable="xxx"; $drop->DropSite (-dropcommand => [\&accept_drop, $drop, $SomeVariable], -droptypes => ($^O eq 'MSWin32' ? 'Win32' : ['KDE', 'XDND', 'Sun']) ); MainLoop; sub accept_drop { my ($drop, $SomeVariable, $selection) = @_; print "This is my variable passed from the main: $SomeVariable\n"; my $filename; eval { if ($^O eq 'MSWin32') { $filename = $drop->SelectionGet(-selection => $selection, 'STRING'); } else { $filename = $drop->SelectionGet(-selection => $selection, 'FILE_NAME'); } }; if (defined $filename) { $drop->insert(0, $filename); } } __END__
Re: Calling subrutine with \& and passing variables
by LanX (Saint) on Oct 28, 2017 at 18:28 UTC
    This means you are passing a callback, tk decides what is passed.

     (-dropcommand => [\&accept_drop, $drop],

    Depending on what you want to achieve you can

    • hard code it inside the subroutine
    • use a closure variable
    • inspect the tk data structures

    you need to be clearer about your goal.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Thank you for your reply. What I want to achive is actually quite simple: I'd like to pass the variable $SomeVariable to my subrutine to further process it (the code is a simplified version of my real code which is a quite complex GUI with many dynamically generated variables I need to process once the user has "dropped" a file into the widget).

      Hardcoding the variable in the subrutine doesn't seem to me viable as the variable is generated outside of it. Using a closure variable - as far as I understand - implies an "answer" from the subrutine (but I may be wrong about what a closure variable is). Inspecting the tk data structures hasn't helped me, unfortunately.

        Just use $SomeVariable inside your subroutine.

        Because it's declared outside with my it becomes a "closure variable".

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!