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

Hi monks,

I have spent my time for the past weeks to read through the materials available on web, and below is the code that I comes out, applying Tk::ExecuteCommand module. From my end, I'm able to execute the command successfully if I keyed-in a single liner command within the entry created from the module. However in real case, I'll need to perform a series of tasks within a subroutine, which in my case I would like the button click to perform the jobs defined in my subroutine with the result displayed on the window. Pls help me on this.

Below is my code but it is fail to call the subroutine to perform, can someone point me where are my errors. Thank you.

#!/usr/bin/perl use Tk; use Tk::ExecuteCommand; my $mw = MainWindow->new; my $param1 = "word1" ; my $param2 = "word2" ; #my $cmd = `&test($param1, $param2)` ;#Subroutine to be called by the +Tk::ExecuteCommand button click my $cmd = sub { my $commandline= "/nfs/work/testing123.pl -text text"; system($commandline) == 0 or warn "Couldn't launch [$commandline]: $!/$?"; #system ("/nfs/work/testing123.pl -text text" &") ; }; $ec = $mw->ExecuteCommand( -command => '', -entryWidth => 50, -height => 10, -label => '', -text => 'Execute', )->pack; $ec->configure(-command => $cmd); $ec->update; MainLoop; sub test {#Subroutine that create a text file to be used in later exte +rnal Perl script. my ($param1, $param2) = @_ ; my $param1_val = $param1->get ; my $param2_val = $param2->get ; open (TEXT, ">text") ; print TEXT "Field1: $param1\n" ; print TEXT "Field2: $param2\n" ; close TEXT ; open (PROJ, '-|', 'perl /nfs/work/testing123.pl -text text') or di +e "unable to start $!"; my $first_line = "Start program...please wait"; $ec->insert( 'end', $first_line ); my $proj_line; while (defined ($proj_line =<PROJ>) ){ $ec->insert( 'end', $proj_line ); $ec->update(); } } ---end-----

Replies are listed 'Best First'.
Re: How to perform a subroutine run in cpan Tk::ExecuteCommand module
by zentara (Cardinal) on Jun 25, 2014 at 09:25 UTC
    I'll need to perform a series of tasks within a subroutine,

    It seems you are trying to make Tk::ExecuteCommand do more than what it was intended for. The easiest way to solve your problem, would be to put all the commands you wish to run, in a separate shell(bash) script, then use ExecuteCommand to execute the shell script. You could even put it all into a second Perl script, if it was easier.

    The other alternative is to roll-your-own Tk execute script, for example:

    #!/usr/bin/perl use warnings; use strict; use Tk; use IPC::Open3; require Tk::ROText; $|=1; my $mw = new MainWindow; my $entry=$mw->Entry(-width => 80)->pack; $mw->Button(-text => 'Execute', -command => \&send_to_shell)->pack; my $textwin =$mw->Scrolled('ROText', -width => 80, -bg =>'white', -height => 24, )->pack; $textwin->tagConfigure( 'err', -foreground => 'red' ); my $pid = open3( \*IN, \*OUT, \*ERR, '/bin/bash' ) or warn "$!\n"; $mw->fileevent( \*OUT, readable => \&read_stdout ); $mw->fileevent( \*ERR, readable => \&read_stderr ); $entry->bind('<Return>',[\&send_to_shell]); $entry->focus; MainLoop; sub read_stdout { if( sysread( OUT, my $buffer, 1024 ) > 0 ){ $textwin->insert( 'end', $buffer ); $textwin->see('end'); } } sub read_stderr { if( sysread(ERR, my $buffer, 1024 ) > 0 ){ $textwin->insert( 'end', $buffer, 'err' ); $textwin->see('end'); } } sub send_to_shell { my $cmd= $entry->get(); print IN "$cmd\n"; }
    You can modify this code to do what you need or want. Instead of getting your commands from the Entry, you could read it from a subroutine.

    Post a fully working example if you need more help.

    P.S.
    Tk::ExecuteCommand could be used as you desire, by using configure to load different commands from your subroutine.

    # in your subroutine $ec->configure( -command => $choice ); $ec->execute_command;

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh

      Thank you Zentara, I have read similar code as you posted and tried the code before I posted this question. The script work fine except for 2 things:-

      i.The button freeze once it is hitted, and there is no way to kill the ongoing job.

      ii.The entry seem only capture first single word from the command line to show in the text widget, I need the whole command line to be prompt.

      Is there any solution fto fix above 2 problems, I don't mind switching to this method if they can achieve my 2 objectives below:-

      i. To show the state of the command within the text widget.

      ii. There is a way to cancel the ongoing process.

      I chosen Tk::ExecuteCommand as it is non-blocking and the button is tactfully turn to "cancel" when a job is running so that user could have chance to cancel an ongoing job.

        To add on, is there a way to call subroutine in Tk::ExecuteCOmmand instead of calling a single liner command?

        To add on, is there a way to call subroutine in Tk::ExecuteCOmmand instead of calling a single liner command?

      Hi Zentara, I tried to apply your hint of can use the configure command every where finally enable my output result shown on the text widget. Thank you! Though there's seem to have the extra entry and button in the form, but it at least solve my problem on showing test in the widget. However is there a way to print plain font to the text widget in Tk::ExecuteCommand say if I don't want to set a couple of mini scripts for simple info message? I tried to print this to the text widget, but it execute like treating it as a script, complaining missing file or directory but it is in fact it is just a pure info text to user.

      My code as below

      my $first_line = "Start running project 1...please wait"; $ec_pc->configure(-command=> $first_line); $ec_pc->execute_command; $ec_pc->update;

      And, below is what is shown within the test widget:

      'Start running project 1...please wait' : No such file or directory
        is there a way to print plain font to the text widget in Tk::ExecuteCommand say if I don't want to set a couple of mini scripts for simple info message? ....
        'Start running project 1...please wait' : No such file or directory

        You have a serious misunderstanding of what is going on. Tk::ExecuteCommand is designed to do just that .... execute a command , which means sending a valid command to a shell to be run. What you want to do is just plainly run some perl code and have it's output in the Text widget.

        For the question of killing a running command, make another button labeled 'Stop', and use Tk::ExecuteCommand's $exec->kill_command;

        my $kill_button = $mw->Button( -text => 'Stop', -command=> sub{ $exec->kill_command; } )->pack();

        As to your other problem of sending plain text messages to the Text Widget, instead of a command, you will need to figure out a way in your subroutine of determining if you have a valid command, or just an informational message. If it's an information message, you can write it directly into the ROText subwidget like the following:

        #!/usr/bin/perl use Tk; use Tk::ExecuteCommand; my $mw = MainWindow->new; $ec = $mw->ExecuteCommand( -command => '', -entryWidth => 50, -height => 10, -label => '', -text => 'Execute', )->pack; $ec->configure( -command => 'dir' ); $ec->execute_command; $ec->bell; $ec->update; # read perldoc Tk::ExecuteCommand for the Advertised Widget section my $ROText = $ec->Subwidget('text'); print "$ROText\n"; $ROText->configure(-bg=>'white'); $ROText->insert('end', "\n\nHi, this is a message\n\n"); $ROText->see('end'); #execute some perl code and output it to the text widget &do_something; MainLoop; sub do_something{ # the Tk::ExecuteCommand kill button won't stop this for (1..5){ $ROText->insert('end', "$_\n"); $ROText->see('end'); } }

        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: How to perform a subroutine run in cpan Tk::ExecuteCommand module
by Corion (Patriarch) on Jun 25, 2014 at 07:37 UTC
    my $cmd = `&test($param1, $param2)` ;#Subroutine to be called by the T +k::ExecuteCommand button click

    Backticks are not how you store a subroutine to be called later. See qx on what backticks actually do.

    Did you try printing $cmd to find out what is actually contained in $cmd.

    Most likely, you want to store a subroutine reference in $cmd:

    my $cmd= sub { test($param1, $param2); };

      Thanks for your reply. I'm newbie, and yes, I tried to print out the $cmd, it print out nothing. Is that possible to call subroutine from the $ec->configure(-command => ) line?

      I wonder how people do if they want to execute external program which read wrapper (text in my case) with this module, as for now my end it only works with single liner command.

        Calling external programs is done by system or qx/backticks.

        If you want to call an external program when a button is clicked, put that call to the external program in your callback:

        my $cmd= sub { my $commandline= "my/external/program --foo=param1 --bar=param2"; system($commandline) == 0 or warn "Couldn't launch [$commandline]: $!/$?"; };