#!/usr/bin/wish -f ## ## Tk SCRIPT NAME: playAniGIFfilesInPlaylist_FrontEnd.tk ## ##+####################################################################### ## PURPOSE: This Tk script provides a GUI for selecting a 'playlist' of ## animated GIF files. The animated GIF files may be scattered ## throughout various directories -- such as sub-directories of a ## web site --- OR sub-directories of a home directory --- OR ## sub-directories on multiple disk drives mounted on a computer --- ## OR sub-directories of a removable storage device mounted on ## a computer. ## ## Besides selecting the playlist file, this utility ## allows the user to select a 'player' program from among ## multiple choices --- and allows the user to specify a 'layout' ## for the GIF files on the screen (i.e. this utility allows ## multiple GIF's to be playing at the same time). ## ## Example Layouts (and number of player-windows at a time): ## - 1x1(1) ## - 2x1(2) ## - 3x1(3) ## - 2x2(4) ## - 3x2(6) ## - 3x3(9) ## - 4x3(12) ## ##+######################### ## TYPICAL OPERATIONAL STEPS: ## ## After specifying/selecting the playlist file and specifying ## the 'player' and 'layout', then the user can click on a 'Launch' ## button to start playing the animated GIF files, in the order ## of the filenames within the playlist file. ## ## The first N files are played, where N is determined by the ## 'layout' that was selected. ## ## To play the next N files, the user clicks on the 'Next N' button. ## ## To discontinue playing any more files from the playlist file, ## the user can click on the 'Stop' button. ## ## The user can also click on the 'CountAniGIFs' and the ## 'ShowAniGIFfilenames' buttons to get a view of the contents ## of the playlist file. ## ## The user can click on the 'Help' button for a description ## of this utility (similar to this description). ## ##+###################### ## AniGIF PLAYER PROGRAMS: ## ## The player program is chosen via radiobutton widgets on the GUI. ## ## Some examples of 'light-weight' 'player' programs: ## - ImageMagick 'animate' ## - 'gifview', that comes with the 'gifsicle' program ## that can be used to make animated GIF files. ## ## Almost any web-browser can be passed the name of an image file, ## such as an animated GIF file, and the browser will play the file. ## But it typically takes quite a few seconds for the web browser ## to start up. So we leave it as an option to the user whether ## they want to add one or more web-browser choices to the GUI ## --- by adding additional radiobuttons to the GUI. ## ## The GUI is intended to employ the main (most useful) aniGIF ## 'player' program options available -- without the user needing ## to reference the 'man animate' or 'man gifview' commands -- ## and other 'animate'/'gifview' documentation, for example, ## via web searches. ## ##+################# ## THE PLAYLIST FILE: ## ## The intent of this utility is to allow the user to easily ## specify files to be played ... by building a simple 'playlist'. ## ## The playlist file contains three types of lines: ## ## 1 - Comment lines may be indicated in the file by a # sign ## in column 1. ## ## 2 - Lines that contain a fully-qualified directory name ## or a 'dot' directory name. ## Examples: /data/aniGIFs/smileys or ## $env(HOME)/smileys or ## /home/fred/smileys ## . or ## ./other ## These lines must start with either slash(/) or $ or dot(.). ## ## The dot can be used to represent the directory in which ## the playlist file lies. This useful when putting the playlist ## file in a directory of animated GIFs and using the dot to ## represent the directory of the aniGIF files. The directory of ## aniGIFs may have sub-directories of aniGIFs, as indicated ## by the last example. The dot allows for moving/copying the ## aniGIFs (with the playlist file) to different directories ## without having to change the internals of the playlist file. ## ## 3 - Lines that contain a relative filename (relative to the ## previous directory name). ## Example: smiley_dancing_15x15_ani.gif ## ## These lines SHOULD NOT start with either slash(/) or $ or dot(.) ## or # --- although a # could be used to comment out a filename. ## ## The first non-comment line of the playlist file should be a ## directory name. ## ## There can be more than one directory name in the file. ## ## Each directory-name-line is followed by names of aniGIF files ## that are in the specified directory. ## ##+################################### ## NOTEWORTHY FEATURES OF THIS UTILITY: ## ## Most light-weight animated GIF 'players, like 'animate' and ## 'gifview', do not support a playlist feature. ## ## This utility is oriented toward using a simple, 'open', common ## playlist format that can be used with any of these animated GIF ## players --- via this Tk GUI script. ## ## Note that in building the playlist file, the user can SELECT ## animated GIF files which may be scattered throughout various PARENT ## DIRECTORIES and THEIR SUB-DIRECTORIES --- and place the filenames ## in a play-back ORDER DETERMINED BY THE USER (that is, determined ## by the order of directories and files in the playlist file). ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are compactly indicated ## by the following 'sketch' of the GUI: ## ## ------------------------------------------------------------------------------------------ ## Play *Animated GIF files* Files of a *Playlist* ... with a choice of players ## [window title] ## ------------------------------------------------------------------------------------------ ## ## {Exit} {Help} {LaunchPlayerJob} {Next N} {Stop (no more files)} {CountAniGIFs} {ShowAniGIFfilenames} ## ## Playlist Filename: _____________________________________________________ {Browse...} ## ## Player Program: O 1 - ImageMagick 'animate' O 2 - 'gifview' ## ## LayoutOnScreen: O 1x1(1) O 2x1(2) O 3x1(3) O 2x2(4) O 3x2(6) O 3x3(9) O 4x3(12) ## ## ------------------------------------------------------------------- ## ## In the above sketch of the GUI: ## ## SQUARE BRACKETS indicate a comment (not to be placed on the GUI). ## BRACES indicate a Tk 'button' widget. ## UNDERSCORES indicate a Tk 'entry' widget. ## A COLON indicates that the text before the colon is on a 'label' widget. ## CAPITAL-O indicates a Tk 'radiobutton' widget. ## CAPITAL-X indicates a Tk 'checkbutton' widget (if any). ## ##+############## ## GUI components: ## ## From the GUI 'sketch' above, it is seen that the GUI consists of ## about ## ## - 8 button widgets ## - 3 label widgets ## - 1 entry widget ## - 9 radiobutton widgets in 2 groups ## - 0 checkbutton widget ## - 0 scale widgets ## - 0 listbox widgets ## ##+##################################################################### ## CALLED BY: This script could be put in a sub-directory of the ## user's home directory, such as $HOME/apps/tkPlayAniGIFsInPlaylist. ## ## Then the user can use their desktop system (such as ## Gnome or KDE) to set up the script as an icon on the ## desktop. Then the user can click on the icon to ## startup the script. ##+######################################################################## ## STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name, win-position, win-color-scheme, ## fonts-for-widgets, widget-geom-parms, text-array-for-labels-etc, ## win-size-control). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack the frames. ## ## 2) Define & pack all widgets in the frames, frame by frame. ## After all the widgets for a frame are defined, pack them in the frame. ## ## 3) Define keyboard and/or mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## 4) Define PROCS, if needed. ## 5) Additional GUI INITIALIZATION (typically with one or more of ## the procs), if needed. ## ## In more detail: ## ## 1a) Define ALL frames -- and sub-frames: ## ## Top-level : ## 'fRbuttons' for Exit, Help, Launch, ... buttons ## 'fRfile' for a playlist (directory-and-filename) entry field ## 'fRdisplayPgm' for several radiobuttons, with a label ## 'fRwindowsLayout' for several radiobuttons, with a label ## ## 1b) Pack ALL frames, including sub-frames (if any). ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in left-to-right, top-to-bottom order: ## ## 3) Define bindings: See BINDINGS section below. ## ## 4) Define procs: ## ## 'get_playlist_filename' - called by the 'Browse...' button ## next to the filename entry field. ## ## 'count_aniGIF_files' - called by the 'CountAniGIFs' button. ## ## 'show_aniGIF_filenames' - called by the 'ShowAniGIFfilenames' button. ## ## 'set_player_command' - called by button1-release bindings on the ## player radiobuttons. ## ## 'start_playing_aniGIF_files' - called by the 'LaunchPlayerJob' button. ## ## 'play_nextN_aniGIF_files' - called by the 'start_playing_aniGIF_files' ## proc and the 'Next N' button. ## ## 'set_window_positions' - called by 'play_nextN_aniGIF_files' proc ## to set the window positions according ## to the current user-selected windows-layout. ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## Also called via the 'CountAniGIFs' and ## and 'ShowAniGIFfilenames' buttons. ## ## For other procs, see the PROCS section below. ## ## 5) Additional GUI initialization: See this section at the bottom ## of this script. ##+####################################################################### ## DEVELOPED WITH: Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala') ## ## $ wish ## % puts "$tcl_version $tk_version" ## ## showed ## 8.5 8.5 ## but this script should work in most previous 8.x versions, and probably ## even in some 7.x versions (if font handling is made 'old-style'). ##+######################################################################## ## MAINTENANCE HISTORY: ## Started by: Blaise Montandon 2014mar11 Started development, on Ubuntu 9.10, ## based on the code of some Tk scripts ## of mine that contained most of the ## widgets needed. Got the GUI up, with ## the procs dummied out. ## Updated by: Blaise Montandon 2014mar12 Prepared the procs for testing. ## Updated by: Blaise Montandon 2014apr14 Added 'file exists'and RETcode checks ## in proc 'play_nextN_aniGIF_files'. ## Changed initial default from ## 3x2 to 2x1. ## Updated by: Blaise Montandon 2014apr22 Added capability to use "." at the ## start of any directory name in the ## playlist file --- to represent the ## directory in which the playlist file ## lies. Changed to ## in binding on filename entry field. ## Updated by: Blaise Montandon 2014may22 Added 'catch' to close of file, ## in 4 places. Replaced var 'fileID' by ## 'fileIDcount','fileIDshow','fileIDplay'. ## Updated by: Blaise Montandon 2014may26 1) Moved some button config statements ## in proc 'start_playing_aniGIF_files' ## so that they do not un-do button config ## statements performed by the proc ## 'play_nextN_aniGIF_files'. ## 2) Added 'eval' to a 'set' statement in proc ## 'show_aniGIF_filenames' and to a 'set' ## statement in proc 'play_nextN_aniGIF_files'. ##+####################################################################### ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "Play *Animated GIF files* Files of a *Playlist* ... with a choice of players" wm iconname . "PlayAniGIFsInPlaylist" # wm geometry . +15+30 # wm geometry . +250+285 wm geometry . -10-10 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as listbox and entry field background color. ##+###################################################### set RGBpalette "#e0e0e0" ;# gray # set RGBpalette "#ffffff" ;# white # set RGBpalette "#000000" ;# black set RGBpalette "#ffcc88" ;# light-brown # set RGBpalette "#ccaa44" ;# darker-brown # set RGBpalette "#00ccff" ;# blue-green tk_setPalette "$RGBpalette" set entryBKGD "#ffffff" set textBKGD "#f0f0f0" set radbuttBKGD "#ffffff" # set chkbuttBKGD "#ffffff" # set scaleBKGD "#f0f0f0" # set listboxBKGD "#f0f0f0" ##+######################################################## ## DEFINE (temporary) FONT NAMES. ## ## We use a VARIABLE-WIDTH font for text on LABEL and ## BUTTON widgets. ## ## We use a FIXED-WIDTH font for LISTBOX lists, ## for text in ENTRY fields --- and often for text in ## TEXT widgets. ##+######################################################## # -family {augie} \ # -family {comic sans ms} \ # -family {dejavu sans} \ # -family {droid sans} \ # -family {vag round} \ # -family {vtcsundaykomix} \ # -family {waree} \ # -family {whackadoo upper} \ # -family {windsor} \ # -family {wetalmorker} \ # -family {y2k neophyte} \ font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) VARIABLE-WIDTH fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible FIXED-WIDTH fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## LABEL widget geom settings: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 set RELIEF_label_lo "flat" ## BUTTON widget geom settings: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## We use '-relief raised' for all 'button' widgets. ## ENTRY widget geom settings: set BDwidthPx_entry 2 ## We use '-relief sunken' for all 'entry' widgets. ## RADIOBUTTON widget geom settings: set PADXpx_radbutt 0 set PADYpx_radbutt 0 set BDwidthPx_radbutt 2 set RELIEF_radbutt_hi "raised" ## CHECKBUTTON widget geom settings: # set PADXpx_chkbutton 0 # set PADYpx_chkbutton 0 # set BDwidthPx_chkbutton 2 # set RELIEF_chkbutt_hi "raised" ## TEXT widget geom settings: set BDwidthPx_text 2 # set RELIEF_numtext "ridge" ## SCALE widget geom parameters: # set BDwidthPx_scale 2 # set scaleThicknessPx 10 ##+############################################################## ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+############################################################## ## if { "$VARlocale" == "en"} ## For '.fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonLAUNCH) "LaunchPlayerJob" set aRtext(buttonNEXT) "Next" set aRtext(buttonSTOP1) "Stop (no more aniGIFs)" set aRtext(buttonSTOP2) "Stop indicator was SET" set aRtext(buttonCOUNT) "CountAniGIFs" set aRtext(buttonPRINT) "ShowAniGIFfilenames" ## For '.fRfile' frame: set aRtext(labelFILENAME) "Playlist Filename:" set aRtext(buttonBROWSE) "Browse..." ## For '.fRdisplayPgm' frame: set aRtext(labelDISPLAYPGM) "Player Program:" set aRtext(radbuttDPGM1) "1 - ImageMagick 'animate'" set aRtext(radbuttDPGM2) "2 - 'gifview'" # set aRtext(radbuttDPGM3) "3 - Seamonkey web-browser" ## For '.fRwindowsLayout' frame: set aRtext(labelLAYOUT) "LayoutOnScreen:" set aRtext(radbuttLAYOUT1x1) "1x1(1)" set aRtext(radbuttLAYOUT2x1) "2x1(2)" set aRtext(radbuttLAYOUT3x1) "3x1(3)" set aRtext(radbuttLAYOUT2x2) "2x2(4)" set aRtext(radbuttLAYOUT3x2) "3x2(6)" set aRtext(radbuttLAYOUT3x3) "3x3(9)" set aRtext(radbuttLAYOUT4x3) "4x3(12)" ## END OF if { "$VARlocale" == "en"} ##+###################################################################### ## Set a MIN-SIZE of the window (roughly). ## ## For WIDTH, allow for the min-width of the '.fRbuttons' frame. ## ## For HEIGHT, allow for the stacked frames: ## 1 char high for the '.fRbuttons' frame ## 1 char high for the '.fRfile' frame ## 1 char high for the '.fRdisplayPgm' frame ## 1 char high for the '.fRwindowsLayout' frame ## -------- ## 4 chars high for the 4 frames ##+##################################################################### ## FOR WIDTH: (allow for widgets in the '.fRbuttons' frame) set minWidthPx [font measure fontTEMP_varwidth \ " $aRtext(buttonEXIT) $aRtext(buttonHELP) $aRtext(buttonLAUNCH) \ $aRtext(buttonNEXT) $aRtext(buttonSTOP1) $aRtext(buttonCOUNT) $aRtext(buttonPRINT)"] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (~5 widgets x 4 pixels/widget = 20 pixels). set minWinWidthPx [expr {28 + $minWidthPx}] ## For HEIGHT --- for ## 1 char high for the '.fRbuttons' frame ## 1 char high for the '.fRfile' frame ## 1 char high for the '.fRdisplayPgm' frame ## 1 char high for the '.fRwindowsLayout' frame ## -------- ## 4 chars high for the 4 frames set charHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {4 * $charHeightPx}] ## Add about 20 pixels for top-and-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (~4 widgets x 2 pixels/widget = 8 pixels). set minWinHeightPx [expr {28 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We may allow the window to be resizable. We pack the canvases ## (and the frames that contain them) with '-fill both -expand 1' ## so that the canvases can be enlarged by enlarging the window. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ## We fix the y-size of the window, but allow the x-size to vary. wm resizable . 1 0 ##+#################################################################### ##+#################################################################### ## DEFINE *ALL* THE FRAMES: ## ## Top-level : ## 'fRbuttons' for Exit, Help, Launch, ... buttons ## 'fRfile' for a playlist filename entry field ## 'fRdisplayPgm' for several radiobuttons, with a label ## 'fRwindowsLayout' for several radiobuttons, with a label ##+#################################################################### ##+#################################################################### ## FOR TESTING of expansion of frames (esp. during window expansion): # set feRELIEF_frame raised # set feBDwidth_frame 2 set feRELIEF_frame flat set feBDwidth_frame 0 frame .fRbuttons -relief $feRELIEF_frame -bd $feBDwidth_frame frame .fRfile -relief $feRELIEF_frame -bd $feBDwidth_frame # frame .fRfile -relief raised -bd 2 frame .fRdisplayPgm -relief $feRELIEF_frame -bd $feBDwidth_frame # frame .fRdisplayPgm -relief raised -bd 2 frame .fRwindowsLayout -relief $feRELIEF_frame -bd $feBDwidth_frame # frame .fRwindowsLayout -relief raised -bd 2 ##+######################################################## ## PACK *ALL* the FRAMES. ##+######################################################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRfile \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRdisplayPgm \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRwindowsLayout \ -side top \ -anchor nw \ -fill x \ -expand 0 ##+################################################################ ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+################################################################ ##+######################################################## ## IN THE '.fRbuttons' frame -- DEFINE several buttons ## --- Exit, Help, Launch. ##+######################################################## button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {popup_msgVarWithScroll .topHelp "$HELPtext"} button .fRbuttons.buttLAUNCH \ -text "$aRtext(buttonLAUNCH)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {start_playing_aniGIF_files} button .fRbuttons.buttNEXT \ -text "$aRtext(buttonNEXT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {play_nextN_aniGIF_files} ## Start with 'Next' button disabled. .fRbuttons.buttNEXT configure -state disabled ## Initialize variable for the following 'Stop' button. set STOPvar0or1 0 button .fRbuttons.buttSTOP \ -text "$aRtext(buttonSTOP1)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {set STOPvar0or1 1 ; \ .fRbuttons.buttSTOP configure -text "$aRtext(buttonSTOP2)"} ## Start with 'Stop' button disabled. .fRbuttons.buttSTOP configure -state disabled button .fRbuttons.buttCOUNT \ -text "$aRtext(buttonCOUNT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {count_aniGIF_files} button .fRbuttons.buttPRINT \ -text "$aRtext(buttonPRINT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {show_aniGIF_filenames} ##+######################################## ## Pack the widgets in the 'fRbuttons' frame ##+######################################## pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttLAUNCH \ .fRbuttons.buttNEXT \ .fRbuttons.buttSTOP \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbuttons.buttPRINT \ .fRbuttons.buttCOUNT \ -side right \ -anchor e \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRfile' frame -- DEFINE 1 LABEL widget, ## 1 ENTRY widget, and 1 BUTTON widget. ##+######################################################## label .fRfile.labelFILENAME \ -text "$aRtext(labelFILENAME)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label_lo entry .fRfile.entryFILENAME \ -textvariable ENTRYfilename \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -bd $BDwidthPx_entry \ -relief sunken ## Set an initial value for the entry var. set playlistDIR "$env(HOME)" # set playlistDIR "$env(HOME)/MyAniGIFplaylists" # set playlistDIR "/data/playlists/aniGIFs" ## FOR TESTING: set playlistDIR [pwd] set ENTRYfilename "$playlistDIR/aniGIF_playlist.lis" ## Put the end of the filename in view. .fRfile.entryFILENAME xview end button .fRfile.buttBROWSE \ -text "$aRtext(buttonBROWSE)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief raised \ -command {get_playlist_filename} ## PACK the widgets in the 'fRfile' frame. pack .fRfile.labelFILENAME \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRfile.entryFILENAME \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRfile.buttBROWSE \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRdisplayPgm' frame -- DEFINE ## several RADIOBUTTONS, preceded by a LABEL widget. ##+######################################################## label .fRdisplayPgm.labelDISPLAYPGM \ -text "$aRtext(labelDISPLAYPGM)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label_lo ## DEFINE radiobuttons for aniGIF 'player' programs : radiobutton .fRdisplayPgm.radbuttDPGM1 \ -text "$aRtext(radbuttDPGM1)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARdisplaypgm \ -value "1" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRdisplayPgm.radbuttDPGM2 \ -text "$aRtext(radbuttDPGM2)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARdisplaypgm \ -value "2" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi ## Change 0 to 1 if you want to activate this radiobutton. if {0} { radiobutton .fRdisplayPgm.radbuttDPGM3 \ -text "$aRtext(radbuttDPGM3)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARdisplaypgm \ -value "3" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi } ## RADVARdisplaypgm is the var for these several radiobuttons. ## Set an initial value. set RADVARdisplaypgm "1" # set RADVARdisplaypgm "2" # set RADVARdisplaypgm "3" ## PACK the widgets in the 'fRdisplayPgm' frame. pack .fRdisplayPgm.labelDISPLAYPGM \ .fRdisplayPgm.radbuttDPGM1 \ .fRdisplayPgm.radbuttDPGM2 \ -side left \ -anchor w \ -fill none \ -expand 0 # .fRdisplayPgm.radbuttDPGM3 \ ##+######################################################## ## IN THE '.fRwindowsLayout' frame -- DEFINE ## 2 RADIOBUTTONS, preceded by a LABEL widget. ##+######################################################## label .fRwindowsLayout.labelLAYOUT \ -text "$aRtext(labelLAYOUT)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label_lo ## DEFINE radiobuttons for layout options : radiobutton .fRwindowsLayout.radbuttLAYOUT1x1 \ -text "$aRtext(radbuttLAYOUT1x1)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "1x1" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRwindowsLayout.radbuttLAYOUT2x1 \ -text "$aRtext(radbuttLAYOUT2x1)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "2x1" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRwindowsLayout.radbuttLAYOUT3x1 \ -text "$aRtext(radbuttLAYOUT3x1)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "3x1" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRwindowsLayout.radbuttLAYOUT2x2 \ -text "$aRtext(radbuttLAYOUT2x2)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "2x2" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRwindowsLayout.radbuttLAYOUT3x2 \ -text "$aRtext(radbuttLAYOUT3x2)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "3x2" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRwindowsLayout.radbuttLAYOUT3x3 \ -text "$aRtext(radbuttLAYOUT3x3)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "3x3" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRwindowsLayout.radbuttLAYOUT4x3 \ -text "$aRtext(radbuttLAYOUT4x3)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARlayout \ -value "4x3" \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt_hi ## RADVARlayout is the variable for these 'layout' radiobuttons. ## Set an initial value and set the text in the 'Next' button. # set RADVARlayout "1x1" # set N2read 1 set RADVARlayout "2x1" set N2read 2 # set RADVARlayout "3x1" # set N2read 3 # set RADVARlayout "2x2" # set N2read 4 # set RADVARlayout "3x2" # set N2read 6 # set RADVARlayout "3x3" # set N2read 9 # set RADVARlayout "4x3" # set N2read 12 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) $N2read" ## PACK the widgets in the 'fRwindowsLayout' frame. pack .fRwindowsLayout.labelLAYOUT \ .fRwindowsLayout.radbuttLAYOUT1x1 \ .fRwindowsLayout.radbuttLAYOUT2x1 \ .fRwindowsLayout.radbuttLAYOUT3x1 \ .fRwindowsLayout.radbuttLAYOUT2x2 \ .fRwindowsLayout.radbuttLAYOUT3x2 \ .fRwindowsLayout.radbuttLAYOUT3x3 \ .fRwindowsLayout.radbuttLAYOUT4x3 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+##################################################################### ## END OF SECTION TO DEFINE AND PACK THE GUI WIDGETS. ##+##################################################################### ##+##################################################################### ##+##################################################################### ## DEFINE BINDINGS: ## button1-release bindings on some radiobutton widgets ## and ## key-press bindings on an entry widget. ## ## (Someday a Return-key binding on the entry widget???) ## ( bind .fR?????.???widget??? {??proc??} ) ##+##################################################################### ## For a button1-release on the 'player' radiobuttons, call the ## 'set_player_command' proc to set the 'VARcommand' variable. bind .fRdisplayPgm.radbuttDPGM1 {set_player_command} bind .fRdisplayPgm.radbuttDPGM2 {set_player_command} ## For a button1-release on the 'layout' radiobuttons, set the ##'N2read' variable --- and change the number on the 'Next' button. ## (Note that clicking on a 'layout' radiobutton sets the ## 'RADVARlayout' variable.) bind .fRwindowsLayout.radbuttLAYOUT1x1 { ## RADVARlayout is "1x1" set N2read 1 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 1" } bind .fRwindowsLayout.radbuttLAYOUT2x1 { ## RADVARlayout is "2x1" set N2read 2 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 2" } bind .fRwindowsLayout.radbuttLAYOUT3x1 { ## RADVARlayout is "3x1" set N2read 3 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 3" } bind .fRwindowsLayout.radbuttLAYOUT2x2 { ## RADVARlayout is "2x2" set N2read 4 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 4" } bind .fRwindowsLayout.radbuttLAYOUT3x2 { ## RADVARlayout is "3x2" set N2read 6 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 6" } bind .fRwindowsLayout.radbuttLAYOUT3x3 { ## RADVARlayout is "3x3" set N2read 9 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 9" } bind .fRwindowsLayout.radbuttLAYOUT4x3 { ## RADVARlayout is "4x3" set N2read 12 .fRbuttons.buttNEXT configure -text "$aRtext(buttonNEXT) 12" } ## As the user types in the filename entry field (KeyRelease events), make sure ## the area near the insertion cursor or the character entry is showing. ## (Strange? 'KeyRelease' works, but 'KeyPress' leaves the insert cursor at the ## end of the field and ALWAYS slightly out of view to the right. It's as if ## a backspace occurs after the KeyPress.) bind .fRfile.entryFILENAME \ {.fRfile.entryFILENAME xview insert} ## FOR TESTING: # bind .fRfile.entryFILENAME { # set entFILENAMExview [.fRfile.entryFILENAME xview] # puts ".fRfile.entryFILENAME 'offset' and 'span': $entFILENAMExview" # } ## An alternative? # bind .fRfile.entryFILENAME \ # {.fRfile.entryFILENAME xview moveto 1.0} ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## ## 'get_playlist_filename' - called by the 'Browse...' button ## next to the filename entry field. ## ## 'count_aniGIF_files' - called by the 'CountAniGIFs' button. ## ## 'show_aniGIF_filenames' - called by the 'ShowAniGIFfilenames' button. ## ## 'set_player_command' - called by button1-release bindings on the ## player radiobuttons. ## ## 'start_playing_aniGIF_files' - called by the 'LaunchPlayerJob' button. ## ## 'play_nextN_aniGIF_files' - called by the 'start_playing_aniGIF_files' ## proc and the 'Next N' button. ## ## 'set_window_positions' - called by 'play_nextN_aniGIF_files' proc ## to set the window positions according ## to the current user-selected windows-layout. ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## Also called via the 'CountAniGIFs' and ## and 'ShowAniGIFfilenames' buttons. ##+##################################################################### ##+##################################################################### ##+##################################################################### ## PROC: 'get_playlist_filename' ##+##################################################################### ## PURPOSE: To get the fully-qualified name of a file and put the ## name into global var 'ENTRYfilename'. ## ## Note: The user may change the filename (after the end of the ## directory name) to change it to a different filename ## in the same directory. For example: change playlist1.lis ## to playlist2.lis. ## ## Furthermore, the user may completely change both ## the directory name and the filename by editing ## the text in the entry field. ## ## CALLED BY: the '-command' option of the 'Browse ...' button. ##+##################################################################### proc get_playlist_filename {} { global ENTRYfilename env playlistDIR ############################################# ## Get a 'aniGIF-playlist' file name. ############################################# set fName [tk_getOpenFile -parent . \ -title "Select Directory-and-Filename - of a playlist-file" \ -initialdir "$playlistDIR" ] ## FOR TESTING: # puts "fName : $fName" ################################ ## Check if fName var is empty. ################################ if {"$fName" == ""} {return} ##################################################### ## Put $fName in ENTRYfilename. Reset the playlistDIR var. ##################################################### if {[file exists "$fName"]} { set ENTRYfilename "$fName" ## Put the end of the filename in view. .fRfile.entryFILENAME xview end set playlistDIR [ get_chars_before_last / in "$ENTRYfilename" ] } ## END OF if directory-exists } ## END OF proc 'get_playlist_filename' ##+################################################################### ## PROC: 'get_chars_before_last' ##+################################################################### ## INPUT: A character and a string. ## Note: The "in" parameter is there only for clarity. ## ## OUTPUT: Returns all of the characters in the string "strng" that ## are BEFORE the last occurence of the characater "char". ## ## CALLED BY: proc 'get_img_filename' and 3 'aniGIF' procs below. ## ##+################################################################## proc get_chars_before_last { char in strng } { set lastIDX [ expr [string last $char $strng ] - 1 ] set output [ string range $strng 0 $lastIDX ] ## FOR TESTING: # puts "From 'get_chars_before_last' proc:" # puts "STRING: $strng" # puts "CHAR: $char" # puts "RANGE up to LAST CHAR - start: 0 lastIDX: $lastIDX" return $output } ## END OF 'get_chars_before_last' PROCEDURE ##+################################################################### ## PROC: 'get_chars_after_last' ##+################################################################### ## INPUT: A character and a string. ## Note: The "in" parameter is there only for clarity. ## ## OUTPUT: Returns all of the characters in the string "strng" that ## are AFTER the last occurence of the characater "char". ## ## CALLED BY: 3 'aniGIF' procs below ##+################################################################## proc get_chars_after_last { char in strng } { set lastIDX [ expr [string last $char $strng ] + 1 ] set output [ string range $strng $lastIDX end ] ## FOR TESTING: # puts "From 'get_chars_before_last' proc:" # puts "STRING: $strng" # puts "CHAR: $char" # puts "RANGE up to LAST CHAR - start: 0 lastIDX: $lastIDX" return $output } ## END OF 'get_chars_after_last' PROCEDURE ##+############################################################# ## PROC: 'count_aniGIF_files' ##+############################################################# ## PURPOSE: For a given playlist file, counts the ## aniGIF filenames in the playlist file --- ## by counting the non-empty lines that do NOT ## start with slash (/) or $ or #. ## ## CALLED BY: the 'CountAniGIFs' button ##+############################################################# proc count_aniGIF_files {} { ## DUMMY OUT THIS PROC (during development) # return global ENTRYfilename env ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entry in the filename 'entry' widget. ####################################################### set ENTRYfilename [string trim "$ENTRYfilename"] if {![file exists "$ENTRYfilename"]} { popup_msgVarWithScroll .topCount \ "Filename in entry field does not exist." return } ############################################################### ## 'count' logic goes here. ############################################################### ## Prepare for counting aniGIF files in the playlist file. ############################################################### set fileIDcount [open $ENTRYfilename] set NaniGIFfiles 0 ################################################################ ## START OF WHILE-LOOP for the 'gets' file-READING. ## The while-test below is equivalent to 'while {![eof $fileIDcount]}'. ################################################################ while {[eof $fileIDcount] == 0} { ############################################################ ## GET THE NEXT LINE (up to a line feed) --- and, optionally, ## get its length. ############################################################ # set lineLen [gets $fileIDcount line] gets $fileIDcount line ########################### ## GET FIRST CHAR of line. ########################### set FIRSTchar [string index "$line" 0] ############################################################## ## IF A COMMENT LINE, SKIP THIS LINE ... i.e. read next line. ############################################################## if { "$FIRSTchar" == "#" } {continue} ################################################################# ## IF A DIRECTORY LINE, (set the anigifDIR var and) read next line. ################################################################# if { "$FIRSTchar" == "/" || "$FIRSTchar" == "$" || \ "$FIRSTchar" == "." } { # set anigifDIR "[string trim $line]" ## Since we are just counting the aniGIF's, we do not really ## need to keep track of the current aniGIFs directory. continue } ################################################################# ## IF A RELATIVE-FILENAME LINE, INCREMENT COUNT & read next line. ################################################################# set EMPTYLINEcheck "[string trim $line]" if { "$EMPTYLINEcheck" == "" } {continue} incr NaniGIFfiles } ## END OF while {[eof $fileIDcount] == 0} catch {close $fileIDcount} ############################################################### ## DISPLAY THE COUNT in a small popup Tk window. ############################################################### popup_msgVarWithScroll .topCount \ "$NaniGIFfiles aniGIF filenames are in the playlist file --- [get_chars_after_last "/" in "$ENTRYfilename"] in directory [get_chars_before_last "/" in "$ENTRYfilename"]" } ## END OF proc 'count_aniGIF_files' ##+############################################################# ## PROC: 'show_aniGIF_filenames' ##+############################################################# ## PURPOSE: For a given playlist file, shows the ## aniGIF filenames in the playlist file --- ## in a Tk popup window with a scrollable text widget. ## ## CALLED BY: the 'ShowAniGIFfilenames' button ##+############################################################# proc show_aniGIF_filenames {} { ## DUMMY OUT THIS PROC (during development) # return global ENTRYfilename env ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entry in the filename 'entry' widget. ####################################################### set ENTRYfilename [string trim "$ENTRYfilename"] if {![file exists "$ENTRYfilename"]} { popup_msgVarWithScroll .topCount \ "Filename in entry field does not exist." return } ###################################################### ## Store the directory of this playlist file, to use ## below in place of "." in a directory-name line. ###################################################### set thisDIR [file dirname "$ENTRYfilename"] ############################################################### ## Logic goes here to SHOW THE AniGIF FILENAMES of ## the 'playlist file'. ############################################################### ## Prepare for getting aniGIF filenames in the playlist file. ############################################################### set fileIDshow [open $ENTRYfilename] set NaniGIFfiles 0 set TEXTfilenames "" ################################################################ ## START OF WHILE-LOOP for the 'gets' file-READING. ## The while-test below is equivalent to 'while {![eof $fileIDshow]}'. ################################################################ while {[eof $fileIDshow] == 0} { ############################################################ ## GET THE NEXT LINE (up to a line feed) --- and, optionally, ## get its length. ############################################################ # set lineLen [gets $fileIDshow line] gets $fileIDshow line ########################### ## GET FIRST CHAR of line. ########################### set FIRSTchar [string index "$line" 0] ############################################################## ## IF A COMMENT LINE, SKIP THIS LINE ... i.e. read next line. ############################################################## if { "$FIRSTchar" == "#" } {continue} ################################################################# ## IF A DIRECTORY LINE starting with "/" or "$", ## SET THE 'anigifDIR' VAR and read next line. ################################################################# if { "$FIRSTchar" == "/" || "$FIRSTchar" == "$" } { set anigifDIR "[string trim $line]" continue } ################################################################# ## IF A DIRECTORY LINE starting with ".", ## SET THE 'anigifDIR' VAR and read next line. ################################################################# if { "$FIRSTchar" == "." } { set lengthOFline [string length "$line"] if {$lengthOFline > 1} { set restOFline [string range "$line" 1 $lengthOFline] set anigifDIR "${thisDIR}$restOFline" } else { set anigifDIR "$thisDIR" } continue } ################################################################# ## IF A RELATIVE-FILENAME LINE, add the full filename to the ## 'TEXTfilenames' var ... and read next line. ## We need the 'eval' on the 'set' statement to convert ## $env(HOME), if it is used, to a directory name. ################################################################# set EMPTYLINEcheck "[string trim $line]" if { "$EMPTYLINEcheck" == "" } {continue} eval set aniGIFfilename "$anigifDIR/$EMPTYLINEcheck" set TEXTfilenames "$TEXTfilenames $aniGIFfilename" incr NaniGIFfiles } ## END OF while {[eof $fileIDshow] == 0} catch {close $fileIDshow} ############################################################### ## DISPLAY THE AniGIF FILENAMES in a popup Tk window. ############################################################### popup_msgVarWithScroll .topList \ "For the playlist file [get_chars_after_last "/" in "$ENTRYfilename"] in directory [get_chars_before_last "/" in "$ENTRYfilename"], the $NaniGIFfiles full-filenames of the aniGIF files in the playlist file are as follows: $TEXTfilenames" } ## END OF proc 'show_aniGIF_filenames' ##+############################################################# ## PROC: 'set_player_command' ##+############################################################# ## PURPOSE: For a user-selected aniGIF player, this proc sets ## the variable 'VARcommand', which holds the name ## of the player program and some of its command-line ## parameters. ## ## CALLED BY: button1-release on a player radiobutton ##+############################################################# proc set_player_command {} { global RADVARdisplaypgm VARcommand ################################################################### ## SET 'VARcommand' with the 'animate' command and ## some of its command-line options (if any). ################################################################### if {"$RADVARdisplaypgm" == "1"} { # set VARcommand "/usr/bin/animate " set VARcommand "animate " return } ################################################################### ## SET 'VARcommand' with the 'gifview' command and ## some of its command-line options (like '-a). ################################################################### if {"$RADVARdisplaypgm" == "2"} { # set VARcommand "/usr/bin/gifview -a " set VARcommand "gifview -a " return } popup_msgVarWithScroll .topList \ "Invalid value of variable RADVARdisplaypgm in proc 'set_player_command'. Value: $RADVARdisplaypgm" } ## END OF proc 'set_player_command' ##+############################################################# ## PROC: 'start_playing_aniGIF_files' ##+############################################################# ## PURPOSE: For a given playlist file, starts plays the aniGIF ## files from the playlist file --- by opening the ## playlist file. ## ## Then uses the 'play_nextN_aniGIF_files' proc to ## read the first $N2read files from the playlist file, ## indicated by directory names and 'relative' aniGIF ## filenames in the playlist file. ## ## CALLED BY: the 'LaunchPlayerJob' button ##+############################################################# proc start_playing_aniGIF_files {} { ## DUMMY OUT THIS PROC (during development) # return global STOPvar0or1 aRtext ENTRYfilename fileIDplay NaniGIFfiles thisDIR ##################################################### ## Reset the 'stop' variable in case it was set to 1 ## to stop a previous display sequence. ##################################################### set STOPvar0or1 0 .fRbuttons.buttSTOP configure -text "$aRtext(buttonSTOP1)" ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entry in the filename 'entry' widget. ####################################################### set ENTRYfilename [string trim "$ENTRYfilename"] if {![file exists "$ENTRYfilename"]} { popup_msgVarWithScroll .topCount \ "Filename in entry field does not exist." return } ###################################################### ## Store the directory of this playlist file, to use ## below in place of "." in a directory-name line. ###################################################### set thisDIR [file dirname "$ENTRYfilename"] ############################################################### ## Prepare for playing the aniGIF files in the playlist file ... ## mainly, open the file. ############################################################### set fileIDplay [open "$ENTRYfilename"] ################################################## ## Initialize a counter for keeping a count of the ## number of aniGIF files read so far. ################################################## set NaniGIFfiles 0 ####################################################### ## Enable the 'Next' & 'Stop' buttons --- and disable the ## 'Launch' button (until the playlist file is closed). ####################################################### .fRbuttons.buttNEXT configure -state normal .fRbuttons.buttSTOP configure -state normal .fRbuttons.buttLAUNCH configure -state disabled ######################################################### ## Call the proc to read and play the next N aniGIF files. ######################################################### play_nextN_aniGIF_files } ## END OF proc 'start_playing_aniGIF_files' ##+############################################################# ## PROC: 'play_nextN_aniGIF_files' ##+############################################################# ## PURPOSE: For a given value of global variable 'N2read' (set ## via the 'layout' radiobuttons), this proc ## reads the next $N2read files from the previously ## opened playlist file. The full filenames are built ## using directory names and 'relative' aniGIF ## filenames in the playlist file. ## ## This proc uses the display program (and suitable ## parameters) in the variable 'VARcommand' to show ## the $N2read files. ## ## This proc uses 2 array variables, aRxpositions and ## aRypositions, to set '-geometry +x+y' player-command ## options to locate the $N2read player-windows on the screen. ## (We use '-geometry' for the 'animate' player, and '--geometry' ## for the 'gifview' player.) ## ## (The aRxpositions and aRypositions arrays are set in ## a call to proc 'set_window_positions', which uses ## the 'RADVARlayout' variable (and screensize) to ## determine the x,y positions for the player windows.) ## ## If the end-of-file is encountered, this proc ## closes the playlist file before exiting. ## ## The global variable 'fileIDplay' is used to do the ## the file reads -- and close the file when end-of-file ## is reached (or the STOP variable has been set). ## ## This proc increments the variable 'NaniGIFfiles' ## to keep track of the total number of files read. ## ## NOTE1: There are 3 conditions under which the playlist file ## reading (done only by this proc) stops: ## 1) If the STOP button has been pressed (STOP var set to 1). ## 2) We encounter end-of-file in reading the playlist ## file with this proc. ## 3) We have read $N2read files with this proc. ## We want to close the playlist file in cases 1 and 2, ## but leave the file open in case 3. ## ## NOTE2: After this proc has displayed (at most) $N2read aniGIF files, ## this GUI falls into an GUI-event-handling state --- until ## the user clicks the 'Next N' button (again). While the ## GUI is in the event-handling state, the user can click on ## the GUI to change radiobutton settings or click on ## buttons like the 'Help' button. ## ## CALLED BY: by the 'start_playing_aniGIF_files' proc and by ## the 'Next N' button ##+############################################################# proc play_nextN_aniGIF_files {} { ## DUMMY OUT THIS PROC (during development) # return global STOPvar0or1 fileIDplay env N2read aRxpositions aRypositions \ VARcommand RADVARdisplaypgm aRtext NaniGIFfiles anigifDIR ENTRYfilename \ thisDIR # global RADVARlayout ## FOR TESTING: # puts "Started proc 'play_nextN_aniGIF_files'." ############################################################ ## CHECK TO SEE IF THE DISPLAY SEQUENCE SHOULD STOP. I.e., ## if STOPvar0or1 is set to 1, the 'Next N' button is to be ## de-activated --- until the 'Launch' button is used again. ## Make sure that 'fileIDplay' is closed. ############################################################ if { $STOPvar0or1 == 1 } { catch {close $fileIDplay} ## FOR TESTING: if {0} { puts "At PROC 'play_nextN_aniGIF_files' - close playlist file:" puts "fileIDplay: $fileIDplay" puts "catchRESULT: $catchRESULT" } popup_msgVarWithScroll .topCount \ "The 'STOP' button was used to call for a stop in the playing of the playlist files. If you are ready to start again, click on the '$aRtext(buttonLAUNCH)' button." ######################################################### ## Reset the 'stop' button to zero (off), and ## reset the 'stop' button text, to indicate the 'stop' ## button is ready to handle another display sequence. ######################################################## set STOPvar0or1 0 .fRbuttons.buttSTOP configure -text "$aRtext(buttonSTOP1)" ########################################################## ## Disable the 'Next' & 'Stop' buttons. Enable the 'Launch' ## button (to allow for starting a playlist file, again). ########################################################## .fRbuttons.buttNEXT configure -state disabled .fRbuttons.buttSTOP configure -state disabled .fRbuttons.buttLAUNCH configure -state normal return } ############################################################### ## For the current windows-layout, in variable 'RADVARlayout', ## set the positions of the $N2read windows --- in ## variables 'aRxpositions' and 'aRypositions'. ## ## NOTE: We allow the 'layout' to be changed after the playlist ## is opened --- i.e. after the user clicks the ## 'Launch' (begin playing) button. ############################################################### set_window_positions ################################################################### ## START OF A WHILE-LOOP for the 'gets' file-READING of the ## playlist file. This loop reads NO MORE THAN $N2read ## aniGIF files from the playlist file --- and starts playing ## each aniGIF in a player window, which is located on the screen ## according to the 'aRxpositions' & 'aRypositions' array variables. ## ## We use the 'READcnt' variable to check when we have read ## 'N2read' aniGIF files. ## ## The while-test below is equivalent to 'while {![eof $fileIDplay]}'. ################################################################### set READcnt 0 while {[eof $fileIDplay] == 0} { ############################################################ ## GET the next line (up to a line feed). ## (We could get the line-length in the same statement.) ############################################################ # set lineLen [gets $fileIDplay line] gets $fileIDplay line ## FOR TESTING: # puts "line: $line" ########################### ## Get first char of line. ########################### set FIRSTchar [string index "$line" 0] ############################################################## ## If a comment line, skip this line ... i.e. read next line. ############################################################## if { "$FIRSTchar" == "#" } {continue} ################################################################### ## If a directory line starting with "/" or "$", ## SET THE 'anigifDIR' VAR and read next line. ################################################################### if { "$FIRSTchar" == "/" || "$FIRSTchar" == "$" } { set anigifDIR "[string trim $line]" continue } ################################################################# ## IF A DIRECTORY LINE starting with ".", ## SET THE 'anigifDIR' VAR and read next line. ################################################################# if { "$FIRSTchar" == "." } { set lengthOFline [string length "$line"] if {$lengthOFline > 1} { set restOFline [string range "$line" 1 $lengthOFline] set anigifDIR "${thisDIR}$restOFline" } else { set anigifDIR "$thisDIR" } continue } ################################################################# ## At this point, we have eliminated 2 line types --- comment ## lines and directory-name lines. So this line must be a ## relative-filename line. ## ## If the relative-filename line is empty, return to the top ## of this 'while' loop to read the next aniGIF filename. ################################################################# set EMPTYLINEcheck "[string trim $line]" if { "$EMPTYLINEcheck" == "" } {continue} ################################################################## ## Set the fully-qualified name of the aniGIF file and ## check if it exists. ## We need 'eval' on the 'set' statement to convert ## $env(HOME), if it is used, to a directory name. ################################################################## eval set aniGIFfilename "$anigifDIR/$EMPTYLINEcheck" ## FOR TESTING: puts "aniGIFfilename: $aniGIFfilename" if {![file exists "$aniGIFfilename"]} {continue} ######################################################################### ## We use READcnt to help determine how many aniGIFs to show at one time. ######################################################################### incr READcnt incr NaniGIFfiles ################################################################### ## At this point the relative-filename line appears to be OK, so we ## PLAY THE AniGIF FILE. ################################################################### ## First, we set the 'geometry' parm for placing the player window ## for this particular aniGIF, among the (up to) $N2read aniGIF files ## that are to be displayed by the call to this proc. ## ## We use '-geometry' for the 'animate' player (RADVARdisplaypgm=1) ## and '--geometry' for the 'gifview' player (RADVARdisplaypgm=2). #################################################################### set OPTgeom "--geometry" if {$RADVARdisplaypgm == 1} {set OPTgeom "-geometry"} set PARMgeom "$OPTgeom +$aRxpositions($READcnt)+$aRypositions($READcnt)" #################################################################### ## SOME SYNTAX NOTES on running the player program via a Tcl 'exec': #################################################################### ## On page 105 of the 4th edition of 'Practical Programming in Tcl & Tk', ## is the following quote on the Tcl 'exec' command: ## ## "The 'exec' command runs programs from your Tcl script. For example: ## set d [exec date] ## The standard output of the program is returned as the value of ## the 'exec' command. However, if the program writes to its standard ## error channel or exits with a nonzero status code, then 'exec' ## raises an error. If you do not care about the exit status, or you ## use a program that insists on writing to standard error, then you ## can use 'catch' to mask the errors: ## catch {exec program arg arg} result" ## ## Unfortunately, running a player program in 'foreground' mode ## like this makes the button widgets on the GUI unavailable --- ## in particular, the 'Stop' button ... and the 'Help' button. ## Also, we cannot continue on in this loop --- to play up to ## $N2read aniGIF files, at the same time. ## ####################################################################### ## On page 107 of the 4th edition of 'Practical Programming in Tcl & Tk', ## is the following quote on the Tcl 'exec' command and 'background' mode: ## ## "A trailing '&' causes the program to run in the background. ## In this case, the process identifier is returned by the 'exec' ## command. Otherwise, the 'exec' command blocks during execution ## of the program, and the standard output of the program is the ## return code of 'exec'." ## ## Page 83 of the same book says: ## "'catch' returns zero if there was no error caught, ## or a nonzero error code if it did catch an error." #################################################################### ## A couple of examples of using a PID (process ID) with Tcl: ## ## catch {eval exec $feREADER_text \"$FULFILname\" &} ViewerPID ## ## set RETcode [ catch {eval exec ${feDIR}/tkGUIs/shofil.tk \ ## "$FULFILname" &} ViewerPID ] ## #################################################################### ## An alternative form of trying 'exec': ## exec /bin/sh -c "$...." #################################################################### ## A 'foreground' run per page 105. ## NOT USED, because it 'locks up' the GUI. # catch {eval exec $VARcommand $PARMgeom "$aniGIFfilename"} CatchMsg ############################################################### ## A 'background' run per page 107. ## (Better for implementing the 'Stop' feature via a button ## on this GUI.) ## We use 'eval' to avoid a 'not found' error when we append ## some parms (or a space) to the player command. ############################################################### set RETcode [ catch {eval exec $VARcommand $PARMgeom "$aniGIFfilename" &} ViewerPID ] if {$RETcode != 0} { set ERRtext \ "Proc 'play_nextN_aniGIF_files' encountered an error on trying to show file $EMPTYLINEcheck in directory $anigifDIR using command '$VARcommand' with parameters $PARMgeom. RETcode: $RETcode Message: $ViewerPID Stopping processing." popup_msgVarWithScroll .topErr "$ERRtext" .fRbuttons.buttLAUNCH configure -state normal return } ## FOR TESTING: if {0} { puts "" puts "***********************" puts "VARcommand: $VARcommand" puts "PARMgeom: $PARMgeom" puts "anigifDIR (directory) : $anigifDIR" puts "EMPTYLINEcheck (filename): $EMPTYLINEcheck" puts "ViewerPID: $ViewerPID" puts "***********************" puts "" } ############################################################ ## If $READcnt is equal to (or greater than) $N2read, ## break out of this aniGIF-file-reading 'while' loop, ## and leave this proc. ## ## We use 'return' rather than 'break'. 'break' would ## drop us out of the 'while' loop, after which we ## 'close' the playlist file, under the assumption ## that we have hit end-of file. ## ## Note that after the 'return', this GUI goes into an ## event-handling state in which it responds to clicks ## on the buttons and radiobuttons of the GUI. ############################################################ if { $READcnt >= $N2read } {return} } ## END OF 'while {[eof $fileIDplay] == 0}' LOOP ####################################################### ## Close the playlist file when the while loop is done. ####################################################### catch {close $fileIDplay} ## FOR TESTING: if {0} { puts "At PROC 'play_nextN_aniGIF_files' - close playlist file:" puts "fileIDplay: $fileIDplay" puts "catchRESULT: $catchRESULT" } ########################################################## ## Disable the 'Next' & 'Stop' buttons. Enable the 'Launch' ## button (to allow for starting a playlist file, again). ########################################################## .fRbuttons.buttNEXT configure -state disabled .fRbuttons.buttSTOP configure -state disabled .fRbuttons.buttLAUNCH configure -state normal ############################################################### ## Inform the user that end-of-file has been reached, and ## DISPLAY THE COUNT OF AniGIF-FILES-PLAYED in a small popup Tk window. ## (If the user finds this annoying, this could be ## deactivated by setting 1 to 0 in the 'if' statement. ## Or we could show this only if NaniGIFfiles were greater than ## 25, say. That is, replace '1' by '$NaniGIFfiles > 25'.) ############################################################### if {1} { popup_msgVarWithScroll .topCount \ "End-of-file was reached on the playlist file --- [get_chars_after_last "/" in "$ENTRYfilename"] in directory [get_chars_before_last "/" in "$ENTRYfilename"] $NaniGIFfiles aniGIF files were shown. " } ## END OF 'if {1}' } ## END OF proc 'play_nextN_aniGIF_files' ##+################################################################# ## PROC: 'set_window_positions' ##+################################################################# ## PURPOSE: For a given value of global variable 'RADVARlayout' ## (and for the screensize of the user's monitor), ## this proc calculates and puts $N2read x,y positions ## in an array 'aRpositions'. ## ## CALLED BY: proc 'play_nextN_aniGIF_files' #################################################################### proc set_window_positions {} { global RADVARlayout SCREENwidthPx SCREENheightPx \ aRxpositions aRypositions # global N2read ######################################################### ## According to RADVARlayout, load the 2 arrays: ## aRxpositions, aRypositions. ######################################################### set XleftPx 15 set YtopPx 30 if {"$RADVARlayout" == "1x1"} { set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx } if {"$RADVARlayout" == "2x1"} { set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx set aRxpositions(2) [expr {$XleftPx + int(0.5 * $SCREENwidthPx)}] set aRypositions(2) $YtopPx } if {"$RADVARlayout" == "3x1"} { set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx set aRxpositions(2) [expr {$XleftPx + int(0.33 * $SCREENwidthPx)}] set aRypositions(2) $YtopPx set aRxpositions(3) [expr {$XleftPx + int(0.66 * $SCREENwidthPx)}] set aRypositions(3) $YtopPx } if {"$RADVARlayout" == "2x2"} { ## ROW 1 set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx set aRxpositions(2) [expr {$XleftPx + int(0.5 * $SCREENwidthPx)}] set aRypositions(2) $YtopPx ## ROW 2 set aRxpositions(3) $XleftPx set aRypositions(3) [expr {$YtopPx + int(0.5 * $SCREENheightPx)}] set aRxpositions(4) [expr {$XleftPx + int(0.5 * $SCREENwidthPx)}] set aRypositions(4) [expr {$YtopPx + int(0.5 * $SCREENheightPx)}] } if {"$RADVARlayout" == "3x2"} { ## ROW 1 set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx set aRxpositions(2) [expr {$XleftPx + int(0.33 * $SCREENwidthPx)}] set aRypositions(2) $YtopPx set aRxpositions(3) [expr {$XleftPx + int(0.66 * $SCREENwidthPx)}] set aRypositions(3) $YtopPx ## ROW 2 set aRxpositions(4) $XleftPx set aRypositions(4) [expr {$YtopPx + int(0.5 * $SCREENheightPx)}] set aRxpositions(5) [expr {$XleftPx + int(0.33 * $SCREENwidthPx)}] set aRypositions(5) [expr {$YtopPx + int(0.5 * $SCREENheightPx)}] set aRxpositions(6) [expr {$XleftPx + int(0.66 * $SCREENwidthPx)}] set aRypositions(6) [expr {$YtopPx + int(0.5 * $SCREENheightPx)}] } if {"$RADVARlayout" == "3x3"} { ## ROW 1 set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx set aRxpositions(2) [expr {$XleftPx + int(0.33 * $SCREENwidthPx)}] set aRypositions(2) $YtopPx set aRxpositions(3) [expr {$XleftPx + int(0.66 * $SCREENwidthPx)}] set aRypositions(3) $YtopPx ## ROW 2 set aRxpositions(4) $XleftPx set aRypositions(4) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] set aRxpositions(5) [expr {$XleftPx + int(0.33 * $SCREENwidthPx)}] set aRypositions(5) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] set aRxpositions(6) [expr {$XleftPx + int(0.66 * $SCREENwidthPx)}] set aRypositions(6) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] ## ROW 3 set aRxpositions(7) $XleftPx set aRypositions(7) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] set aRxpositions(8) [expr {$XleftPx + int(0.33 * $SCREENwidthPx)}] set aRypositions(8) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] set aRxpositions(9) [expr {$XleftPx + int(0.66 * $SCREENwidthPx)}] set aRypositions(9) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] } if {"$RADVARlayout" == "4x3"} { ## ROW 1 set aRxpositions(1) $XleftPx set aRypositions(1) $YtopPx set aRxpositions(2) [expr {$XleftPx + int(0.25 * $SCREENwidthPx)}] set aRypositions(2) $YtopPx set aRxpositions(3) [expr {$XleftPx + int(0.50 * $SCREENwidthPx)}] set aRypositions(3) $YtopPx set aRxpositions(4) [expr {$XleftPx + int(0.75 * $SCREENwidthPx)}] set aRypositions(4) $YtopPx ## ROW 2 set aRxpositions(5) $XleftPx set aRypositions(5) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] set aRxpositions(6) [expr {$XleftPx + int(0.25 * $SCREENwidthPx)}] set aRypositions(6) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] set aRxpositions(7) [expr {$XleftPx + int(0.50 * $SCREENwidthPx)}] set aRypositions(7) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] set aRxpositions(8) [expr {$XleftPx + int(0.75 * $SCREENwidthPx)}] set aRypositions(8) [expr {$YtopPx + int(0.33 * $SCREENheightPx)}] ## ROW 3 set aRxpositions(9) $XleftPx set aRypositions(9) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] set aRxpositions(10) [expr {$XleftPx + int(0.25 * $SCREENwidthPx)}] set aRypositions(10) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] set aRxpositions(11) [expr {$XleftPx + int(0.50 * $SCREENwidthPx)}] set aRypositions(11) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] set aRxpositions(12) [expr {$XleftPx + int(0.75 * $SCREENwidthPx)}] set aRypositions(12) [expr {$YtopPx + int(0.66 * $SCREENheightPx)}] } } ## END OF proc 'set_window_positions' ##+######################################################################## ## PROC 'popup_msgVarWithScroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## ## We do not use focus,grab,tkwait in this proc, ## because we use it to show help when the GUI is idle, ## and we may want the user to be able to keep the Help ## window open while doing some other things with the GUI ## such as putting a filename in the filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext } { ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global. ## global env # bell # bell ################################################# ## Set VARwidth & VARheight from $VARtext. ################################################# ## To get VARheight, ## split at '\n' (newlines) and count 'lines'. ################################################# set VARlist [ split $VARtext "\n" ] ## For testing: # puts "VARlist: $VARlist" set VARheight [ llength $VARlist ] ## For testing: # puts "VARheight: $VARheight" ################################################# ## To get VARwidth, ## loop through the 'lines' getting length ## of each; save max. ################################################# set VARwidth 0 ############################################# ## LOOK AT EACH LINE IN THE LIST. ############################################# foreach line $VARlist { ############################################# ## Get the length of the line. ############################################# set LINEwidth [ string length $line ] if { $LINEwidth > $VARwidth } { set VARwidth $LINEwidth } } ## END OF foreach line $VARlist ## For testing: # puts "VARwidth: $VARwidth" ############################################################### ## NOTE: VARwidth works for a fixed-width font used for the ## text widget ... BUT the programmer may need to be ## careful that the contents of VARtext are all ## countable characters by the 'string length' command. ############################################################### ##################################### ## SETUP 'TOP LEVEL' HELP WINDOW. ##################################### catch {destroy $toplevName} toplevel $toplevName # wm geometry $toplevName 600x400+100+50 wm geometry $toplevName +100+50 wm title $toplevName "Note" # wm title $toplevName "Note to $env(USER)" wm iconname $toplevName "Note" ##################################### ## In the frame '$toplevName' - ## DEFINE THE TEXT WIDGET and ## its two scrollbars --- and ## DEFINE an OK BUTTON widget. ##################################### if {$VARheight > 10 || $VARwidth > 100} { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10 || $VARwidth > 100} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################## ## END of PROC definitions. ##+######################## ## Set HELPtext var. ##+######################## set HELPtext "\ ** HELP for this 'Play Animated GIF Files in a Playlist File' utility ** ( essentially a 'front end' for MULTIPLE aniGIF-player programs ) This utility provides a GUI for selecting a 'playlist' of animated GIF files. The animated GIF files may be scattered throughout various directories --- such as sub-directories that store the files for a web site --- OR sub-directories of a home directory --- OR sub-directories on multiple disk drives mounted on a computer --- OR sub-directories on a removable storage device mounted on a computer. Besides selecting the playlist file, this utility allows the user to select an animated-GIF-file 'player' program from among multiple choices --- and allows the user to select a 'layout' of the player windows on the screen. That is, this utility facilitates playing MULTIPLE animated-GIF files 'at a time'. (Since animated GIF files are frequently no more than a few hundred pixels horizontally and vertically, it is possible to fit several of them on the screen without overlap.) This utility provides 'layout' choices such as the following. Configuration Number of of the Windows on Windows the monitor screen at one time (N) ------------------- ----------- 1x1 1 2x1 2 3x1 3 2x2 4 3x2 6 3x3 9 4x3 12 The user can click on a 'Launch' button to start playing the aniGIF files, in the order of the filenames within the playlist file. When the 'Launch' button is clicked the first N aniGIF files are shown on the screen. A 'Next N' button allows for displaying the next N aniGIF files. (Typically, one would want to close the currently-running N player-windows before clicking on the 'Next' button. However, one could simply click on the 'Next' button --- and then move player-windows around so that all or most of the player-windows are showing, without overlap.) ******** STOPPING: ******** This utility also provides for 'interrupting' the display of the sequence of aniGIF files --- via a 'Stop' button on the GUI. This is handy in the cases where the playlist file contains the names of MANY aniGIF files --- and the user realizes that he/she does not want to see them all at this time. If the user does not use the 'Stop' button, he/she can keep clicking on the 'Next N' button to show ALL the aniGIF files in the playlist file. OR ... click on 'Exit' to close the GUI and stop showing any more aniGIF files of the playlist file. **************** PLAYER PROGRAMS: *************** Some examples of animated-GIF 'player' programs: - ImageMagick 'animate' - 'gifview', which comes with the 'gifsicle' program that can be used to create animated GIF files. The player program is chosen via radiobutton widgets on the GUI. The code for the Tk script that constitutes this utility can be easily changed to allow for additional or other animated-GIF players. The GUI is intended to make the user aware of the main (most useful) options available without the user needing to reference the 'man animate' or 'man gifview' commands and other 'animate' or 'gifview' documentation, for example, via web searches. ***************** THE PLAYLIST FILE: ***************** The intent of this utility is to allow the user to easily specify files to be played ... by building a simple 'playlist'. The playlist file contains three types of lines: 1 - Comment lines may be indicated in the file by a # sign in column 1. 2 - Lines that contain a fully-qualified directory name or a 'dot' directory name. Examples: /data/aniGIFs/smileys OR \$env(HOME)/aniGIFs/smileys OR /home/fred/aniGIFs/smileys OR . OR ./other These lines must start with either slash (/) or $ or dot(.). The dot can be used to represent the directory in which the playlist file lies. This useful when putting the playlist file in a directory of animated GIFs and using the dot to represent the directory of the aniGIF files. The directory of aniGIF files may have sub-directories of aniGIF files, as indicated by the last example. The dot allows for moving/copying the aniGIF files (with the playlist file) to different directories without having to change the internals of the playlist file. 3 - Lines that contain a relative filename (relative to the previous directory name). Example: smiley_dancing_20x20_ani.gif These lines SHOULD NOT start with either slash(/) or $ or dot(.) or # --- although a # could be used to comment out a filename. The first non-comment line of the playlist file should be a directory name. There can be more than one directory name in the file. Each directory-name-line is followed by names of aniGIF files that are in the specified directory. *************************************** HOW TO CHANGE Animated-GIF File PLAYERS: *************************************** A different aniGIF player program could be used in place of any of the current aniGIF-players by some simple label-text changes, to an 'aRtext' array variable, in the Tk script playAniGIFfilesInPlaylist_FrontEnd.tk --- and, additionally, a few coding changes to 'if' statements in the proc set_player_command of that Tk script. *************** THE GUI WIDGETS: *************** The GUI consists of about - 3 label widgets - 8 button widgets - 1 entry widget - 9 radiobutton widgets in 2 groups - 0 checkbutton widgets - 0 scale widgets - 0 listbox widgets The button, entry, and radiobutton widgets are the control widgets of the GUI --- so there are about 18 control options. ************************** TYPICAL OPERATION SEQUENCE: ************************** STEP 1: You can use the 'Browse...' button to retrieve a full filename to the playlist filename entry field. STEP 2: Before clicking on the 'LaunchPlayerJob' button, you can also change *radiobutton* settings as needed --- for - a player program and - layout of player windows on the screen --- 1x1(1), 2x1(2), 3x1(3), 2x2(4), 3x2(6), 3x3(9), and 4x3(12). *THEN* click on the 'Launch' button. STEP 3: Clicking on the 'Next N' button causes the next N animated GIF files to be played. The user has the option whether to close the currently running aniGIF 'player' windows before starting the next N windows. STEP 4: Keep doing 'Step 3', until all aniGIFs of the playlist are shown --- or until the 'Stop' button or 'Exit' button is used. --- Clicking on the 'Stop' button sets an indicator. The next time the user clicks on the 'Next N' button, the user will be advised that no more of the aniGIF's in the playlist file will be shown. After a 'Stop', the user can use the 'Launch' button to start another playing sequence via a playlist file. --- In the future, additional options could be added to the GUI, via adding a few widgets, to allow for - controlling the speed of the playback of animated GIF's - controlling the number of loops through the animated GIF's - scaling up/down the size of the playback windows and other playback options --- if at least one of the aniGIF 'players' supports the option. ******** CONTROLS of the Animated-GIF-Players: ******** ************************************************* FEATURES & IDIOSYNCRACIES OF THE 'animate' PLAYER: ************************************************* The 'man' page for the 'animate' command is about 120 lines long (about 2 pages). There are about 70 possible COMMAND-LINE OPTIONS for 'animate', but the main option we use is '-geometry' --- to place the aniGIF-player windows on the screen --- for a user-selected 'layout'. In the 'man help', there are no documented KEYBOARD CONTROLS that you can use when the 'animate' display window comes up --- for example, to pause-playing or to re-size the image. However ... If you click on an 'animate' window, an ImageMagick menu GUI appears. ************************************************* FEATURES & IDIOSYNCRACIES OF THE 'gifview' PLAYER: ************************************************* The 'man' page for the 'gifview' command is about 150 lines long (about 2.5 pages). Although the 'gifview' command (which is usually packaged with the 'gifsicle' command) does not have nearly as many COMMAND-LINE OPTIONS as the ImageMagick 'animate' command, the 'gifview' command may be handy to try if 'animate' fails to play an animated GIF file. 'gifview' has about a dozen COMMAND-LINE OPTIONS, but the main two that we use are: -a to animate the GIF file -- rather than showing it in a default 'slideshow' mode --geometry +x+y to place the various windows -- for a user-selected 'layout' of aniGIF-player windows on the screen. 'gifview' does not have any control options in a GUI. But there some KEYBOARD CONTROLS, including: q Quit gifview. ESC Stop the animation. Space or n Go to the next frame. b or p Go to the previous frame. r or < Go to the first frame. > Go to the last frame. s or a Toggle between animation and slideshow mode. u Toggle between normal and unoptimized mode. --- NOTE: We must use '-geometry' for 'animate' and '--geometry' for 'gifview. *********************************************** SETTING UP THIS UTILITY FOR EASY ICON-CLICK USE: *********************************************** The set of files for this utility (a single Tk script) could be put in a sub-directory of the user's home directory, such as \$HOME/apps/tkPlayAniGIFsInPlaylist. Then the user can use their desktop system (such as Gnome or KDE) to set up the Tk script as an icon on the desktop (or in a desktop 'panel'). Then, whenever the user wants to start the 'play-aniGIF-files-in-a-playlist' GUI, the user can click on the desktop icon to startup the Tk script. ***************** STARTUP DIRECTORY: ***************** If you want the playlist filename to start at a different directory, in the Tk script, you can look for the line set playlistDIR \"\$env(HOME)\" and change it according to nearby examples. " ##+###################################################### ## ADDITIONAL GUI INITIALIZATION section. ##+###################################################### ## NOTE: Some of the settings of variables done when ## widgets were defined in the widget-defining section ## could be moved down here to keep most of the ## initializing of variables in one place. ##+###################################################### ##+####################################### ## Set screensize vars, for use in the ## 'set_window_positions' proc. ##+####################################### set SCREENwidthPx [winfo screenwidth .] set SCREENheightPx [winfo screenheight .] ##+################################################ ## Initialize the setting of variable 'VARcommand', ## according to the initial setting of the ## player-program radiobuttons. ##+################################################ set_player_command ##+########################################### ## Disable/enable widgets according to initial ## GUI settings. (NOT IMPLMENTED, yet) ##+########################################## # disable_enable_widgets