by James Davis (c)2000
One of the chief problems with designing interfaces for TCL is using the packer. Its fairly convenient to define widgets programmatically. However to arrange them, we need to use frames to lay things out. If you know the exact arrangement beforehand then this is easy. However, when we want to make changes, an extra layer of heirarchy is often required, and because of the way widgets are named relative to their frame, we need to go back and rename all the widgets. This is a real hassle. Smartpack defines some routines that make this a little less painful, and code a little easier to read. For example, suppose we want to group some widgets: frame .frame button .frame.b1 -text b1 button .frame.b2 -text b2 button .frame.b3 -text b3 pack .frame.b1 .frame.b2 .frame.b3 pack .frame If we decide that we want to insert another layer, we have frame .frame button .frame.b1 -text b1 frame .frame.newlevel button .frame.newlevel.b2 -text b2 button .frame.newlevel.b2 -text b3 pack .frame.newlevel.b2 .frame.newlevel.b3 pack .frame.b1 .frame.newlevel pack .frame Note that we added two new lines, and changed three of the original six lines. All this for a single conceptual change. Part of the problem is that when we define widgets we have to explicitly give the widget path, rather than just saying 'Please give me a new widget in the current frame.' If we could define widgets relative to some current state, then when we changed the heirarchy, the widgets would just appear in new current level when they are defined, without actually changing the defining code. A second problem, is that we have to explicitly pack all the widgets we built. Often we'd like to just 'pack all widgets' So, we can define a couple new constructs which begin and end levels of the heirarchy, and then declare widgets relative to these. startframe .frame button $smartpack.b1 -text b1 button $smartpack.b2 -text b2 button $smartpack.b3 -text b3 packframe pack .frame Lets look at whats new here. We use 'startframe' rather than 'frame' to declare a new frame. This sets up a current context. Then we can use $smartpack to reference this notion of current context. When we finish with the frame we use 'packframe' to pack all the widgets currently defined in the frame. Note that we skip explicitly naming the widgets. Lets look at what we have to do now to add a level of heirarchy. startframe .frame button $smartpack.b1 -text b1 startframe newlevel button $smartpack.b2 -text b2 button $smartpack.b3 -text b3 packframe packframe pack .frame In this case we only had to add two lines, but didn't have to change any of the existing lines of code. We added one line to create a new current context, and one line to pack things in this context. Also note that since this is a sub-frame we only specified a relative frame name 'newlevel', rather than a complete path '.frame.newlevel'. In some cases we might want the code to more clearly indicate the frame heirarchy, so we can use one more new construct to combine the startframe and packframe commands, like this: smartframe .frame { button $smartpack.b1 -text b1 button $smartpack.b2 -text b2 button $smartpack.b3 -text b3 } pack .frame Note that we changed from 'startframe' to 'smartframe', and the packframe at the end became implicit. After changing we have: smartframe .frame { button $smartpack.b1 -text b1 smartframe newlevel { button $smartpack.b2 -text b2 button $smartpack.b3 -text b3 } } pack .frame We've added one new command, and a level of {} braces in order to change the heirarchy. In addition if we use an editor that auto-indents then the logical widget heirarchy will be reflected in the code indentation naturally. This can improve the readability of the code quite a bit on complex arrangements. Here is a more complete sytax example: smartframe ".frame -relief groove" { button $smartpack.b1 -text b1 smartframe "newlevel -bd 2" { smartframe moreLevels { button $smartpack.b2 -text b2 button $smartpack.b3 -text b3 } } -anchor w } -side left startframe .frame2 button $smartpack.b1 -text "A different b1" packframe pack .frame .frame2 Finally, here is a reference: proc startframe {fname args} proc packframe {args} proc smartframe {fname body args}
|