in reply to Re: run command every x seconds
in thread run command every x seconds

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.

Replies are listed 'Best First'.
Re^3: run command every x seconds
by kcott (Archbishop) on Feb 28, 2014 at 10:33 UTC

    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