package require Tk
proc getBestFitFontSize {win fontFamilyName words} {
# Get the size of the screen. Allow some room on the sides to ensure
# the word doesn't wrap
set maxWidth [expr [winfo screenwidth $win] - 15]
# Initial size guess based on the number of letters in the given words.
# The -1 is to make sure the initial guess is too big
set avgLetters [expr [string length [join $words {}]] / [llength $words] - 1]
if {$avgLetters == 0} {incr avgLetters}
set sizeGuess [expr $maxWidth / $avgLetters]
set font [list $fontFamilyName $sizeGuess]
set sizes {}
# Find the word that takes the most space and find how much space it take
foreach word $words {
lappend sizes [font measure $font $word]
}
set maxSize [lindex [lsort -integer -decreasing $sizes] 0]
set maxIdx [lsearch $sizes $maxSize]
set biggestWord [lindex $words $maxIdx]
# Shrink the size until the width of the biggest word fits
while {$maxSize > $maxWidth} {
incr sizeGuess -10 ;# Binary search would be more efficient. Not interested in making the effort
set font [list $fontFamilyName $sizeGuess]
set maxSize [font measure $font $biggestWord]
}
# If all the words are short (3 letters or so), then the result will tend
# to be a bit too large height-wise. Adjust if needed.
set height [expr [font metrics $font -ascent] + [font metrics $font -descent]]
set maxHeight [expr [winfo screenheight .] - 100] ;# 100 was chosen without any investigation if it is reasonable
while {$height > $maxHeight} {
incr sizeGuess -10
set font [list $fontFamilyName $sizeGuess]
set height [expr [font metrics $font -ascent] + [font metrics $font -descent]]
}
return $sizeGuess
}
proc ::showNextWord {wordList} {
# Replace the text in the text widget with the next word on the list
.words.t delete 1.0 end
.words.t insert 1.0 [lindex $wordList 0] centered
# Remove that word from the list. Rewrite the button callback so it
# contains the updated word list. Give a hint of the next word in the
# button text
set newWordList [lrange $wordList 1 end]
if {[llength $newWordList] > 0} {
.words.next configure -text "Next: [lindex $newWordList 0]" -command [list showNextWord $newWordList]
} else {
.words.next configure -text Close -command {destroy .words}
}
}
proc showWordSlideShow {wordList} {
toplevel .words
focus -force .words
# Maximize the window. From http://wiki.tcl.tk/2233
wm overrideredirect .words 1; wm geometry .words [join [wm maxsize .] x]+0+0
# Pick the font and size and create widgets
set font {Times New Roman}
set fontSize [getBestFitFontSize .words $font $wordList]
pack [::text .words.t -font [list $font $fontSize] -foreground red -height 1]
pack [button .words.next]
# Convenient, mouse-free operation
bind .words <Return> {.words.next invoke}
.words.t tag configure centered -justify center
# Start the slideshow
showNextWord $wordList
}
# Some test code
wm withdraw .
showWordSlideShow {applesauce banana car house pig}RS: My daughters are out of that age now, but my 13yo still enjoys teaching programs I hack for her in Tcl (vocabulary trainer, math trainer), even more if I add multimedial elements: play sounds (applause or short phrases), or display images. In this case it might be extremely didactic if large images of the object in question are shown (use Img for JPEGs), the word spelled out, and even the pronunciation played as sound clip... I do that on Win 95 with
exec sndrec32 /play /close [file nativename $f] &Why call sndrec and not use the snack package?

