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

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.

Related

Perl win32::GUI progressbar

Progress bar is not following the script. I want to display progressbar as the script executes. when it done it should stop. currently it is displying full process then going for printing output.
Please find my code below:
$Progress_bars = $main->AddProgressBar(
-pos => [10,840],
-background => [0,255,85],
-smooth => 1,
-size => [470,20],
);
my $intended_max = 100;
my $x = 11;
my $y = int($intended_max/$x);
$Progress_bars->SetRange(0,$x*$y);
for (1 .. $x) {
Win32::GUI::DoEvents();
$Progress_bars->SetStep($y);
$Progress_bars->StepIt();
Win32::Sleep(50);
}
$output = $telnet->print('ls');
print $output;
You're code is increasing the value by the progress value rather than setting it directly.
Try calling the SetPos methods instead of SetStep and StepIt.
(Note that I don't use Win32::GUI, so I'm guessing the method name based on MSDN.)

Selenium + Perl: Check if alert exists?

Selenium 2.* is running on Linux with Firefox as the browser.
I am using perl with the Selenium::Remote::Driver module to interact with the server.
Is there anything available to check if an alert is present? The perl module provides several functions to click OK on an alert, or to get the text from it, but this will raise an error if there is no alert - how to avoid an error and still get rid of any alert?
Basically, I want to remove all alerts when a page finished loading (if any), but not sure how?
Another option that I tried is disabling all alerts by setting a variable in the firefox profile (which works when you use the browser yourself), but somehow the alert is still there when the browser is used by Selenium because i think Selenium handles the alert itself because of the "handlesAlerts" capability, which is always set to true, and I'm not sure how to disable it. It may be the solution if it's not possible to check for the existence of an alert.
Anyone have an idea?
You could try to dismiss alerts, use eval block to handle exceptions
eval {
$driver->accept_alert;
};
if ($#){
warn "Maybe no alert?":
warn $#;
}
I created a couple of functions that check for an alert and then either cancel or interact with it as needed.
use Try::Tiny qw( try catch );
# checks if there is a javascript alert/confirm/input on the screen
sub alert_is_present
{
my $d = shift;
my $alertPresent = 0;
try{
my $alertTxt = $d->get_alert_text();
logIt( "alert open: $alertTxt", 'DEBUG' ) if $alertTxt;
$alertPresent++;
}catch{
my $err = $_;
if( $err =~ 'modal dialog when one was not open' ){
logIt( 'no alert open', 'DEBUG2' );
}else{
logIt( "ERROR: getting alert_text: $_", 'ERROR' );
}
};
return $alertPresent;
}
# Assumes caller confirmed an alert is present!! Either cancels the alert or
types any passed in data and accepts it.
sub handle_alert
{
my ( $d, $action, $data ) = #_;
logIt( "handle_alert called with: $action, $data", 'DEBUG' );
if( $action eq 'CANCEL' ){
$d->dismiss_alert();
}else{
$d->send_keys_to_alert( $data )
if $data;
$d->accept_alert();
}
$d->pause( 500 );
}

Pop Up in perl that goes away automatically after pause

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.

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.

How can I close a window in Perl/Tk?

In my Perl/Tk script I have opened two windows. After a specific button click I want to close one of them. How can I do that? Here's what I have so far:
$main = new MainWindow;
$sidebar = $main->Frame(-relief => "raised",
-borderwidth => 2)
->pack (-side=>"left" ,
-anchor => "nw",
-fill => "y");
$Button1 = $sidebar -> Button (-text=>"Open\nNetlist",
-command=> \&GUI_OPEN_NETLIST)
->pack(-fill=>"x");
MainLoop;
sub GUI_OPEN_NETLIST
{
$component_dialog = new MainWindow;
$Button = $component_dialog -> Button (-text=>"Open\nNetlist",
-command=> **close new window**)
->pack(-fill=>"x");
MainLoop;
}
The simplist way is to call $component_dialog->destroy in the buttons -command callback. This has the disadvantage that if you want to redisplay the window later you have to recreate it.
The withdraw method will hide the window without destroying it so you can redisplay it later if you need to. This will save you some time when the button is pressed. The classes Dialog and DialogBox do this for you automatically when one of their buttons is pressed. If you need a window that behaves like a traditional dialog they can a much simpler option that building your own.
Also except in unusual cases you shouldn't need more than one call to MainLoop. When your callback GUI_OPEN_NETLIST returns the MainLoop will resume, explicitly calling MainLoop will likely lead to odd bugs later.
I think this is close to what your looking for, I haven't tested it though.
use strict;
use warnings;
my $main = new MainWindow;
my $sidebar = $main->Frame(-relief => "raised",
-borderwidth => 2)
->pack (-side=>"left" ,
-anchor => "nw",
-fill => "y");
my $Button1 = $sidebar -> Button (-text=>"Open\nNetlist",
-command=> \&GUI_OPEN_NETLIST)
->pack(-fill=>"x");
my $component_dialog = $main->Dialog( -buttons => [ 'Close' ], );
MainLoop;
sub GUI_OPEN_NETLIST
{
$component_dialog->Show();
}
If you don't want a dialog you should consider if you want to create a second MainWindow or create a Toplevel window dependant on your existing MainWindow.
A Toplevel will close automaticaly when it's MainWindow is closed, a second MainWindow will stay open after the other MainWindow is closed.