Frames and Toplevels are both designed specifically to be containers of other widgets. They differ in two ways: in their default settings and in their relationships to other widgets. A Frame, by default, has no visible border, relief, or anything to indicate that it's there, whereas all Toplevel widgets have decoration that is consistent with the system on which your application is run. Also, a Toplevel can be manipulated independently of other Toplevel widgets within the application, whereas a Frame always requires a parent (a Toplevel or another Frame) to reside in. It can't be independent.
We have already seen many examples using Toplevel widgets. The widget created by calling MainWindow->new is actually a Toplevel widget. If you print the variable, you see something like this:
print "$mw\n"; # prints: MainWindow=HASH(0x909a2d0)
This window is special because it displays itself automatically when you call MainLoop. In every other respect, that MainWindow widget is a Toplevel. By creating a Toplevel widget, you are creating another window as part of your application. Other Toplevel widgets in your program must be displayed explicitly somewhere in the code.
When to use an additional Toplevel is a design decision that you'll have to make. You should use another Toplevel widget instead of the MainWindow if there is too much information to fit in one window. Using Toplevels to group information is also sometimes a good idea. You don't want to have too many windows for the user to navigate, but a well-designed application might be able to make use of one or two.
Here are some examples of how you can use Toplevel widgets:
Display informational text with a Close Button.
Provide data gathering output in different Toplevel windows that are triggered by something the user does (e.g., clicking a button).
The entire purpose of a Frame widget is to provide a container for other widgets. This doesn't seem important, but it is. The geometry managers provided with Perl/Tk have some limitations (see Chapter 2, "Geometry Management"), and we can use Frames to help them do their jobs better. We'll use pack as our example geometry manager throughout this chapter because it is the most popular, but remember that the basic rules for using a Frame apply to the other geometry managers as well.
When a Frame contains other widgets, it accommodates the size of the widgets inside it. If you don't have any widgets packed into the Frame, you won't see the Frame. If the widgets inside the Frame are resized for any reason, the Frame will try to resize as well.
 To change this behavior, use packPropagate or gridPropagate.
There is nothing special about creating a Frame widget, except you will usually save a reference to the widget so that you can put other things inside it.
# a Frame you'll never see $frame1 = $mw->Frame; $frame1->Button(-text => 'button1')->pack; $frame1->Button(-text => 'button2')->pack; # a more visible Frame $frame2 = $mw->Frame(-borderwidth => 2, -relief => 'groove'); $frame2->Button(-text => 'button1, frame2')->pack; $frame2->Button(-text => 'button2, frame2')->pack; # a Frame in a Frame $frame3 = $frame2->Frame(-borderwidth => 3, -relief => 'raised'); $frame3->Button(-text => 'button1, frame3')->pack; $frame3->pack; # not visible yet $frame2->pack; # still nothing visible... $frame1->pack; # now we can see all the Frames and Buttons
The Frame's parent can be a MainWindow, a Toplevel, or another Frame widget. After the Frame is created, it can become a parent to other widgets. You must have created the Frame but not necessarily packed it on the screen for it to be the parent of other widgets. Keep in mind that even if you pack other widgets inside your Frame, if you don't pack the Frame as well, the other widgets won't show on the screen.