![]() |
Welcome to Beginner Programming
|
| Course content | Enter chat room | Send email
to mailing list |
Check calendar |
In this lesson we are going to learn how to work with frames.
Frames can be thought of as their picture frame counterparts.
A picture frame holds a painting or a photograph. Tk frames are kind
of like rectangular containers into which we place widgets.
| We are also going to learn about the canvas widget. The canvas widget will be essential for our animation work later in the course. | ![]() |
Before we begin the code for lesson #4, here is a possible solution
to the exercise we left you with in lesson #3.
#=======================================
# hogwarts - entry point
#=======================================
proc hogwarts { mycolor } {
puts stdout [format "lastcolor was %s" $mycolor]
if { $mycolor == "red" } {
.hello configure -text "change me to yellow" -background green
set newcolor green
} else {
if { $mycolor == "yellow" } {
.hello configure -text "change me to green" -background red
set newcolor red
} else {
.hello configure -text "change me to red" -background yellow
set newcolor yellow
}
}
puts stdout [format "newcolor is %s" $newcolor]
return $newcolor
} ;# end of proc hogwarts
#========================================
# main - entry point
#========================================
set lastcolor red
destroy .hello
button .hello -text "change me to green" -background red \
-command { set lastcolor [hogwarts $lastcolor] }
pack .hello -padx 20 -pady 20
|
Notice that the only thing we added was a third "if" branch to handle
the case for the yellow condition.
Step 1: the stoplight |
What object in real life has red, yellow and green elements in it? A stoplight of course. We are going to modify our little program to present something which resembles a stoplight in this lesson.
Let us illustrate our lesson by way of an example. Using
the editor you used in lesson #2 create the following partial program by
typing in these statements (note this program will not yet run correctly)
:
#========================================
# main - entry point
#========================================
destroy .myArea
set f [frame .myArea -borderwidth 5 -background blue]
set f_a [frame $f.animateFrame -background lightBlue]
set f_b [frame $f.buttons]
set lastcolor red
canvas $f_a.c -height 200 -width 300 -background lightBlue
pack $f_a.c -side top
button $f_b.hello -text "change me" \
-command { set lastcolor [hogwarts $lastcolor] }
pack $f_b.hello -side left
place $f_a -x 10 -y 10
place $f_b -x 10 -y 250
pack $f -side top -expand true -fill both
createStoplight
|
We have introduced our first example of a frame construct.
| set f [frame .myArea -borderwidth 5 -background blue] |
Notice the compound statement consisting of an inner and an outer part. The inner part (inside the []) will be evaluated first and the result will be what "f" is set to. The inner part sets up a frame widget called myArea. This frame is attached to the top level window called ".". This frame widget has 2 attributes set on it: one for the width of the border and the other for the background color. This statement is exactly analogous to the button .hello statement which we had in previous examples.
The next statements set up frames within the f frame widget:
set f_a [frame $f.animateFrame -background lightBlue] |
This is accomplished by using the "$" in front of the frame variable "f" which we had previously set. Recall that the "$" means use the "value stored at".
After we have set up the 3 frames we are going to use in our little
example we start by adding the widgets into those frames. The
first of these is another brand new widget called a "canvas" widget.
The canvas widget was designed to hold drawing elements such as rectangles,
ovals, lines etc. I suppose the word canvas was chosen after
the oil paint artists backing material of choice.
canvas $f_a.c -height 200 -width 300 -background lightBlue pack $f_a.c -side top |
The next sequence in the code should be familiar by now: the button widget from our previous examples.
Below that comes these two new statements:
place $f_a -x 10 -y 10 place $f_b -x 10 -y 250 |
Up until now we have used the Tk command "pack" to locate and render our widgets on the screen. While the pack widget is quite easy to use and is very useful, occasionally we would like to have exact positional control as to where the widgets are located on our screen. For this purpose the Tk designers created the "place" command. Before discussing the place command further we need to introduce two new concepts: pixels and coordinates.
Pixels are the dots on a video screen that go into making up any graphical image. A pixel is therefore the smallest element that we can draw on the screen. Most modern computer screens have at least 800 pixels horizontally across the screen and 600 pixels down the screen ... hence the term 800x600 resolution. Better monitors might be capable of 1024x768 or even higher.
Coordinates are a means of labeling each and every pixel on the screen by counting its location horizontally from the top left side of the screen (the x coordinate) and its location vertically from the top of the screen ( the y coordinate). In other words if we said a pixel had the coordinates x=10 and y=250 we would be saying that it was 10 pixels from the left and 250 pixels down from the top of the screen. You can see that using this pixel coordinates we can position things on our screen with a great deal of precision.
The place command does just that. It positions a widget by defining the coordinates of its top left most corner pixel. The key to understanding the place command is that it measures its coordinates from the left and top of its parent widget and not from the screen itself. While this may sound confusing at first it really is not. Imagine a typical graphical computer application that we use everyday on the computer. The whole area visible in the monitor is what is called a screen. Onto that screen we typically have windows. Windows are enclosed areas inside which run our applications. In our case we often have 3 windows open when developing Tcl/Tk programs: 1 for our editor, 1 for the wish console and 1 for wish itself. Inside our wish window we often place frame widgets (as we are doing with this particular program). Inside those frames we then place our buttons etc. that go to making up our application. You can see that this is a little bit like those toys you used to play with as an infant ... the large block with a series of progressively smaller blocks inside them. In computereeze these things are what are known as a hierarchy. A well know example of a hierarchy that you use every time you work with a computer is the construct known as a folder. Folders can contain other folders and finally some files. Again in computereeze the item immediately above another (or enclosing another) in the hierarchy is what is termed "the parent". For the life of me I don't know why they would have called it that.
Returning to our screen:window:frame:widget hierarchy ... the screen
is the window's parent ... the window is the frame's parent ... and the
frame is the widget's parent. So in our example
(which by now has probably scrolled off the screen above) where we have
a frame within a frame ... the x and y coordinates in the place command
are measured from the left and top of the outer frame.
Therefore in our example we are placing two inner frames: one
at x=10, y=10 and the other at x=10, y=250.
We are then using a pack command to position the outer (parent) frame
inside the window.
pack $f -side top -expand true -fill both |
The "-expand true" arguments tell the packer to allow the packing space containing the frame "f" to expand to as needed into the available window cavity. The "-fill both" arguments tell the packer to let the frame expand in both (x and y) directions. Don't worry I find the distinction between these two very difficult to understand ... fortunately I've almost always seen them come together like this. Suffice to say that if you add "-expand true -fill both" to your pack command things will grow to fill whatever space you have for them in your window.
The final command in our little code snip
createStoplight |
actually does all the remaining setup of the widgets in our frames and
canvas areas. In other words it is a command we are going to
write which will draw a stoplight for us.
Step 2: draw the stoplight |
Using you editor add the following code snip at the top of your file
you just created above. In other words these lines will all
be coming before the comment lines which wrap the "main - entry point"
words.
#========================================
# createStoplight - entry point
#========================================
proc createStoplight { } {
global f_a
$f_a.c create rectangle \
100 25 150 75 -fill white -tag redback
$f_a.c create oval \
100 25 150 75 -fill red -tag redlight
$f_a.c create rectangle \
100 75 150 125 -fill black -tag yellowback
$f_a.c create oval \
100 75 150 125 -fill yellow -tag yellowlight
$f_a.c create rectangle \
100 125 150 175 -fill black -tag greenback
$f_a.c create oval \
100 125 150 175 -fill green -tag greenlight
} ;# end createStoplight
|
The first new construct we have introduced here is the command "global".
global f_a |
In programming lingo we refer to variables as having a "scope". What programmers mean here is that variables have areas in the code where they can be used and areas where they can not be used. A full discussion of variable scope is beyond this introductory course, however. Suffice to say that unless we say otherwise a variable can only be accessed inside the code block where we have first set it. This means that in our example in the previous lesson the variable called "newcolor" which we set inside the proc called hogwarts would only be "visible" inside that proc itself. If we tried to access $newcolor in the main body of the code we would get an error saying that "newcolor" was not defined. Hiding variables in this manner is a good thing most of the time. It helps us as programmers write code with less bugs and code that is easier to understand. Occasionally, however, we want to override this "variable hiding" feature and expose our new variable for all to see. This is done by using the "global" command as above.
With this particular statement we are saying that we will be using the variable called "f_a" (a frame) that was defined outside our current code block ... the proc createStopLight. I guess the Tk designers chose the word global because now our variable is known globally in our code.
Having arrived here we now see a series of repeated statements like
these ... one for each of the three colors in a stoplight.
$f_a.c create rectangle \ 100 25 150 75 -fill white -tag redback $f_a.c create oval \ 100 25 150 75 -fill red -tag redlight |
Recall that we created a frame called "f_a" into which we put our "animation canvas" widget called "c". Hence the $f_a.c construct we see repeated over and over here refers to that canvas widget.
One of the neat things about canvas widgets is that you can "draw" shapes on it. Here we are drawing two shapes: a rectangle with a circle inside it. Circle you say ... where do I see the word circle you ask. Well circles are simply special cases of a more general curvy shape called an oval. Most of us think of ovals as being that "egg shape". In fact if you think about it a circle is just a "squished" oval.
Here comes some more coordinate stuff at us. All of the basic shapes we are using here can be fully defined if we could describe the shape of the box which contains them. For the rectangle that is easy ... the box that contains it is exactly the rectangle itself. There are many ways you could communicate to the computer the size of the rectangle you wished it to draw for you. The most compact is what is used here. If you specify the x and y coordinates of the top left corner and the bottom right corner then the computer would have all the information it would need to draw your rectangle. That is precisely what the "100 25 150 75" set of numbers is. (Remember that the "\" means that the rectangle statement is continued on the line below).
It's a little trickier for our friend the circle. If you said that a circular shape had to touch each of the 4 sides of its imaginary bounding box ... you would then have a very easy way of telling the computer how to draw your circular shape. This is exactly how the Tk designers chose to do it. Hence we can specify that our oval must fit in the very same box shape that we just drew for the rectangle. If you don't quite understand this ... don't worry it will hopefully become clearer when you see the final image.
The only other thing we need to mention in connection with our canvas
shapes is the "tag" attribute. Think of the tag
as you would your underwear label. It's invisible when
your underwear is on but helps you identify which way is inside out when
your undewear is off. Tags on canvas shapes help us identify
them uniquely in case we want to change things about them later on ...
such as turning the "light" on or off.
Step 3: be patient |
| Be patient we are almost done. | ![]() |
The final bit we need to add to make our stoplight program functional
is the following adaptation of our "hogwarts" command from lesson #3.
#=======================================
# hogwarts - entry point
#=======================================
proc hogwarts { mycolor } {
global f_a
global f_b
puts stdout [format "lastcolor was %s" $mycolor]
if { $mycolor == "red" } {
$f_a.c itemconfigure redback -fill black
$f_a.c itemconfigure greenback -fill white
set newcolor green
} else {
if { $mycolor == "yellow" } {
$f_a.c itemconfigure yellowback -fill black
$f_a.c itemconfigure redback -fill white
set newcolor red
} else {
$f_a.c itemconfigure greenback -fill black
$f_a.c itemconfigure yellowback -fill white
set newcolor yellow
}
}
puts stdout [format "newcolor is %s" $newcolor]
return $newcolor
} ;# end of proc hogwarts
|
The only bits that have really changed here are summarized by the repeated
statements like the two below.
$f_a.c itemconfigure redback -fill black $f_a.c itemconfigure greenback -fill white |
We have already exploited the ability of Tk to allow us to change attributes
(such as the color) of a widget even after it is on the screen.
We did this in lesson #3 when we were able to change the color of our button
background. We can do the same with shapes drawn on our
canvas. For that purpose we use the canvas "itemconfigure"
construct above. Notice the use of the both the canvas
widget name and the shape tagname in each of these statements.
Step 4: final listing |
The program in operation should look like the screen below.
The full listing of the source code for lesson #4 is below.
#=======================================
# hogwarts - entry point
#=======================================
proc hogwarts { mycolor } {
global f_a
global f_b
puts stdout [format "lastcolor was %s" $mycolor]
if { $mycolor == "red" } {
$f_a.c itemconfigure redback -fill black
$f_a.c itemconfigure greenback -fill white
set newcolor green
} else {
if { $mycolor == "yellow" } {
$f_a.c itemconfigure yellowback -fill black
$f_a.c itemconfigure redback -fill white
set newcolor red
} else {
$f_a.c itemconfigure greenback -fill black
$f_a.c itemconfigure yellowback -fill white
set newcolor yellow
}
}
puts stdout [format "newcolor is %s" $newcolor]
return $newcolor
} ;# end of proc hogwarts
#========================================
# createStoplight - entry point
#========================================
proc createStoplight { } {
global f_a
$f_a.c create rectangle \
100 25 150 75 -fill white -tag redback
$f_a.c create oval \
100 25 150 75 -fill red -tag redlight
$f_a.c create rectangle \
100 75 150 125 -fill black -tag yellowback
$f_a.c create oval \
100 75 150 125 -fill yellow -tag yellowlight
$f_a.c create rectangle \
100 125 150 175 -fill black -tag greenback
$f_a.c create oval \
100 125 150 175 -fill green -tag greenlight
} ;# end createStoplight
#========================================
# main - entry point
#========================================
destroy .myArea
set f [frame .myArea -borderwidth 5 -background blue]
set f_a [frame $f.animateFrame -background lightBlue]
set f_b [frame $f.buttons]
set lastcolor red
canvas $f_a.c -height 200 -width 300 -background lightBlue
pack $f_a.c -side top
button $f_b.hello -text "change me" \
-command {set lastcolor [hogwarts $lastcolor]}
pack $f_b.hello -side left
place $f_a -x 10 -y 10
place $f_b -x 10 -y 250
pack $f -side top -expand true -fill both
createStoplight
|
Summary |
You have been introduced to a bunch of new Tcl/Tk statements and constructs in this lesson. Let's review:
Exercises |
End of Lesson 4.