Tcl/Tk GUI Programming
Lesson #7: text animation

Course content Enter chat room Send email
to mailing list
Check calendar

In memory of Linda.  Worldwide Cancer sites here.    Canadian Cancer donations here.
 

In this lesson we are going to continue to work with the menu widget.    In particular you are going to learn how to disable (dim out) and reenable menu items from within your program.

We are going to introduce our first text animiation concepts.     We are going to apply these to building a ticker tape text area.

Since we gave you a break from homework after the last lesson there is no solution to the exercises to present.
 
 
HINT:
Some of you have no doubt discovered that although Tcl/Tk is a friendly enough programming language it does have its share of quirks.     For some reason this lesson brings those out more than others.

Recall from a much earlier lesson that all Tcl/Tk statements are built up from a 
                                  command arg1 arg2 arg3 etc.
The way the Wish program can distinguish between elements in a statement is from the spaces in between.

WHEN IN DOUBT ADD A SPACE.    It is not always easy to pickup where they are on the webpage.


Step 0: preparation for lesson#7

As with all the previous lessons let's start by creating a new folder inside our level1 folder called "lesson7".

Some of  you may have noticed that programmers are inherently a lazy bunch.   We generally try not to have to retype things if we can pinch something close enough from somewhere else and modify it to suit.     Let's do just that for lesson7.tcl ... lesson6.tcl is close enough to save us some typing so let's make a copy of lesson6.tcl into our lesson7 folder and rename it as lesson7.tcl.

Using your editor modify the main area of the program as follows.
 
#========================================
# explicitly setup our main window
#========================================
wm geometry  .   400x75+10+10
wm title  .   "lesson 7"

This will make our wish window a little wider and squatter in anticipation that our ticker will be only needing that type of space.
 
 

Step 1: adding the ticker area

In the same main area of the code modify the text widget lines to look as follows:
 
#========================================
# add the ticker area as a text widget
#========================================
text $f.t  \
	-bd 2 \
	-bg white \
	-height 1 \
	-relief ridge \
	-wrap none \
	-state disabled

pack $f.t -side bottom
pack $f -side top -expand true -fill both 

Notice that we have reintroduced the "statement continues on the next line" character ... the backslash "\".      This is a convenient way to display long statement lines in your code listing to enhance readability.

Notice also that we have added some additional attributes to our new text widget.

-relief ridge    gives our text area a raised border effect
-wrap none    will be necessary when we come to have our text drop off the screen to the left
-state disabled    this prevents anyone from typing into this text area

Next we need to remove the statements which attempt to add text to the text widget.
 
#==========================
# add a line of text
#==========================
$f.t insert end "abc\n" tag0
$f.t insert end "def" tag1
REMEMBER THESE LINES ABOVE ARE TO BE REMOVED

Run your program with these changes and you should see the new look for our text widget.
 

Step 2: the Menus

We want to add a Ticker menu to our menu bar and then a "Start" option to that menu.

Using your editor modify the menu section to look like that below:
 
#========================================
#  create a pull down menu with a label 
#========================================
set File [menu .menubar.mFile]
.menubar add cascade -label File  -menu  .menubar.mFile

#========================================
# add the Open menu item
#========================================
$File add command -label Open -command openFile

#========================================
# add the menu item
#========================================
$File add command -label Quit -command exit 

#========================================
#  create a Ticker pull down menu 
#========================================
set Ticker [menu .menubar.mTicker]
.menubar add cascade -label Ticker  -menu  .menubar.mTicker

#========================================
# add the  Start menu item
#========================================
$Ticker add command -label Start -command toggleTicker -state disabled

Rerun your program now.
 
 
HINT:

Recall in a previous lesson we introduced the concept of a programming cycle:   change it, save it, run it, look at it ... repeat until satisfied.

Notice that we removed the Save menu item from the File menu.    We added a new Ticker menu with a "dimmed out" Start item. ie. the text shows up "dimmed" and the mouse will not hilite it in any way.

In the next section we are going to see how to manipulate this dimmed menu item from inside our program.
 

Step 3: manipulating the Ticker menu

Notice in the code snip above that we introduced a new - as yet to be written - command called toggleTicker.

Before we demonstrate how to do this, let me explain what we want our toggleTicker procedure to do.

When this program is completed we will want to be able to load some text into our Ticker tape and then Start it "ticking".    We will want that to happen via this Start menu item.     However, when we do that we want that menu item to change it's label to read Stop.     So that the next time we select the Ticker menu we can Stop our ticker tape from running.     When we click stop we want our label to go back to Start.

Start/Stop/Start/Stop ...   in other words we want our menu item to behave like a toggle switch with two positions.    Each time we press it , it reverts to the other position.

Using your text editor once again let's create a procedure called toggleTicker and place it at the beginning of our program.
 
#==================================
#   toggleTicker - entry point
#   Note: spaces are very important on a proc line
#==================================
proc toggleTicker { } {
set fn "toggleTicker"

puts stdout [format "%s:ding" $fn]

puts stdout [format "%s:done" $fn]

} ;#end toggleTicker

Let's run our program and see what happens.

Oops !!!    Our procedure can never run because we have disabled the menu item.

Let's temporarily change that until we get our little procedure more "fleshed out".       This is a great programming technique called "commenting out".

Using your text editor once again locate the menu line below and make the following changes
 
# TEMPORARILY COMMENTED OUT FOR TESTING
#$Ticker add command -label Start -command toggleTicker -state disabled

# FOR TESTING ONLY
$Ticker add command -label Start -command toggleTicker

Notice how we used the "#" to temporarily disable the original statement.    We then duplicated it and reenabled it without the -state disabled parameter set.

Now when you run your program and select the Start menu item the toggleTicker messages should be appearing on your wish console window.

While we are at it let's temporarily disable  the Open menu option which we are not using yet.
 
# TEMPORARILY COMMENTED OUT FOR TESTING
#$File add command -label Open -command openFile

# FOR TESTING ONLY
$File add command -label Open -command openFile -state disabled

 
REMEMBER:

This programming stuff may not come easily at first.   Not to worry.     If you are having difficulties don't hesitate to contact the mailing list or use the chat room resources for assistance.     At this beginner level there is no such thing as a stupid question to ask.

Let us see if we can now make our toggleTicker actually do some toggling.

The first thing we are going to need is a variable which is going to change value alongside the toggle.     This is how our program is going to keep track of what state the toggle is current set at.

Just below the Ticker menu stuff at the end of our program listing let's add a section for initialization of some variables.    These variables are sometimes called "global variables" because they are going to be used in more than one procedure
 
#=====================================
#   initialize some global variables
#=====================================

set tickerState "stopped"

Here we are going to initialize our tickerState to "stopped".   In other words the ticker tape is not "running" ... which incidently will be the other state that this variable will take on.

Now let's enhance out toggleTicker procedure to accept this global variable and print it out at the incoming point and just before it returns.
 
#==================================
#   toggleTicker - entry point
#==================================
proc toggleTicker { } {
set fn "toggleTicker"
global tickerState

puts stdout [format "%s:ding  tickerState=<%s>" $fn $tickerState]

puts stdout [format "%s:done  tickerState=<%s>" $fn $tickerState]

} ;#end toggleTicker

It should now say "stopped" at both the "ding" and "done" points.

Let's add some toggle logic.      This will be very similar to the stoplight logic that you used in a previous lesson.

So if you want to stop here and try it on your own ... DON'T PEEK AT THE SOLUTION BELOW.
 
 
#==================================
#   toggleTicker - entry point
#==================================
proc toggleTicker {  }  {
set fn "toggleTicker"
global tickerState

puts stdout [format "%s:ding  tickerState=<%s>" $fn $tickerState]

if { $tickerState == "running" } {
	set tickerState "stopped"
	puts stdout [format "ticker is now stopped"]
} else {
	set tickerState "running"
	puts stdout [format "ticker is now running"]
} ;# end if

puts stdout [format "%s:done  toggleState=<%s>" $fn $toggleState]

} ;#end toggleTicker

When this program is run repeated selection of the Ticker/Start menu item produces the desired toggle effect on the messages appearing in the Wish console window.

What we want to achieve as well is a toggling of the menu label from Start to Stop as well.

For this we need to introduce a menu configuration statement into each branch of the if/else as shown below.
 
global tickerState
global Ticker

puts stdout [format "%s:ding  tickerState=<%s>" $fn $tickerState]

if { $tickerState == "running" } {
	set tickerState "stopped"
	$Ticker entryconfigure Stop -label Start
	puts stdout [format "ticker is now stopped"]
} else {
	set tickerState "running"
	$Ticker entryconfigure Start -label Stop
	puts stdout [format "ticker is now running"]
} ;# end if

When this program is run you should see the Ticker menu toggling between Start and Stop in a consistent context with the state message showing on the console.

We are now ready for the Ticker tape portion.
 

Step 4:  the Ticker tape

Remember  text widget we created on the bottom of our window.   We are going to work on turning that area into a scrolling text area commonly called a ticker tape.

For this we are going to create another new procedure at the top of our program listing called "ticker" as follows:
 
#==========================
#  ticker - entry point
#==========================
proc ticker  { t }  {
set fn "ticker"
global text_messages
global maxMessages

puts stdout [format "%s:ding" $fn ]

puts stdout [format "%s:done" $fn ]

} ;#end ticker

In the main area at the very bottom of the program let's add some temporary lines to call our ticker procedure.
 
#==========================
#   THIS IS TEMPORARY
#    TO TEST ticker PROCEDURE
#==========================
ticker  $f.t

If all is well when you run this program you should see the

ticker:ding
ticker:done
message pair appear in the Wish console window.     GOOD.    You have your tickle procedure at least being called.

Let's begin to add some meat to this procedure to have it do something useful.     In particular we want to have it put our ticker message into the text area at the bottom of our window.

Notice we added some global variables (text_messages and maxMessages) to this procedure.    We are now going to make use of them:
 
#==========================
#  ticker - entry point
#==========================
proc ticker  { t }  {
set fn "ticker"
global text_messages
global maxMessages

puts stdout [format "%s:ding" $fn ]

set text_messages(0) Zero

set maxMessages 1

for { set j 0 } { $j < $maxMessages} { incr j } {
	puts stdout [ format  "array(%d)=<%s>"  $j  $text_messages($j) ] 
} ;#end for

puts stdout [format "%s:done" $fn ]

 ;#end ticker

Run it and then we'll explain.

We have introduced a completely new Tcl/Tk construct in this section called an array.

The lines in question are:
 
set text_messages(0) Zero

puts stdout [ format "array(%d)=<%s>"  $j  $text_messages($j) ] 

Arrays in any programming language are ways to collect a list of items under a single variable name.    It would be like creating a grocery list and then labeling each item on that list as:

grocery1
grocery2
grocery3
etc.
The array is a variable with two parts.    The name and the index.    Tcl/Tk is rather unique as a language because the array index can be anything at all.     Most computer languages restrict array indices to being numeric values.     We won't be exploiting Tcl/Tk's special capabilities and our array indices will all be numeric.

The first of the lines above illustrates how we declare an array variable in Tcl/Tk.     You simply include the "(index)" part and it is done!    In other words we have told the Wish program that text_messages is an array and the 1st item is to be set to Zero.

Wait a minute !!!   The first item?

That's right if you recall we encountered a similar "starting from 0" counting scheme in our previous lesson on loops.    Don't worry about it ... it is common for computer programming language designers to want to start counting from 0,1,2 etc.

The second of our lines above illustrates how we can extract the contents of our new array variable.

$text_messages($j)
where our index in this case is now a variable itself ($j).

This ability forms the basis of the power of arrays as a programming construct.    The ability to index into them with variables as indices.

Let's try to illustrate by adding some more items to our little array.
 
set text_messages(0) Zero
set text_messages(1) One
set text_messages(2) Two
set text_messages(3) Three

# maxMessages is always set to the next available array index
set maxMessages 4

Arrays are a very useful programming construct indeed.

We now need to get our array contents to show up in our ticker area.
 
for { set j 0 } { $j < $maxMessages } { incr j } {
	puts stdout [ format  "array(%d)=<%s>"  $j  $text_messages($j) ]

	$t configure -state normal
	$t insert end  $text_messages($j)  tag$j
	$t configure -state disabled

} ;#end for

Notice the "t" variable that we passed into this procedure is actually the set the to "$f.t" variable which is how we represented our ticker text widget.    In addition we also left the text area disabled so that we could not interact with it via the mouse or keyboard.    Unfortunately,   this disabled state prevents even our program itself from interacting with the text widget.

Hence we have to temporarily reenable the widget (-state normal) ... do our thing ... and then disable it once more.

When you run the program now you should see:

ZeroOneTwoThree
Appear in the ticker area.

This is good, but we'd like to achieve some separation between the individual ticker items.

We need to do this separation in a general way, so let's start by altering our ticker procedure call and parameter list.
 
#==========================
#   THIS IS TEMPORARY
#    TO TEST ticker PROCEDURE
#==========================
ticker  $f.t  " +++ "

and
 
#==========================
#  ticker - entry point
#==========================
proc ticker { t filler } {

If we now make use of this filler string we can space apart each item from our array as it gets added to the ticker widget.
 
$t configure -state normal
$t insert end $text_messages($j)  tag$j
$t insert end $filler fill
$t configure -state disabled

All that remains to be done in this lesson is to make the ticker actually move across the screen.
 
 

Step 5:  making the Ticker tape move

We need to add some more parameters to our ticker procedure, both in the call and at the definition point.
 
#==========================
#   THIS IS TEMPORARY
#    TO TEST ticker PROCEDURE
#==========================
ticker  $f.t  400  " +++ "  95

and
 
#==========================
#  ticker - entry point
#==========================
proc ticker  { t delay filler minlen } {

We also need to add a new global variable called index.    This is to be added in the "initialize some global variables" section near the bottom of the main area in the program as below:
 
#=====================================
#   initialize some global variables
#=====================================
set tickerState "stopped"
set index 0

These changes should have no effect on our program yet.

We also need to modify the "else" branch inside the toggleTicker procedure as follows
 
} else {
	set tickerState "running"
	$Ticker entryconfigure Start -label Stop
	ticker  $f.t  400  " +++ "  95
	puts stdout [ format  "ticker is now running" ]
} ;# end if

We will be needing the "ticker" line here to act as a trigger for our ticker tape animation to follow.   Since we have made use of the previously defined "f" frame variable here we need to add a
 
global f

statement along with the other "global" calls near the top of the toggleTicker procedure.
 

Now let's modify our ticker procedure to look like:
#==========================
#  ticker - entry point
#==========================
proc ticker { t delay filler minlen } {
set fn "ticker"
global text_messages
global maxMessages
global index

puts stdout [format "%s:ding" $fn ]

set text_messages(0)  Zero
set text_messages(1)  One
set text_messages(2)  Two
set text_messages(3)  Three

set maxMessages  4

scan [ $t index 1.end ] %d.%d  line  len

# add to ticker
if  { $len  <  $minlen }  {
	if  { $index  >=  $maxMessages }  { set  index  0 }

	set  j  $index

	puts stdout [ format "array(%d)=<%s>"  $j  $text_messages($j) ] 
	$t  configure  -state  normal
	$t  insert  end  $text_messages($j)  tag$j
	$t  insert  end  $filler  fill
	$t  configure -state  disabled

	incr index
} ;#end if

# move ticker one character to the left
$t  configure -state  normal
$t  delete  1.0
$t  configure -state  disabled

puts stdout [format "%s:done" $fn ]

} ;#end ticker

Run this program and toggle the Start/Stop menu item under Ticker menu a few times.     You should notice that the text in the ticker area begins to change and scroll slowly off to the left.

We have introduced a new statement here called scan.       You can best think of scan as an opposite to format.     With format we are converting variables into displayable strings.     With scan we are converting strings of data back into individual variables.

The construct inside the [ ] on the scan line is used to return the text widget index for the end of the 1st line.      For the text widget this index will be in the form of:

linenumber.characternumber
We are simply using the scan statement to extract the linenumber into a variable called line and the characternumber into a variable called len.      In other words this is a "clever" way to extract the length of the line.

To understand our ticker motion more clearly we need to identify two distinct operations:  the adding to the end of the ticker tape; the removal of text on the left side of the ticker tape.      The adding part insures that the ticker always is replenished.      The deletion of the leftmost character in the text widget  is what makes the ticker move from right to left.

The addition block is inside the if statement noted by "add to ticker" comment.      This warrants a small explaination.
 
# add to ticker
if  { $len  <  $minlen }  {
	if { $index  >=  $maxMessages }  { set  index  0 }

Rather than develop some complex logic to add to text to the end of the ticker area on a character by character basis, we have chosen a simpler approach.     Firstly we are going to work in the text widget area that is beyond the visible portion.    In other words in the area that would be to the right of the visible text window.     This is why we set "minlen" to be 95 characters.       At any given time only 50 or so characters are visible in the ticker area.

Our algorithm has the actual text len compared to this "minlen".    Once we detect that the text in the widget has dropped below this minlen threshold we are going to add the next complete word from the array and a filler message following it.    The position in the array is marked by our variable called "index".      The if statement and the incr statement

if { $index  >=  $maxMessages }  { set  index  0 }
incr  index
will insure that index counts 0,1,2,3,4,0,1,2,3,4etc.     If you don't understand how this works study the lines a little more and then ask the mailing list or chat room for some help.

The last remaining thing we need to do is to "animate" the ticker.     In other words we need to make it operate on its own.

Tcl/Tk has some very simple but powerful animation capabilities built into the language.    We are going to take advantage of one of the simplest of these.   Tcl/Tk has the ability to tell the program to "queue" up a statement for execution after some time has elapsed.

All we need to do is to add the following statements to the end of our "ticker" procedure.   NOTE: that we commented out the puts statement near the end.   You probably want to do the same with the "ding" puts statement at the top of the ticker procedure.    This will save activity on your console window once you start the animation.
 
$t  configure -state  normal
$t  delete  1.0
$t  configure  -state  disabled

if  { $tickerState  ==  "running" }  {
	after  $delay  [ list  ticker  $t  $delay  $filler  $minlen ]
} ;#end if tickerState

#puts stdout [ format "%s:done" $fn  ]

} ;#end ticker

and a new global statement near the top of the ticker procedure.
 
global  tickerState

 
Congratulations !

You have just written your very first animated Tcl/Tk program.

Before we wrap up this lesson let's examine the "after" statement in more detail.
 
after $delay [ list ticker $t $delay $filler $minlen ]

The "after" statement tells the Tcl/Tk Wish program to queue something for execution at some future point in time.    The key concept here is "queue".     Your Wish program will continue execution beyond the "after" statement.     This is often a difficult concept to understand.    All the "after" statement did is tell the Wish program to set something up to happen in the future but continue on as usual once that is done.    When that future time arrives the statement we "queued"  will be run  in much the same way that clicking the mouse on a button in an earlier lesson caused a procedure to run.    This would be very analogous to setting your alarm clock or the automatic bake feature on an oven.    Bake start time is reached and oven goes on.
 
 
This brings up an  characteristic of programming for GUI's as we are doing here:   we trade ease off writing a complex program  against the loss of "cause - effect" control.     The Wish program is doing a tremendous amount of the work for us in behind the scenes.      If we are trying to follow the flow of our little program in a way we might follow the plot in a good novel we find that the "execution flow" is very disconnected.     The computer itself isn't seeing a disconnected "plot" in the least ...  it is being told to do this ... then that ... then the next thing in a very orderly fashion.     The problem is most of that activity is originating in the Wish  program itself and we can't see it in our source code.

Returning to our "after" statement ...the 1st argument ($delay in our case) represents the number of milliseconds in the future we wish to set our "alarm clock" for.

When that "alarm clock" goes off we are going to arrange to run the statement that results from the operation inside the [ ] ... how's that for a mouthful.
 
list ticker $t $delay $filler $minlen

This introduces our last Tcl/Tk statement of this lesson ... the "list" statement.        What list does is effectively substitute for each of the $ variables and produce a result which would read.

ticker  .myarea.t  400  " +++ "  95
It is this statement which we will be running 400ms into the future ... when the "alarm clock" sounds.
 
 

Step 6: final listing

The full listing of the source code for lesson #7 is below.
 
#==========================
#  ticker - entry point
#==========================
proc ticker  { t  delay  filler  minlen } {
set fn "ticker"
global text_messages
global maxMessages
global index
global tickerState

#puts stdout [format "%s:ding" $fn ]

set text_messages(0) Zero
set text_messages(1) One
set text_messages(2) Two
set text_messages(3) Three

set maxMessages 4

scan [$t index 1.end] %d.%d line len

# add to ticker
if  { $len  <  $minlen }  {
	if { $index  >=  $maxMessages }  { set  index  0 }
	set j $index

	puts stdout [format "array(%d)=<%s>" $j $text_messages($j)] 
	$t configure -state normal
	$t insert end $text_messages($j) tag$j
	$t insert end $filler fill
	$t configure -state disabled

	incr index
} ;#end if 

# move ticker one character to the left
$t configure -state normal
$t delete 1.0
$t configure -state disabled

if { $tickerState == "running" } {
	after $delay [ list ticker $t $delay $filler $minlen ]
}; #end if tickerState

#puts stdout [format "%s:done" $fn ]

} ;#end ticker


#==================================
#   toggleTicker - entry point
#==================================
proc toggleTicker {  }  {
set fn "toggleTicker"
global tickerState
global Ticker
global f

puts stdout [format "%s:ding  tickerState=<%s>" $fn $tickerState]

if { $tickerState == "running" }  {
	set tickerState "stopped"
	$Ticker entryconfigure Stop -label Start
	puts stdout [format "ticker is now stopped"]
} else {
	set tickerState "running"
	$Ticker entryconfigure Start -label Stop
	ticker $f.t 400 " +++ " 95
	puts stdout [format "ticker is now running"]
} ;# end if

puts stdout [format "%s:done  tickerState=<%s>" $fn $tickerState]

} ;#end toggleTicker



#==========================
#  saveFile - entry point
#==========================
proc saveFile { }  {
set fn "saveFile"
global f

set myFile [tk_getSaveFile]

puts stdout [format "%s:myFile=<%s>" $fn $myFile]

set fileID [open $myFile w]

set line [$f.t get 1.0 1.end]
for {set i 2} {[string length $line] > 0 } {incr i } {
	puts stdout [format "line=<%s>" $line]
	puts $fileID $line
	set line [$f.t get $i.0 $i.end]
} ; end for loop

close $fileID

} ;#end saveFile 



#==========================
#  openFile - entry point
#==========================
proc openFile { }  {
set fn "openFile"
global f

set myFile [tk_getOpenFile]

puts stdout [format "%s:myFile=<%s>" $fn $myFile]

set fileID [open $myFile r]
set i 1
$f.t delete 1.0 end
while { [ gets $fileID line ] >= 0 } {
	puts stdout [format "line(%d)=%s" $i $line]
	$f.t insert end [format "%s\n" $line]
	incr i
} ;#end while

close $fileID

} ;#end openFile



#========================================
#  main - entry point
#========================================

#========================================
# explicitly setup our main window
#========================================
wm geometry  .   400x75+10+10
wm title  .   "lesson 7"

#========================================
# setup the frame stuff
#========================================
destroy .myArea
set f [frame .myArea -borderwidth 5 -background blue]

#========================================
# add the ticker area as a text widget
#========================================
text $f.t  \
	-bd 2 \
	-bg white \
	-height 1 \
	-relief ridge \
	-wrap none \
	-state disabled

pack $f.t  -side bottom
pack $f -side top -expand true -fill both 

#========================================
# create a menubar
#========================================
menu .menubar
. config -menu .menubar

#========================================
#  create a pull down menu with a label 
#========================================
set File [menu .menubar.mFile]
.menubar add cascade -label File  -menu  .menubar.mFile

#========================================
# add the Open menu item
#========================================
#$File add command -label Open -command openFile

#TEMPORARY FOR TESTING
$File add command -label Open -command openFile -state disabled

#========================================
# add the menu item
#========================================
$File add command -label Quit -command exit 

#========================================
#  create a Ticker pull down menu 
#========================================
set Ticker [menu .menubar.mTicker]
.menubar add cascade -label Ticker  -menu  .menubar.mTicker

#========================================
# add the  Start menu item
#========================================
# TEMPORARILY COMMENTED OUT FOR TESTING
#$Ticker add command -label Start -command toggleTicker  -state disabled

# FOR TESTING ONLY
$Ticker add command -label Start -command toggleTicker 

#=====================================
#   initialize some global variables
#=====================================
set tickerState "stopped"
set index 0

#==========================
#   THIS IS TEMPORARY
#    TO TEST ticker PROCEDURE
#==========================
ticker $f.t 400 " +++ " 95

For those running PCWindows the result of running this program should be something like:


 

Summary

You have been introduced to a bunch of new Tcl/Tk statements and constructs in this lesson.   Let's review:

Exercises

Lesson #8 will be your final lesson for this course.   In that we will attempt to wrap together the file editor we built in lesson #6 with the ticker we built in this lesson.

As an exercise see if you can reenable the Open menu option and have the contents of the file you open be placed into the ticker tape.

End of Lesson 7.


Copyright of iCanProgram Inc. 2002