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

is it possible to run a command every x seconds? Lets say i wanted to watch for new lines in a file and if there is a new line, parse the line for a specific word and add that word to a button on a tk UI? Continue to do so as long as new lines are added tot hat file?

Replies are listed 'Best First'.
Re: run command every x seconds
by graff (Chancellor) on Feb 27, 2014 at 04:03 UTC
    while (1) { # do something, then… sleep( $x ); # maybe do something else... }
    In the context of a Tk GUI, you probably want to check out something called "Tk::after" (I'm not sure how to link to an online man page for that, but if you have Perl Tk installed, just run "perldoc Tk::after")

      Thanks this looks very promising. I am having a look now. I'd rather use metacpan than perldoc. https://metacpan.org/pod/Tk::after

        Dude, web sites of man pages are like processed sugar (and they vanish when connectivity goes away).

        Perldoc is salt of the earth, mother's milk, Popeye's Spinach, manna from heaven, food of the gods, chicken soup for the programmer's soul, Breakfast of Champions, … and it's ready to serve your reading pleasure pretty much whenever and wherever perl itself can run. You gotta love it.

Re: run command every x seconds
by kcott (Archbishop) on Feb 27, 2014 at 07:25 UTC

      I wasn't going to use sleep in a while loop anyway. Someone in a perl group on Facebook suggested, POE. But I was looking at Tk::After then had to stop because something came up. But I thinks that where I'm gonna start. I had a look at POE and it looked like Tk::After was an easier approach!

      I'd like to clarify that what I am doing is using hcitool con to check for connected Bluetooth devices and parse out the word handle with its corrisponding number. I run the command and push the output to a file. Then parse the handle number and add it to a button. Depending on how many connected devices there are is how many buttons I want to be displayed on my UI.

      The only problem is that if I start the program and only one device is connected, it'll show one device. If I connect another device, the program won't load the button. This is why I want to run a command to dynamically load buttons.

        Here's a working (albeit barebones) example to show the techniques you'll probably need. (See Notes at the end.)

        #!/usr/bin/env perl use strict; use warnings; use autodie; use Tk; use Tk::Pane; my $mw = MainWindow->new(); $mw->geometry('200x250+50+50'); my $connection_file = './pm_tk_dynamic_buttons_connection.txt'; my $action_F = $mw->Frame()->pack(-side => 'bottom'); $action_F->Button( -text => 'Change Connections', -command => [\&change_connections, \$connection_file], )->pack; $action_F->Button(-text => 'Exit', -command => sub { exit })->pack; my $pane_F = $mw->Frame()->pack(-fill => 'both', -expand => 1); $pane_F->pack(-fill => 'both', -expand => 1); my $button_P = $pane_F->Scrolled('Pane', -scrollbars => 'osoe', -sticky => 'nsew' )->pack(-fill => 'both', -expand => 1); my $text_F = $mw->Frame()->pack(-fill => 'both', -expand => 1); my $pressed_T = $text_F->Scrolled('Text', -scrollbars => 'osoe', -wrap => 'none', -height => 10 )->pack(-fill => 'both', -expand => 1); my $delay = 100; $mw->repeat($delay, [\&check_connections, \$connection_file, \$button_P, \$pressed_T] ); MainLoop; { my %connect_button_for; my $last_mod; sub check_connections { my ($file_ref, $but_win_ref, $out_win_ref) = @_; if (-e $$file_ref) { my $cur_mod = -M $$file_ref; return if defined $last_mod && $cur_mod == $last_mod; $last_mod = $cur_mod; $_->destroy for values %connect_button_for; %connect_button_for = (); open my $fh, '<', $$file_ref; /handle=(\d+)/ and ++$connect_button_for{$1} while <$fh>; close $fh; for my $handle (sort keys %connect_button_for) { $connect_button_for{$handle} = $$but_win_ref->Button( -text => "Handle $handle", -command => sub { $$out_win_ref->insert( end => "Clicked 'Handle $handle' button.\n +" ); $$out_win_ref->yview('end'); }, )->pack; } } } } BEGIN { my @all_connections = ( ['>' => 1], ['>>' => 2, 3], ['>' => 2, 4], ['>>' => 5, 3], ['>'], ); my $connection_index = -1; sub change_connections { my ($file_ref) = @_; my $index = ++$connection_index % @all_connections; my @connectons = @{$all_connections[$index]}; my $mode = shift @connectons; open my $fh, $mode, $$file_ref; print $fh "... handle=$_ ...\n" for @connectons; close $fh; } } END { unlink $connection_file }

        Notes

        • I don't have hcitool nor do I know what its output looks like: I've just used some dummy data ("... hanhdle=number ...") for test purposes.
        • The BEGIN and END blocks are just for testing. You may want something similar if you want to update the hcitool output manually from the GUI.
        • I've set the delay for check_connections() to 100ms. Set this to whatever you want but bear in mind that the human eye is unlikely to register more than about 25 changes per seconds; setting this to less than 40ms will be pointless from a visual perspective and wasteful from a processing perspective.
        • When the hcitool output changes, all existing buttons are destroyed and a new set is created. If you have data associated with the buttons, you may want to change this such that buttons are only destroyed if they no longer appear in the updated output.
        • Scrollbars will automatically appear in the Tk::Pane ($button_P) when they are needed. The same is true for the Tk::Text ($pressed_T); although, I only added that for demo purposes.
        • I didn't need Tk::fileevent in this instance; however, if you're appending data to your output file, this may be appropriate. Note that my tests both wrote data to a newly created output file and appended data to an existing file: the logic I've shown may suffice for your needs.
        • I think everything else should be reasonably straightforward but do ask if there's something you don't understand.

        Update: I had a misplaced closing brace in check_connections() (putting the for loop outside the if (-e $$file_ref) block — it should've been inside that block). This didn't affect how the routine currently functioned but may have introduced a weird bug if subsequent changes were made. Fixed and successfully retested.

        -- Ken

Re: run command every x seconds
by LanX (Saint) on Feb 27, 2014 at 03:58 UTC
    See sleep for delaying.

    But if I were you I'd have a look at tail -f and it's pure Perl equivalents.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    update

    Did you update the TK part of your question?

Re: run command every x seconds
by basiliscos (Pilgrim) on Feb 27, 2014 at 09:51 UTC

    If you are using *nix, then you can white your watching-logic as usual perl program, and then launch it via :

    watch my-script.pl my-arg1 my-arg2

    By default, watch re-launches the program every 2 seconds, and clears the screen.