Pop Up in perl that goes away automatically after pause - perl

I'm writing a script to assist people who'll scan a barcode and get a response to keep or dispose the scanned sample. I want to have a message, similar to tk's messagebox or Win32::MsgBox but one that requires no user interaction to go away after three seconds.

My thought was to create the messages in a child process, using alarm to kill the process after a delay. In Tk:
sub tmpMsgBox {
my ($message,$delay) = #_;
if (fork() == 0) {
my $topWin = MainWindow->new;
my $label = $topWin->Label();
my $ok = $topWin->Button();
$label->pack(-side => 'top');
$ok->pack(-side => 'bottom');
$label->configure(-text => $message);
$ok->configure(-text => 'Ok', -command => sub {exit});
$SIG{ALRM} = sub {exit};
alarm $delay || 1;
$topWin->MainLoop;
}
}
for (3..10) {
tmpMsgBox("This window will disappear in $_ seconds", $_);
}
I don't think Tk plays nicely with fork, though, so this idea probably won't work so well if you are also using Tk in your main process.

Desktop::Notify is the standard-compliant interface to the desktop's passive notification pop-ups.
perl -MDesktop::Notify -e'
Desktop::Notify
->new
->create(
body => q{why hello there},
timeout => 3000
)->show'

What you want to do is to send a destroy message to the window after your timeout (remembering to cancel the sending of the message if the user does choose something!) Tk's certainly capable of doing this.
# Make the timeout something like this...
$id = $widget->after(3000, sub {
$widget->destroy;
});
# To cancel, just do...
$id->cancel;
You also need to make sure that you don't block when the widget is forced to go away, of course. This also prevents trouble if someone kills the widget by other means too, so it's a double-bonus.

Related

How can I detect window resize event using Win32::GUI and WM_DISPLAYCHANGE?

I am struggling to make a simple receive WM_DISPLAYCHANGE informing my Win32::GUI app that the Windows Screen Resolution has changed, since the results for this question here is "0" accordingly informed by the search engine.
Could you provide a simple working example of a simple Win32::GUI program that detects a WM_DISPLAYCHANGE message and prints some info about that change in resolution?
From user "beech" at PerlMonks: http://perlmonks.org/index.pl?node_id=1171819
Try using the Hook method:
something like
$main->Hook( WM_DISPLAYCHANGE(), \&onDisplayChange );
sub onDisplayChange {
my( $object, $wParam, $lParam, $type, $msgcode) = #_;
print "Click handler called!\n";
}
Give a name to your window. Let's call it Main.
$main = Win32::GUI::Window->new(
-name => 'Main',
-width => 100,
-height => 100,
);
Now, define an event handler for the window. It should be of below pattern:
<window name>_<event name>
For example, for Resize event the event handler should be Main_Resize.
sub Main_Resize {
my $mw = $main->ScaleWidth();
my $mh = $main->ScaleHeight();
my $lw = $label->Width();
my $lh = $label->Height();
#print the height/width or whatever you want
}
I would suggest going through Win32::GUI::Tutorial.

Mojo IOLoop blocks app when ran

I use Mojo::IOLoop to perform background tasks that should be run every so often, and am doing this using Mojo::IOLoop::recurring. I do this within the Mojo app itself:
sub startup {
my $self = shift;
$self->setup_routes();
... more setup
my $sleep_time = $self->config()->{sleep_time};
Mojo::IOLoop->recurring($sleep_time => sub {
my $sync = My::BackgroundTask->new(
sleep_time => $sleep_time,
);
$sync->run();
});
local $SIG{TERM} = sub {
Mojo::IOLoop->stop_gracefully;
};
}
When the time comes for the above loop to run, when trying to view the actual app the site times out, and when it's finished the app is available again. Not sure why this is happening, would someone be able to explain?
EDIT:
My::BackgroundTask::run
sub run {
my ($self, $data) = #_;
while ( scalar(#{$data}) > 0 ) {
my #batch = splice(#{$data}, 0, 100);
$self->schema->update_batch_of_data( \#batch );
# sleep for a while to not be rude :P
sleep ($self->sleep_time);
}
return 1;
}
Are you saying that it sleeps for $sleep_time seconds? Because that's not how it's supposed to work. It should exit and continue the process next time the recurring starts it up. As it is, you're trying to start another copy of the task of the task each time recurring kicks in, and maybe your task is hanging because it's unable to start more than one task? Just a guess, Does it ever return from $sync->run()? And what sort of thing is $sync?
– Borodin
yeah seems to have been because of the sleep inbetween, thanks! Any suggestions on how to be friendly when calling external sources and not milking them in one go? :P – a7omiton
Yes, you need a similar recurring timer, but only do a fraction of the work at each step
– Borodin

How can I get these Perl scripts to delay?

I'm making a simple IRC bot in Perl that can be used to "hunt ducks" in response to this IRC game bot. I'm doing this on a private scripting channel, irc.freenode.net ##duckhunt2 so as not to interfere with real people playing the game.
So far I've tried making a Perl bot using Net::IRC and a plugin for XChat, with my code here. The duck source bot sends a message like
・゜゜・。。・゜゜\_O< quack!
a random amount of time in between 8-60 minutes since the last duck was shot to let you know that a duck has arrived. You can then reply with .bang to shoot the duck and get one point added to your score. However, if you reply too quickly (within one second), it puts you in a 2 hour cooldown mode where you can't shoot any ducks. Sometimes it also throws in 7 second cooldowns because of "jammed guns" and such, as shown in line 272 of the game bot code.
Perl code
use Net::IRC;
use Time::HiRes qw(usleep nanosleep);
$ducksource = 'DUCK_SOURCE';
$server = 'IRC_SERVER';
$channel = 'IRC_CHANNEL';
$botnick = 'BOT_NICKNAME';
$botnick2 = 'BOT_BACKUP_NICKNAME';
$password = 'BOT_PASSWORD';
$botadmin = 'BOT_ADMIN_NICKNAME';
$irc = new Net::IRC;
$conn = $irc->newconn(
Nick => $botnick,
Server => $server,
Port => IRC_SERVER_PORT,
Username => $botnick
);
$conn->add_global_handler('376', \&on_connect);
$conn->add_global_handler('disconnect', \&on_disconnect);
$conn->add_global_handler('kick', \&on_kick);
$conn->add_global_handler('msg', \&on_msg);
$conn->add_global_handler('public', \&on_public);
$irc->start;
sub on_connect {
$self = shift;
$self->privmsg('nickserv', "identify $password");
$self->join($channel);
print "Connected\n";
}
sub on_disconnect {
$self = shift;
print "Disconnected, attempting to reconnect\n";
$self->connect();
}
sub on_kick {
$self = shift;
$self->join($channel);
$self->privmsg('nickserv', "/nick $botnick");
}
sub on_msg {
$self = shift;
$event = shift;
if ($event->nick eq $botadmin) {
foreach $arg ($event->args) {
if ($arg =~ m/uptime/) {
$self->privmsg($botadmin, `uptime`);
}
}
}
}
sub on_public {
$self = shift;
$event = shift;
if ($event->nick eq $ducksource) {
foreach $arg ($event->args) {
if (($arg =~ m/</) && ($arg !~ m/>/)) {
usleep(250000);
$self->privmsg($channel, ".bang");
}
if ( ($arg =~ m/missed/)
|| ($arg =~ m/jammed/)
|| ($arg =~ m/luck/)
|| ($arg =~ m/WTF/)) {
$self->privmsg('nickserv', "/nick $botnick2");
$self->privmsg($channel, ".bang");
$self->privmsg('nickserv', "/nick $botnick");
}
if (($arg =~ m/script/) || ($arg =~ m/period/)) {
$self->privmsg('nickserv', "/nick $botnick2");
$self->privmsg($channel, ".bang");
}
}
}
}
The Perl bot connects to the server, joins the chat room, and responds to a duck appearing, but I can't get it to delay the sending of the command .bang so that the game bot receives it after 1 second has passed and I don't go into the two-hour cooldown mode.
I know that the Perl sleep command only accept multiples of one second. I need to delay 0.25 seconds because it takes about 0.75 seconds for the message to reach the game bot, so I've tried using Time::HiRes and the usleep command, which uses microseconds (1,000 microseconds = 1 millisecond).
On line 61 of my code, I added usleep(250000) which should make the script pause for 0.25s before sending the message on the next line
$self->privmsg($channel, ".bang")
But the script does not wait -- it just sends the message as normal. It acts like it is ignoring the usleep command.
How can I fix this and make the bot wait before it sends the message?
Secondly, I'm confused over how to change nicknames. If the game bot gives me a 7 second cooldown, I'd like to quickly change my nick to another nick (e.g. HunterBot6000 to HunterBot6000_) shoot the duck (.bang), and change my nick back before another bot gets the duck. Typically you accomplish a nick change through the /nick NEWNICK command. However, I've tried sending this command to the channel and NickServ, and this doesn't change my nickname. How should I accomplish this?
I also tried writing an XChat plugin for the script to see if that would get rid of the timing issue, but that doesn't work either. After connecting to the server and joining the chat room in XChat, I load the plugin, and I have the same issue -- it responds to ducks with .bang but I cannot get it to wait before sending.
You can see the documentation Writing a simple XChat Perl Script. What am I doing wrong?
You're asking multiple questions, but I can only answer one from my phone
You can change nicknames by sending
NICK newnick
Further information can be found in the RFC 2812.
However, Net::IRC might have more appropriate means for that.
I have also had trouble from usleep from Time::HiRes. This should effect a sleep of 250ms:
select(undef, undef, undef, 0.25);
Thank you for everyone's help. I was able to get the usleep command working and verify that it was delaying properly by changing the delay to a larger amount of seconds (e.g. usleep(25000000), 25 seconds) and then changing back to 0.25 seconds by removing one 0 at a time. I also added print Time::HiRes::time; before and after to verify that the delay was working. I also found that the proper command to change nicks is $self->nick($botnick2);, even though it is nowhere to be found in any Net::IRC documentation. Once again, thank you all for the help and advice.

How can i repeatedly prompt the user with Tkx?

Using Perl Tkx, I want to get some input from the user, close the window, and maybe do it again later. For user input, I'm just displaying some buttons, and the user gets to click on one of them. Here's what I have now:
sub prompt_user {
my $answer;
my $mw = Tkx::widget->new("."); ## the main window is unavailable the second time
$mw->g_wm_title("Window Title"); ## line 40
$mw->g_wm_minsize(300, 200);
my $label = $mw->new_label( -text => "Question to the user?");
$label->g_pack( -padx => 10, -pady => 10);
my $button1 = $mw->new_button(
-text => "Option One",
-command => sub { $answer = 0; $mw->g_destroy; },
);
$button1->g_pack( -padx => 10, -pady => 10);
my $button2 = $mw->new_button(
-text => "Option Two",
-command => sub { $answer = 1; $mw->g_destroy; },
);
$button2->g_pack( -padx => 10, -pady => 10);
Tkx::MainLoop(); ## This blocks until the main window is killed
return $answer;
}
So the user clicks on one of the buttons, the window closes, prompt_user() returns 0 or 1 (depending on which button the user clicked), and execution continues. Until I try to prompt the user again. Then I get an error:
can't invoke "wm" command: application has been destroyed at MyFile.pm line 40
I just want a way to put up a bunch of buttons, let the user click one, wait to see which one is clicked, and maybe do it again later. Is there a way I can wait for a response to the button click without destroying the main window? Maybe create a subwindow?
I'm new to using Tkx, and googling shows lots of simple examples like the above code (using MainLoop/g_destroy), but I couldn't find any examples of recreating windows. I did see stuff about a Dialog Box or Message Box, but those won't suit my needs. I want to put text on the buttons, and use an arbitrary number of buttons (so I don't want to be limited to yes/no/cancel, and only have 3 options).
Update
Here's what I was able to use
# hide the main window, since I'm not using it
my $mw = Tkx::widget->new(".");
$mw->g_wm_withdraw();
# function to prompt the user to answer a question
# displays an arbitrary number of answers, each on its own button
sub prompt {
my $prompt = shift;
my $list_of_answers = shift;
# Note: the window name doesn't matter, as long as it's './something'
my $answer = Tkx::tk___dialog( "./mywindowpath", # window name
"Prompt", # window title
$prompt, # text to display
undef, # tk bmp library icon
undef, # default button
#$list_of_answers); # list of strings to use as buttons
return $answer;
}
# use the button to ask a question
my $index = prompt("Who was the best captain?",
[ "Kirk", "Picard", "Cisco", "Janeway", "Archer" ] );
I'm not really familiar with Tkx but Tk doesn't really work well that way. In general Tk applications are asynchronous. You should re-write your application in term of callbacks (kind of like javascript).
Basically, this kind of logic:
sub do_something {
perform_some_action();
my $result = prompt_user();
perform_some_other_action($result);
}
should be re-written to something like:
sub do_something {
perform_some_action();
prompt_user(perform_some_other_action);
}
Your program should basically not have a main loop. Instead the call to Tkx::MainLoop at the end of your program becomes the main loop and you should do all processing by handling events.
Having said that, there are some mechanisms available that emulates blocking. Read the documantation for vwait. Though, I think even that requires a running Tkx::MainLoop so it does not necessarily avoid refactoring your whole program.
On the question of how to create and destroy windows there are two solutions:
Use the main window (.) but don't destroy it at the end. Instead hide it and destroy all its children. You can then later reuse . by unhiding it.
Hide . and don't use it. Instead create other windows (toplevels) and use them. Since toplevels are children of . they are safe to destroy without screwing up Tk.

Why do I have to send multiple messages to my Jabber bot before it will logout?

I am trying to make my own Jabber bot but i have run into a little trouble. I have gotten my bot to respond to messages, however, if I try to change the bot's presence then it seems as though all of the messages you send to the bot get delayed.
What I mean is when I run the script I change the presence so I can see that it is online. Then when I send it a message it takes three before the callback subroutine I have set up for messages gets called. After the thirrd message is sent and the chat subroutine is called it still process the first message I sent.
This really doesn't pose too much of a problem except that I have it set up to log out when I send the message "logout" and it has to be followed by two more messages in order to log out. I am not sure what it is that I have to do to fix this but i think it has something to do with iq packets because I have an iq callback set as well and it gets called two times after setting the presence.
Here is my source code:
#!/usr/bin/perl
use strict;
use warnings;
#Libraries
use Net::Jabber;
use DBI;
use DBD::mysql;
#--------------- Config Vars -----------------
# Jabber Client
my $jbrHostname = "DOMAINNAME";
my $jbrUserName = "USERNAME";
my $jbrPassword = "PASSWORD";
my $jbrResource = "RESOURCE";
my $jbrBoss = new Net::Jabber::JID();
$jbrBoss->SetJID(userid=>"USERNAME",server=>$jbrHostname);
# MySQL
my $dbHostname = "DOMAINNAME";
my $dbName = "DATABASENAME";
my $dbUserName = "USERNAME";
my $dbPassword = "PASSWORD";
#--------------- End Config -----------------
# connect to the db
my $dbh = DBI->connect("DBI:mysql:database=$dbName;host=$dbHostname",$dbUserName, $dbPassword, {RaiseError => 1}) or die "Couldn't connect to the database: $!\n";
# create a new jabber client and connect to server
my $jabberBot = Net::Jabber::Client->new();
my $status = $jabberBot->Connect(hostname=>$jbrHostname) or die "Cannot connect ($!)\n";
my #results = $jabberBot->AuthSend(username=>$jbrUserName,password=>$jbrPassword,resource=>$jbrResource);
if($results[0] ne "ok")
{
die "Jabber auth error #results\n";
}
# set jabber bot callbacks
$jabberBot->SetMessageCallBacks(chat=>\&chat);
$jabberBot->SetPresenceCallBacks(available=>\&welcome);
$jabberBot->SetCallBacks(iq=>\&gotIQ);
$jabberBot->PresenceSend(type=>"available");
$jabberBot->Process(1);
sub welcome
{
$jabberBot->MessageSend(to=>$jbrBoss->GetJID(),subject=>"",body=>"Hello There!",type=>"chat",priority=>10);
&keepItGoing;
}
$jabberBot->MessageSend(to=>$jbrBoss->GetJID(),subject=>"",body=>"Hello There! Global...",type=>"chat",priority=>10);
#$jabberBot->Process(5);
&keepItGoing;
sub chat
{
print "Chat Called!\n";
my ($sessionID,$msg) = #_;
$jabberBot->MessageSend(to=>$msg->GetFrom(),subject=>"",body=>"Chatting!",type=>"chat",priority=>10);
if($msg->GetBody() ne 'logout')
{
print $msg->GetBody()."\n";
&keepItGoing;
}
else
{
&killBot($msg);
}
}
sub gotIQ
{
print $_[1]->GetID()."\n";
&chat;
}
sub keepItGoing
{
print "Movin' the chains!\n";
my $proc = $jabberBot->Process(1);
while(defined($proc) && $proc != 1)
{
$proc = $jabberBot->Process(1);
}
}
sub killBot
{
$jabberBot->MessageSend(to=>$_[0]->GetFrom(),subject=>"",body=>"Logging Out!",type=>"chat",priority=>10);
$jabberBot->Process(1);
$jabberBot->Disconnect();
exit;
}
Thanks for your help!
You've got resource starvation because of your keepItGoing routine. In general, trying to use XMPP synchronously like this is not going to work. I suggest getting your callbacks set up, then just calling Process() in one loop.
The docs for Process() say:
Process(integer) - takes the timeout period as an argument. If no
timeout is listed then the function blocks until
a packet is received. Otherwise it waits that
number of seconds and then exits so your program
can continue doing useful things. NOTE: This is
important for GUIs. You need to leave time to
process GUI commands even if you are waiting for
packets. The following are the possible return
values, and what they mean:
1 - Status ok, data received.
0 - Status ok, no data received.
undef - Status not ok, stop processing.
IMPORTANT: You need to check the output of every
Process. If you get an undef then the connection
died and you should behave accordingly.
Each time you call Process(), 0 or more of your callbacks will fire. You never know which, since it depends on server timing. If you want for Process() to return before sending something, you're almost always thinking synchronously, rather than asych, which kills you in XMPP.
In your case, if you remove the call to keepItGoing from chat(), I bet things will work more like you expect.
Replace the line:
$jabberBot->Process(1);
with these:
while (defined($jabberBot->Process(1))) {
# Do stuff here
}