Journal Articles
Browse in : |
All
> Journals
> CVu
> 173
(15)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Introduction to Tcl/Tk: Part 2
Author: Administrator
Date: 02 June 2005 05:00:00 +01:00 or Thu, 02 June 2005 05:00:00 +01:00
Summary:
Body:
In our first article in this series we introduced the Tcl/Tk language and the accompanying programming cycle. We wrote some trivial programs and explored some basic concepts.
In this article we will go further and show that Tcl/Tk can be used to write some very capable applications.
Most of you will have used computer software with pull down menus. The familiar File Edit etc. menu bar is across the top of most program windows including the browser you are using to view this page right now. We are going to learn how to create menus in Tcl/Tk .
Before we begin we must discuss terminology.
-
a menu bar is the area at the top of a window on which we will hang our menus.
-
menu labels are the words in the menu bar where we click our mouse
-
the menus are the things which pop down when we click our mouse on a menu label in the menu bar.
-
the menu item is the thing we click on once the menu has popped down
Let us illustrate by way of an example.
Using the now familiar text editor create a new file and enter the following partial program:
#======================================== # main - entry point #======================================== destroy .menu bar menu .menu bar . config -menu .menu bar
Note the . in front of config is on its own. That is there is a space before the word config.
When you run this program with wish it won't be all that impressive. In fact for you Windows users it will look exactly like nothing happened. The problem here is that different computers (PC Windows, Mac, Linux) handle windows slightly differently.
This is as good a point as any to introduce a couple of constructs which will help make our Tk windows behave better.
Using your text editor once again add the following statements just before the menu .menu bar statement above.
wm geometry . 200x250+10+10 wm title . my program
Don't forget to leave a space before and after the dot in these statements. The purpose of this dot will be come apparent shortly. In the meantime if you run your little program once again you should see a slightly bigger window with a proper my program displayed as the title. Unfortunately we still don't have an obvious menu bar showing up.
To help with this we need to introduce a new widget called a frame. 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. At this point we are going to simply use the frame to colour a section of our window thus leaving the menu bar obvious.
You will need to add the following statements after the wm title statement above.
destroy .myArea set f [frame .myArea -borderwidth 5 - background blue] pack $f -side top -expand true -fill both
Presto!
When we run our little program now at least Windows users will see a blank area under the title ... our menu bar! You may have to look quite hard to notice it. It's just above the blue area.
Let's examine the statements in more detail starting with the wm and frame statements first.
We notice a couple of new statements beginning with the command wm. Those of you running on a Linux platform might recognize that wm is short for window manager. Tcl/Tk was first developed on a UNIX platform. On the likes of Mac, Windows and Linux, the windowing interface is built in. The term window manager loses its meaning in that case. The designers of Tcl/Tk elected to keep with the name in any event.
For our purposes the wm command allows us to control the wish window. We can specify its size. We can specify where we want it to appear on the desktop. We can specify which title is to appear at the top of the window.
The first of the wm commands concerns the location and size of our window:
wm geometry . 200x250+10+10
Recall that in the first Tk article I mentioned that the window in Tk is known simply as . (dot). The explanation lies once again in Tk's Unix roots. Suffice to say that the . immediately after the word geometry in the command is the name of the Tk window. The next argument consists of a set of 4 numbers joined by a x and a couple of +. The first pair of numbers represents the width x height of the window in units of pixels. Our window is to be 200 pixels wide and 250 pixels high. The next pair of numbers represents the position of the window as measured in pixels from the left of the desktop screen and the top of the desktop screen. Our window will be 10 pixels in from the left and 10 pixels down from the top of the desktop screen.
The second wm statement is somewhat obvious:
wm title . my program
It will add the title my program to the top of our wish window when the program starts running.
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 the variable 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 colour. This statement is exactly analogous to the button .hello statement which we had in previous examples in our first article.
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.
Finally back to discussing menu bars!
The first thing we need to recognize is that the Tcl/Tk menu widget designers are a cruel lot. They decided that menu bars and menus are really just the menu widget with different attributes turned on. I for one find this confusing ... but bear with me it is really not all that bad.
The first statement creates menu bar instance of a menu widget called ... you guessed it ... menu bar.
menu .menu bar
This is exactly similar to the
button .hello
statement that we used in the previous article to create an instance of a button widget.
The command menu creates a menu widget. The argument .menubar says call it menubar and attach it in the main window (called .). As with our previous example of the button, nothing will happen until we force the widget to render itself visible. In the previous button example this was accomplished with the pack command.Menu widgets are special and we don't use a pack command to render them.
This is because menus cannot exist unless they are attached directly to other widgets. In our little example What other widgets do we have up? you might ask. The answer is we always have the main window up which is itself a widget. Because menus can only exist when part of a window, the designers of Tcl/Tk decided to manipulate the menu widget as a special window attribute.
Stay with me here ... it does get easier once we are through this part. This does explain the next statement:
. config -menu .menubar
A little tricky ... but not overly so. The leading dot is the representation for the main (or parent) window widget. The first argument configure (or config in the shortened form) behaves exactly the same as the configure argument we used to change an attribute of our button widget in the previous example. In this case we are changing the attributes of the main window itself. What we are doing is attaching our menu widget as the menu bar in the window. Just to thoroughly confuse everything the Tcl/Tk designers decided to use -menu rather than -menu bar to do this attachment. So much for concise terminology.
There is a saying amongst programmers which goes like this:
If you don't understand a set of statements thoroughly, go ahead and use them anyway as long as they work.
It is probably good advice for the menubar.
Let's enhance the program with some menu labels now. For this, add these two statements to the bottom of the program above:
set File [menu .menu bar.mFile] .menu bar add cascade -label File -menu .menu bar.mFile
When you run the program now you will see that it will have added a label called File to our menu bar. If you press this label with your mouse you will see that it tries to pop up a menu but all it succeeds in doing is showing a small dashed line.
Here's where the Tcl/Tk menu widget designers tax our understanding a little more.
The first statement is an example of a compound statement not unlike the format statements we have already used. Remember that the stuff inside the [ ] gets worked out first. The result of that is substituted and the whole statement is then resolved. The [ ] above creates another menu widget we have called .menu bar.mFile. We are then calling this new widget File using the set command. Recall from our discussion above how a menu widget cannot exist on its own ... it must be attached to another widget. This other widget could just as well be another already existing menu widget ... our menu bar for example.
The statement :
.menubar add cascade -label File -menu .menu bar.mFile
does the attachment. The -label attribute is what gives the File menu label.
It now might make sense why we chose the awful name for the newest menu widget:
.menu bar.mFile
Think of it as a convenient way of showing a hierarchy. top level window called . -> menu widget called menu bar -> menu widget called mFile
If you don't understand this ... don't worry just use it and your program will work.
Recall that by using the $ in front of a variable means use the value stored at. In the section above we used to set command to create a variable we called File.
We can now reference our pull down menu widget as $File.
Add the following statement to the end of the program.
$File add command -label Quit
When you run the program it will look much more like a graphical menu. The menu item Quit should appear. However, when you click your mouse on the item nothing happens ... yet.
If you recall the button example we had the same challenge. We resolved that with a -command attribute. Menus are no different. The only thing we need to add now is an action command when the particular menu item is selected.
The menu widget supports the -command attribute much like the button widget did before. Let's enhance our last statement to look like:
$File add command -label Quit -command exit
Congratulations! You now have created your very first menu driven Tcl/Tk program. If you click on the Quit menu item now your little program should exit cleanly.
Another useful Tk widget is the text widget. We will be making extensive use of the text widget as we expand our exploration to ever more complex examples. Besides the fact that we will be using them in our examples, the text widget is also one of the most feature rich and useful of the Tcl/Tk widgets.
In case you are wondering exactly what is a text widget anyway ... think of your text editor. A properly configured text widget is fully capable of being used as a text editor.
The first enhancement we are going to make to our little program is to add the text widget into our frame area.
Using your editor scroll down inside your source file until you reach the set f [ frame statement. You are going to insert the text widget lines between this statement and the pack $f statement as illustrated with the code snip below.
set f [frame .myArea -borderwidth 5 -background blue] #======================================== # add the text widget #======================================== text $f.t -bd 2 -bg white -height 5 pack $f.t pack $f -side top -expand true -fill both
When you run this program with wish a small white box should appear near the top of the blue frame area.
That wasn't that hard was it!
You now added a text widget to your program. Try typing something into the little white box that appears. You should be able to do many of the things you are familiar with in your text editor ... things like inserting, overwriting, highlighting, and deleting text.
It would be a subject for a more advanced discussion to look at all the things you can do with text inside a text widget. Suffice to say we will only be using a tiny fraction of that capability in this exercise.
But first let's not get too far ahead of ourselves and let's look at the new statements that we have introduced.
text $f.t -bd 2 -bg white -height 5
This statement begins with the widget invocation command text. This creates a text widget. This is very similar to the command you used previously to create a button, menu or a frame.
-
The first argument is a little strange. It says we are going to name our new text widget as $f.t. Recall that the $ has special meaning in the Tcl/Tk language. It means substitute the value of the variable in my place. In this case that variable contains the frame widget itself.
-
What $f.t really means to us is simply that the text widget is going to be added as a child widget to the frame. Remember the hierarchy ... parent->child -> grandchild etc. Simply put, the text widget is inside the frame.
-
The next argument is short for border. It simply says leave 2 pixels of space all around the text widget as a border.
-
The next argument is short for background. It simply says that we want the background of our text widget to be white.
-
The final argument says make this widget 5 lines high. In other words it will be big enough to store 5 lines of text.
There are many more arguments that can be used to set attributes of this text widget but the few we have chosen are sufficient for our purposes.
The other new statement:
pack $f.t
we have encountered before. For our purposes it says render our widget named $f.t visible please.
Our text widget would not be of much use if we could not add lines of text to it from within our little program.
Using your text editor once again add the following statements just after the pack $f statement above.
#========================== # add a line of text #========================== $f.t insert end abc tag0
When you run your newest version of our little program now the letters abc appear in the first line of the text box.
Let's examine what the new statement actually does.
This statement is a lot like the configure statement we used in the past with the menu bar. In English it would read something like:
"to the end of the visible text in the text box called $f.t insert a new string of text containing abc and mark that string with an invisible label called tag0."
To help illustrate how this insert statement works try adding another insert line immediately after this one like.
$f.t insert end def tag1
Notice how the string is now abcdef. The insert simply appended the new text to the end of what was already there.
How do we put def on the line below? The easiest way is to add a special character to the end of the first line. This is the same character that would be inserted in your text editor when you hit return . In deference to the days when this key actually did mechanical things on a typewriter it is called the carriage return character. In word processors and in text editors it is invisible on the screen but actually exists in the file. That is why if you hit backspace at the beginning of a line it suddenly gets merged to the end of the previous line. Reason: you just deleted that invisible carriage return character that forced the line separation. In Tcl/Tk this carriage return is represented by \n.
So if we were to modify our abc line to look like
$f.t insert end abc\n tag0
we would force the def to go to the next line.
Now try positioning your cursor at the beginning of the def line in the text box and hit backspace on you keyboard. Now press return. You should be able to move def to end of the abc line or back to the second line.
Invisible characters are a huge part of every word processor program you have ever used. They control everything from line spacing to font type and point size.
A program with only a Quit menu item is not very useful. Traditionally under the File menu there is an Open menu item. Let's add the Open menu item by editing our source file and inserted the lines required immediately above those for the Quit item as per our code snip below:
#======================================== # add the Open menu item #======================================== $File add command -label Open #======================================== # add the menu item #======================================== $File add command -label Quit -command exit
Recall in the previous article introduced the very powerful concept of procedures. Procedures are used to add new statements to the Tcl/Tk language. In fact computer languages would be next to useless without that ability.
We are going to add a procedure which will be executed when we select the Open button in the menu.
At the top of your source file add the following few statements
#========================== # openFile - entry point #========================== proc openFile { } { set fn openFile puts stdout [format %s: ding $fn] } ;# end openFile
When you run your program now ... absolutely nothing should change.
"OK ... where is he going with this now?" you ask.
What happens if we enhance our Open menu item line to look like
$File add command -label Open -command openFile
Now what happens when you run your program and select the Open menu item under the File menu.
For those of you sceptics ... take a look into your wish console window. You should see the result of that puts stdout message on that window:
openFile: ding
Let's move on and introduce our first dialog.
Although you may not know it you have already used dialogs lots of times in your computer already. Simply put, a dialog is the name GUI designers gave to those small windows that pop up when you do things like opening a file, saving a file etc. A typical dialog asks you to supply input and won't let you do anything else with your computer until you have done so ... or hit the Cancel button to dismiss the window.
The Tcl/Tk language comes with a rich set of predesigned dialogs for doing a whole range of common things. A dialog serves exactly the purpose you might expect ... it provides information and solicits user input to select from amongst that information. One of the more common dialogs is that which is presented to users every time they wish to open a file.
Inside the procedure openFile add the following statement (just after the puts stdout ding line is as good as any place).
set myFile [tk_getOpenFile]
Now run the program.
This is really starting to look like a real program. In fact all the real programs you have been using on your computer all along are nothing more than more complicated versions of this little program you have written.
To use the writing analogy: the real programs are full novels ... you have learned how to write a paragraph. All that is required to produce a full novel is to connect lots of paragraphs together into a cohesive story.
The next enhancement we are going to make to our little program is to add the call to the save dialog, which mirrors our call to the open dialog we introduced above. Using your editor modify your source file to add the saveFile procedure as shown below.
proc saveFile { } { set fn saveFile set myFile [tk_getSaveFile] puts stdout [format %s:myFile=<%s> $fn $myFile] } ;#end saveFile
You'll also want to add the Save option under the File menu as indicated below.
#======================================== # add the Open menu item #======================================== $File add command -label Open -command openFile #======================================== # add the Save menu item #======================================== $File add command -label Save -command saveFile #======================================== # add the menu item #======================================== $File add command -label Quit -command exit
When you run this program with wish and select the Save menu option you should see a dialog appear asking you to enter the file name you wish to save things as. Try typing some imaginary file name and clicking the save button.
For those of you who were paying attention to what lines you modified above you would have expected that you should see a line of output appear on your wish console window. That line should look something like:
saveFile:myFile=<c:/myprog/bob.tcl>
The string of words in the <> above represent what is known in programming circles as a full file name. Notice that this full file name tells you exactly where to find your file. Each operating system (Windows, Mac or Linux) will have slightly different full file names.
You have used a hierarchy in your computer travels often without really knowing it exists. The graphical representation of the file system as folders and files shields this from you. But underneath that graphical veneer almost every computer file system behaves in much the same way. You can almost always access a given file by simply specifying the full path to that file.
Next time, we'll see if we can use our new found knowledge to actually access a file on our hard drive from within our Tcl/Tk program.
Notes:
More fields may be available via dynamicdata ..