While it is common to use a button widget to create a child window, IMO it's better to use Tk::Menu to create a standard menubar with links that create the child windows and then use button widgets to preform specific tasks within the window. http://search.cpan.org/~srezic/Tk-804.032/pod/Menu.pod

The menus in this example are extremely simple. In real life, each menu will probably be far more complex. This makes it essential that each menu is contained in its own file. Switching between menus requires that each menu file knows the reference to other menus so it can hide are show menus within the system. I did this by making each menu reference “our” so the each menu can see the others.

I’d appreciate any advice on how to make this system more Perlish.

The code is now broken up into 4 separate Perl files:

TkMenus.pl: The mainline that calls the menu creation subs and runs the main loop.

Code

use Modern::Perl '2013'; use Tk;

our $mainMenu; our $menu1; our $menu2;

main();

sub main { do 'MainMenu.pl'; do 'Menu1.pl'; do 'Menu2.pl';

CreateMainMenu(); # This only creates the top menu CreateMenu1(); # This creates and hides a sub menu CreateMenu2(); # This creates and hides a sub menu

Thanks for the reply, looks like you posted yours one minute before I posted mine!

A few questions.

Within the schema of the menu system I see for my ap, many of the menus will be generated when a user request encounters a problem. When that happens a new menu will “pop up” to ask for more information. I need to make sure that only one menu is active at a time. This is what led to the withdraw and raise code in my example.

I am somewhat familiar with Tk::Toplevel. Each top level menu is the child of a parent menu. Can a second top level menu become the grandchild of another child menu?

OK, I made the changes you suggested and it works the same way. Thanks, as I said I wanted to be more Perlish. The new code is basically the same as multi-file post (the third post of this thread) with the following minor changes:

$mainMenu = new MainWindow; was replaced with $mainMenu = tkinit;

And

$menu1 = new MainWindow; was replaced with $menu1 = $mainMenu->Toplevel(); Same for menu2!

But I do have one question on the code you supplied. I didn’t make this change, but why did you change the order of the menu hide and show logic?

I hope that you also mean that you want to develop your scripts using good/best practices. In which case, you should get rid of the global vars and instead use lexical vars and pass parameters to and from your subs.

I definitely want to learn “best practices” from the start, but understanding the “scope” of things in Perl is a bit confusing.

I’m working on a process to track investment in your personal 401Ks. If you move some of your money to stock symbol ABCDE:

Scenario one: (The flexible way) You enter the symbol and it is not in your database so it asks you to add it. While adding it you want to classify it as a “foreign” investment, but foreign is not yet a predefined class, so it asks you to add the class and define its meaning.

Scenario two: (The strict way) Before you can add each investment you have to check to see if the symbol exists. You have to revert to the main menu to check and possibly add the symbol, but before you can do that you have to start at the main menu to check to see if the classification of the symbol exists and add the new classification if needed.

My system will have multiple menus that might be called from various other menus, but only one menu will be visible at a time. The only thing I want to avoid is any possible loops in the logic. I plan to have a “switch menu” process that keeps a stack of the menus in the order they were called, so the go back function knows where to return to.

If the menuX.pl file keeps a global variable “my MenuX;” so it can be defined and have a sub function to return the menu’s reference, then menuK.pl and menuQ.pl which can both call menuX will have to include the line “do ‘menuX.pl’” so they can call MenuXref() to obtain the menu’s reference.

If the menuX.pl file declares a universal variable “our MenuX;”, then any other menu that wants to switch to it can merely include the “our MenuX;” line prior to any subs.

By using the list of universal “our menu?:” lines, each menu can be self-contained. They only have to know the “name” of the menu they wish to call, they do not have to “include/do” the code of those menus.

Another option would be to have a menu hash Perl file which maintains the references to all menus defined by their hash names.

I do not know how many menus my system will eventually have. I want to make it a simple process to expand the system as new ideas arise. I know my initial idea of a set of universal variables starting with the prefix “menu” is far from the right way to go. What do you recommend as the "best practice" to do this?

My system will have multiple menuswindows that might be called from various other menuswindows, but only one menuswindow will be visible at a time.

Proper terminology is important. Without it, confusion arises.

Quote

The only thing I want to avoid is any possible loops in the logic.

A lot of what you describe sounds to me like you'll end up with logic loops and/or spaghetti.

Quote

I plan to have a “switch menu” process that keeps a stack of the menus in the order they were called, so the go back function knows where to return to.

If you use $window->Toplevel to create your child window(s), then you can use the $child_window->parent method to find out which window you need to go back to.

Quote

Scenario one: (The flexible way) You enter the symbol and it is not in your database so it asks you to add it. While adding it you want to classify it as a “foreign” investment, but foreign is not yet a predefined class, so it asks you to add the class and define its meaning.

In this scenario I'd raise a child window to handle the defining of the foreign class without withdrawing the parent window. And construct it so that the parent window can't gain focus while the child window is shown.

Good strong arguments can be made for each of your scenarios. The first is probably more user friendly, but also more complex to setup.

Personally, I'd probably keep the number of windows to a minimum. The main window would have a navigation menu (just like any standard app) and the menu options would raise and withdraw the given frame that contains the widgets needed for that operation. If needed a child window could probably be opened within the frame.

I'm getting behind in my projects at work so I can't work up any example scripts until I get caught up.

OK, another try at you training an old programmer new tricks, making me Perlish!

Here is another set of Perl files. They are far more concise than the previous set.

I changed all of my menu references to window.

I changed the logic so main can call window 1 & 2 and 1 can also call 2. 2 will return properly to whoever called it.

The loop and spaghetti logic will be on me, I want the process to be flexible.

I’m not sure that $child_window-> parent will always work when the child can be called by multiple parents. I created a new Perl file, WindowHash.pl, that controls a hash of references to all windows and also a call stack.

I changed the code to use $window = getWindow('MainWindow')->Toplevel; instead of $window = new MainWindow; but I’m not sure what the difference will be if I do not intend to use the window’s parent logic due to multiple call from options.

I can now open the next window directly using “-command =>[\&ShowWindow, 'Window2']” and return to whoever called this window with “command =>\&PrevWindow”.

This new Perl file does contain two variables that are global within the file, but they are not universal to all other files. Is there a better way to create and handle the hash and array in this file?

Mainline.pl: simple file to load the other files, create the windows and start the loop.

Code

use Modern::Perl '2013'; use Tk;

main();

sub main { do 'WindowHash.pl'; do 'MainWindow.pl'; do 'Window1.pl'; do 'Window2.pl';

CreateMainWindow(); CreateWindow1(); CreateWindow2();

MainLoop; }

WindowHash.pl: New logic to manage a hash of window references by name and a call stack of the windows On Edit: I've change the name of this module to WindowManager.pl since that better describes what it does!

MainWindow.pl: all windows are now called $window internally. This makes copying controls from one window to another easier. Due to the window hash logic, changing from one window to another can now be done directly by the Button –command =>[\&ShowWindow, 'Window1'].

Ok, even though I haven't tested it, I can say that it is much better than your previous sample.

However, I still see an issue with code duplication. The subs CreateWindow1 and sub CreateWindow2 are nearly identical. The only difference is the text label and sub used in the command. This is an indication that you should refactor the subs and use a single sub which accepts the required parameters for the differences. I'd probably pass a hash ref.

I realize that your actual subs will be more involved than these samples, but whenever you have code duplication like this, you should refactor the code.

Thanks FishMonger. I value your input. I totally agree with the refactoring in general, but in this example I wanted to show how different menus could be calling each other. In real life these routines would have been refactored. At least I’m getting more Perlish. At least I didn’t call it Pearlish!

I’m already using the new code schema for my project, again, thanks for all your assistance.