I have a Pertl Tk code, I want to close the main window and open another but after the first window is closing again when second window gets open te first window also appears.
The code
use strict;
use Tk;
my $mw;
#Calling the welcome_window sub
welcome_window();
sub welcome_window{
#GUI Building Area
$mw = new MainWindow;
my $frame_header = $mw->Frame();
my $header = $frame_header -> Label(-text=>"Molex Automation Tool");
$frame_header -> grid(-row=>1,-column=>1);
$header -> grid(-row=>1,-column=>1);
my $region_selected = qw/AME APN APS/;
my $frame_sub_header = $mw->Frame();
my $sub_header = $frame_sub_header -> Label(-text=>"Region Selection");
$frame_sub_header -> grid(-row=>2,-column=>1);
$sub_header -> grid(-row=>2,-column=>1);
my $frame_region = $mw->Frame();
my $label_region = $frame_region -> Label(-text=>"Region:");
my $region_options = $frame_region->Optionmenu(
-textvariable => \$region_selected,
-options => [#regions],
);
$frame_region -> grid(-row=>3,-column=>1);
$label_region -> grid(-row=>3,-column=>1);
$region_options -> grid(-row=>3,-column=>2);
my $frame_submit = $mw->Frame();
my $submit_button = $frame_submit->Button(-text => 'Go!',
-command => \&outside,
);
$frame_submit -> grid(-row=>4,-column=>1,-columnspan=>2);
$submit_button -> grid(-row=>4,-column=>1,-columnspan=>2);
MainLoop;
}
#This sub is just made to close the main window created in Welcome_Wiondow() sub and call the second_window()
sub outside{
$mw -> destroy;
sleep(5);
second_window();
}
sub second_window{
my $mw2 = new MainWindow;
my $frame_header2 = $mw2->Frame();
my $header2 = $frame_header2 -> Label(-text=>"Molex Automation Tool");
$frame_header2 -> grid(-row=>1,-column=>1);
$header2 -> grid(-row=>1,-column=>1);
my $frame_sub_header2 = $mw2->Frame();
my $sub_header2 = $frame_sub_header2 -> Label(-text=>"Tasks Region Wise");
$frame_sub_header2 -> grid(-row=>2,-column=>1);
$sub_header2 -> grid(-row=>2,-column=>1);
MainLoop;
}
I have reduced the code and only put the relevant lines. Now please let me know why I can't kill the main window opened in sub welcome_window() in the sub outside(). Currently what it does is it closes the main windows during the sleep command but as soon as I open the main windows of the second_windows, the windows of welcome_window also reappears.
Got the above code working now, there was some issue in the logic
which was calling the welcome_window again. Thank you all for your
help.
You can't have more than one MainWindow. Create a Toplevel window for the initial one, use withdraw to hide the real main window at the start, make it reappear with deiconify:
my $mw = MainWindow->new;
my $tl = $mw->Toplevel;
$tl->protocol(WM_DELETE_WINDOW => sub {
$mw->deiconify;
$tl->DESTROY;
});
$mw->withdraw;
MainLoop();
Related
I just start Perl Tk and I had a look on some tutorials but I have a problem. When I click on a button it displays on the entry widget the scalar that I want. It works but when I click an other time it keeps what was written on the entry. So I have two hello. I know that it comes from insert(0, "Hello") but I don't what to put instead of 0.
#!/usr/local/bin/perl
use Tk;
my $mw = MainWindow->new;
$mw->geometry("500x350+0+0");
$mw->title("Report Information about a Protein of Interest");
my ($bite) = $mw -> Label(-text=>"Enter the uniprot accession number:")->grid(-row => 0, - column => 0);
my ($ent) = $mw->Entry()->grid(-row => 0, - column => 1, -columnspan => 2, -sticky => 'nsew');
$ent2 = $mw->Button(-text=> "Search", -command => \&push_button)->grid(-row => 1, - column => 0);
MainLoop;
#This is executed when the button is pressed
sub push_button {
$ent -> insert(0,"Hello, ");
}
The insert method for a Tk::Entry widget inserts text after the current insertion cursor position; to delete the existing text in the widget before inserting you can do:
sub push_button {
$ent -> delete(0, 'end'); # clears the widget
$ent -> insert(0,"Hello, ");
}
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.
I'm trying to add a menubar with the standard File Open, Save and New options.
However, instead of behaving as expected, the subroutine handling the open, save and new actions is launched upon creation of the frame. But, when I actually click on them, it is not.
Following is the code I'm using. (Main window contains only the menubar)
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use Data::Dumper;
use Tk 8.0;
use Tk::NoteBook;
use Tk::MsgBox;
my $mw=MainWindow->new;
$mw->geometry("+500+300");
# Menu Bar Buttons
my $mbar=$mw->Menu();
$mw->configure(-menu => $mbar);
my $file=$mbar->cascade(-label=>"~File", -tearoff => 0);
my $help=$mbar->cascade(-label =>"~Help", -tearoff => 0);
# File Menu
$file->command(-label =>'~New ', -command=>&menu_file('n'), -accelerator=>'Ctrl+N');
$file->command(-label =>'~Open ', -command=>&menu_file('o'), -accelerator=>'Ctrl+O');
$file->command(-label =>'~Save ', -command=>&menu_file('s'), -accelerator=>'Ctrl+S');
$file->separator();
$file->command(-label =>'~Quit ', -command=>sub{exit}, -accelerator=>'Ctrl+Q');
# Help Menu
$help->command(-label => 'Version');
$help->separator;
$help->command(-label => 'About');
# Menu Bar Accelerators
$mw->bind('<Control-n>', &menu_file('n'));
$mw->bind('<Control-o>', &menu_file('o'));
$mw->bind('<Control-s>', &menu_file('s'));
$mw->bind('<Control-q>', sub{exit});
MainLoop;
sub menu_file {
my $opt=shift;
my $filetypes = [
['Codac files', '.k'],
['All Files', '*' ],
];
if($opt eq 's'){
my $txt_ent_script = $mw->getSaveFile(-filetypes=>$filetypes, -initialfile=>'jitter', -defaultextension=>'.k');
print "Output filename: $txt_ent_script\n";
}
}
That's because &menu_file('n') is syntax for invoking a subroutine (more details). Instead, you have to do it like this:
$mw->bind('<Control-n>' => sub{menu_file('n')});
Or like this:
$mw->bind('<Control-n>' => [\&menu_file, 'n']);
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.
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.