uniquename - 2012oct14
Near the bottom of my uniquename page, I made a commitment to donating five 'PlotQuik' utilities to this Tcl-Tk wiki. Donating them requires 'extracting' the Tk scripts from the 'feHandyTools' subsystem of my free, open-source Freedom Environment software, that is available at www.freedomenv.com [L1 ].
The Tk code in the 'feHandyTools' system is written to use Tk 'include' files of common code. The 'include' code is merged into various Tk scripts via Tk 'source' statements.
In order to have 'stand-alone' scripts that I can donate here, I needed to merge some statements from those Tk-code 'include' files into the 'PlotQuik' Tk scripts.
I have done that for the 'pie chart' PlotQuik GUI and the code is presented below.
In the process of doing the merge and changing some comments in the code, I made a few improvements in the script (especially in regard to positioning the pie between a top title line and a 'legend' below the pie). So this donation is actually a little better than the 'PlotQuik' pie-chart-making Tk script in the current version of 'feHandyTools'.
I also added braces to some of the 'expr' statements to improve the execution speed by a few milliseconds.
However, it is hard to make a visually significant improvement in the execution speed, because the plot is performed in less than a second (after you enter/change the 3 user input fields --- title line, pie segment percents, pie segment legend-labels).
Note that this utility is 'Quik' in two ways:
1) Easy for the user to enter the data for the plot.
2) Once the data is entered, the plot is rendered in a fraction of a second.
Here is a reduced-size image of the pie chart GUI.
A full-size image of the GUI is available at this link [L2 ].
This GUI is not meant to be a toy. A plot of budget data is used to show that this GUI is meant to be an actual 'productivity' tool --- a free and open-source one.
(When we are all working on 'retina display' monitors --- resolution of more than 2000x1500 for desktop computers, like WQXGA = 2560x1600 --- then the jaggies of the circle may be unnoticeable. The images above were captured on a 1024x768 monitor.)
_____________________________________________________________________
Below is the code that produced this GUI.
There are comments above the sample code, in a section titled 'OUTPUT:', that describe how one could capture and make use of images produced by this GUI.
I follow my usual 'canonical' structure for Tk code, for this Tk script:
0) Set general window & widget parms (win-name, win-position, win-color-scheme, fonts, widget-geometry-parms, win-size-control). 1a) Define ALL frames and sub-frames. 1b) Pack ALL frames and sub-frames. 2) Define & pack all widgets in the frames. 3) Define keyboard or mouse/touchpad/touch-sensitive-screen action BINDINGS, if needed. 4) Define PROCS, if needed. 5) Additional GUI initialization (typically with one or more of the procs), if needed.
This structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.
This structure makes it easy for me to find code sections --- while generating and testing a script, and when looking for code snippets to include in scripts.
_________________________________________________________________
As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus Tk scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', and '-expand' --- on all the 'pack' commands for the frames and widgets.
I think I have found a good setting of the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various widgets of this GUI. In particular ...
The 'canvas' widget expands/contracts appropriately when the window size is changed --- and button and label widgets stay fixed in size and relative-location as the window size is changed. Entry fields x-expand if the window x-expands.
Note that there are 'DwnCan' and 'UpCan' buttons on the GUI to help re-size the canvas appropriately within the current window size, whatever that may be.
If anyone wants to change the way the GUI configures itself as the main window size is changed, they can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various widgets --- to get the widget behavior that they want.
Also, you could change the fonts used for the various GUI widgets and plot elements (title and legend). For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.
In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.
Furthermore, there are variables used to set geometry parameters of widgets --- parameters such as border-widths and padding. Feel free to experiment with those parameters as well.
___
Note that the 'BkgdColor' button calls on a color-selector-GUI script to set the background color of the canvas. You can make that color-selector script by cutting-and-pasting the code from the page A non-obfuscated color selector GUI on this site.
When you want to make a printed copy of a screen image, to save on ink, you will probably want to make the background of the plot canvas pure white (255,255,255 or #FFFFFF).
____________________________________________________________________
That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.
The main plotting code is in the proc 'update_plot'.
The copious comments in the code might help Tcl-Tk coding 'newbies' get started in making GUI's like this. Without the comments (especially in the 'update_plot' proc), the code might look too cryptic --- and potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch America's/Japan's/Germany's/Albania's Funniest Home Videos.
#!/usr/bin/wish ## ## SCRIPT NAME: plot_quik_pie2d_forWiki.tk ## ## --- adapted from the script 'plot_quik_pie2d.tk' in the ## 'feHandyTools' subsystem of the 'Freedom Environment' ## subsystems at www.freedomenv.com --- adapted for donation ## of the code to the Tcl-Tk wiki at wiki.tcl.tk. ## ##+############################################################################ ## PURPOSE: A intuitive, easy-to-use 2D (not pseudo-3D) PIE-CHART plot utility ## that can be used to QUICKLY make 'presentation quality' plots ## --- for web pages, emails, or whatever. ## ## This Tk script presents a GUI with a canvas widget that shows ## a 2-D pie-chart plot --- with titles and labels that can be ## dragged with the mouse. ## ## This script presents entry fields in the GUI to prompt for ## pie-slice data and titles. ## ## This is a pie-chart-utility implementation using BASIC ## Tcl-Tk commands, i.e. not requiring an 'extension' of Tcl or Tk. ## ## Unfortunately, there do not seem to be any FAIRLY GENERAL, yet ## RELATIVELY SIMPLE, pie-chart plotting scripts at Tcl-Tk archive ## sites --- even in 2012, more than 20 years after the development ## of the necessary Tk canvas facilities to support plotting. ## ## Nor are such general, easy-to-use pie-chart plotting Tk scripts ## available via web searches on keyword strings such as ## 'bin wish canvas' or 'bin wish oval'. ## ## Even searches on 'canvas' and 'oval' on the wiki.tcl.tk site, ## in early 2012, yield only simplistic pie chart 'demos' that are ## not suited to general and FAST pie chart plots --- with entry ## fields for quick entry of user data. ## ## This script is meant to fill that long-time void. ## ## SOURCES and CREDITS: ## ## The technique of dragging canvas items came from the Tcl-Tk demo in ## /usr/local/lib/tk4.0/demos/plot.tcl (on SGI-IRIX Unix, 1995 May 26). ## However, that script allowed the user to drag the data points. ## This script allows the user to drag titles and labels. ## ## On Linux (for example, Ubuntu 9.10, circa 2009), see ## /usr/share/doc/tk8.4/examples/plot.tcl ## or /usr/share/doc/tk8.5/examples/plot.tcl ## There are 60-plus other Tcl-Tk code examples are in the ## 'examples' directory. ## ## See also 'create oval' plot scripts like ## 'items.tcl' and 'twind.tcl' in /usr/share/doc/tk8.x/examples/. ## These scripts provide examples of procs to move 'items' around ## on the canvas with a mouse. ## ## _____________________________________________________________ ## ## There is a '3D' pie-chart package of tcl-tk scripts (in a rather ## complex, OBJECT-ORIENTED FORM). See the 'demo.tcl' script ## from the contributed package 'tkpiechart' (versions 1.2 thru ## 5.x by 1999) by Jean-Luc Fontaine. ## ## Downloads of 'tkpiechart' were available at Fontaine's home page ## http://jfontain.free.fr/ ## (version 6.5, circa 2006?), as late as 2011 Jul. ## ##+##################################################################### ## ## INPUTS (via entry fields on the GUI): ## plot title, pie-section-percents, pie-section-names. ## ## OUTPUT: Intended for screen/window capture to an image file with a ## screen-capture tool (such as 'gnome-screenshot' on Linux). ## ## The image could be cropped with an image editor (such as ## 'mtpaint' on Linux) and the cropped image could be printed ## using an image view-print utility (such as 'eog' = Eye of ## Gnome, on Linux). ## ## If you are going to print the image, you will probably ## want to change the background color to white, via the ## 'BkgndColor' button at the top of the GUI. ## ## (Optionally, a Postscript-Print button and proc ## could be implemented --- sample code commented below.) ## ##+##################################################################### ## STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name, win-position, color-scheme, ## fonts, widget-geom-parms, win-size-control, text-array-for-labels-etc). ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack ALL frames and sub-frames. ## 2) Define & pack ALL widgets within frames --- typically going ## top-to-bottom and/or left-to-right, frame by frame. ## ## After ALL widgets are defined for a frame, ## pack the widgets in the frame. ## ## 3) Define key and mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if any. ## 4) Define PROCS, if any. ## 5) Additional GUI INITIALIZATION (typically with one or more ## of the procs), if needed. ## ## In more detail for this particular script: ## ## 1a) Define ALL frames: 'fRbuttons', 'fRtitle_main' , 'fRsect_pcnts' , ## 'fRsect_names' 'fRplot.fRmsg', 'fRplot.fRcanvas' ## ## 1b) Pack these frames in appropriate groups to get proper behavior ## of widgets during window expansion (expansion is allowed). ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in top-to-bottom, left-to-right order: ## ## - 'fRbuttons' contains buttons, including 'Exit', 'UpdatePlot', ## 'ToggleBorder', 'BkgdColor', 'DwnCan', 'UpCan'. ## ## - 'fRtitle_main' contains 1 label & 1 entry widget. ## - 'fRsect_pcnts' contains 1 label & 1 entry widget. ## - 'fRsect_names' contains 1 label & 1 entry widget. ## ## - 'fRplot.fRmsg' contains a how-to-use message in a label widget ## on the left side of the 'fRplot' frame. ## ## - 'fRplot.fRcanvas' contains a canvas widget on the right side ## of the 'fRplot' frame (to be populated with ## 'items' when the 'update_plot' proc is issued --- either ## whenever the 'UpdatePlot' button in 'fRbuttons' ## is poked --- or when issued at the bottom of this script ## to initialize the GUI with a sample plot). ## ## 3) Define BINDINGS: (See BINDINGS code section below for more info.) ## ## To Drag pie-slice-titles (and labels and main title): ## - .c bind TAGmoveable <1> "itemSelect .c %x %y" ## - bind .c <B1-Motion> "itemMove .c %x %y" ## - .c bind TAGmoveable <ButtonRelease-1> ".c dtag TAGselected" ## ## 4) Define PROCS: (See PROCS code section below for more info.) ## - 'itemSelect' - To select main/pie-slice/legend titles ## - 'itemMove' - To drag main/pie-slice/legend titles ## - 'update_plot' - for 'UpdatePlot' button; ## (Re)Sets contents of canvas widget!! ## - 'toggle_border' - for 'ToggleBorder' button ## - 'popup_msgVarWithScroll' - to pop up a msg whenver needed ## - 'getset_bkgdcolor' - for 'BkgdColor' button ## - 'resize_win' - for use by 'downsize_canvas' proc ## - 'downsize_canvas' - for 'DwnCan' button ## - 'upsize_canvas' - for 'UpCan' button ## ## procs Not used yet (example code): ## - 'print_plot' - for 'Print' button ## - 'print_preview' - for 'PrtPreview' button ## - 'get_image' - for 'GetImage' button ## ## 5) Additional GUI initialization: issue 'update_plot' to present ## an intitial demo plot on the canvas ## ##+####################################################################### ## 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 2012oct12 Started development, on Ubuntu 9.10, ## based on my code 'plot_quik_pie2d.tk' ## in the 'feHandyTools' subsystem of ## the 'Freedom Environment' subsystems ## at www.freedomenv.com --- adapted for ## donation of this code to the Tcl-Tk ## wiki at wiki.tcl.tk. ## Updated by: Blaise Montandon 2013jul23 Put most text strings in aRtext array. ## Re-order procs. Chg 2 proc names and ## 2 var names for the select, move procs. ## Some esoteric changes to 'downsize_can' ## and 'upsize_can' procs. Reduced font of ## the Help msg, so that the text is ## unlikely to be clipped on netbook ## screens (600 pixels high). ## Added 'Px' on end of some var names. ##+############################################################################ ##+####################################################################### ## Set general window parms (win-title, win-position). ##+####################################################################### package require Tk wm title . "PlotQuik - Pie Chart - version for wiki.tcl.tk" wm iconname . "PlotPie" wm geometry . +15+30 ##+###################################################### ## Set the color scheme for the window and its widgets --- ## and set the color for the canvas background. ##+###################################################### set r255 200 set g255 200 set b255 200 ## If env vars R255,G255,B255 were set, then we could use the following ## format statement to get a hex value for specifying tkGUI colors, ## after 'catch'-ing the three values into r255,g255,b255 Tcl vars. # catch { set r255 "$env(R255)" } # catch { set g255 "$env(G255)" } # catch { set b255 "$env(B255)" } set COLOR_hex [format "#%02X%02X%02X" $r255 $g255 $b255] ## Or could set palette from a hex-valued env var. # catch { set COLOR_hex "$env(FE_PLOT_PALCOLOR_HEX)" } tk_setPalette "$COLOR_hex" ## Set colors for canvas/plot items. # set COLOR_plottitle black set COLOR_plottitle #000000 ## Set colors for GUI widgets. set entryBKGD "#f0f0f0" ##+########################################################## ## We use a VARIABLE-WIDTH FONT for label and button widgets. ## ## We use a FIXED-WIDTH FONT for listboxes and entry fields ## and text widgets, if any. ##+########################################################## set FONTsize 14 set FONT_SMALLsize 12 set FONTparms_varwidth " -family {comic sans ms} \ -size -$FONTsize -weight bold -slant roman" set FONTparms_SMALL_varwidth " -family {comic sans ms} \ -size -$FONT_SMALLsize -weight normal -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 set FONTparms_fixedwidth " -family {dejavu sans mono} \ -size -$FONTsize -weight bold -slant roman " set FONTparms_SMALL_fixedwidth " -family {dejavu sans mono} \ -size -$FONT_SMALLsize -weight normal -slant roman " ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## Liberation Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+##################################################################### ## DEFINE (temporary) FONT VARS to be used in '-font' widget specs below ## --- and for some plot titles. ##+##################################################################### eval font create fontTEMP_button $FONTparms_varwidth eval font create fontTEMP_label $FONTparms_varwidth eval font create fontTEMP_entry $FONTparms_fixedwidth eval font create fontTEMP_listbox $FONTparms_fixedwidth eval font create fontTEMP_msg $FONTparms_fixedwidth eval font create fontTEMP_text $FONTparms_fixedwidth eval font create fontTEMP_SMALL_button $FONTparms_SMALL_varwidth eval font create fontTEMP_SMALL_label $FONTparms_SMALL_varwidth eval font create fontTEMP_SMALL_entry $FONTparms_SMALL_fixedwidth eval font create fontTEMP_SMALL_listbox $FONTparms_SMALL_fixedwidth eval font create fontTEMP_SMALL_msg $FONTparms_SMALL_fixedwidth eval font create fontTEMP_SMALL_text $FONTparms_SMALL_fixedwidth ## For the text in the plot: eval font create fontTEMP_plottitle $FONTparms_varwidth eval font create fontTEMP_sectnum $FONTparms_varwidth eval font create fontTEMP_legend $FONTparms_SMALL_varwidth ##+####################################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. padx, pady for Buttons) ##+####################################################################### ## Set internal PADDING (X and Y) for BUTTON widgets. set fePADY_button 0 set fePADX_button 0 ## Set BORDER-WIDTH for LABEL, BUTTON, ENTRY, LISTBOX, ## TEXT, and MESSAGE widgets. set feBDwidth_label 2 set feBDwidth_button 2 set feBDwidth_entry 2 set feBDwidth_listbox 2 set feBDwidth_text 2 set feBDwidth_msg 2 ##+################################################################### ## Set a MINSIZE of the window (roughly). ## ## For width, allow for the minwidth of the '.fRbuttons' frame: ## about 4 widgets --- Exit & Help buttons, a label for the ## scale widget, and the scale widget. ## ## For height, allow ## 1 char high for the '.fRbuttons' frame, ## ## ## 24 pixels high for the '.fRplot' frame. ##+################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "Exit UpdatePlot ToggleBorder BkgdColor DwnCan UpCan"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 4 x 4 pixels/widget for borders/padding for ## at least 6 button widgets. set minWinWidthPx [expr {24 + $minWinWidthPx}] ## MIN HEIGHT --- ## for the 5 stacked frames allow ## 1 char high for 'fRbuttons' ## 1 char high for 'fRtitle_main' ## 1 char high for 'fRsect_pcnts' ## 1 char high for 'fRsect_names' ## 24 pixels high for 'fRplot'. set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {4 * $CharHeightPx}] set minWinHeightPx [expr {$minWinHeightPx + 24}] ## Add about 28 pixels for top-bottom window decoration, ## about 5x4 pixels for each of the 5 stacked frames and their ## widgets (their borders/padding). set minWinHeightPx [expr {$minWinHeightPx + 48}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We allow the window to be resizable and we pack the canvas with ## '-fill both -expand 1' so that the canvas 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 ##+############################################################## ## 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(buttonPRINT) "Print" # set aRtext(buttonPRTPREVIEW) "PrtPreview" set aRtext(buttonCOLOR) "BkgdColor" set aRtext(buttonBORDER) "ToggleBorder" # set aRtext(buttonIMAGE) "GetImage" set aRtext(buttonUPDATE) "UpdatePlot" set aRtext(buttonDWNCAN) "DwnCan" set aRtext(buttonUPCAN) "UpCan" set aRtext(labelCOLOR) "Canvas background color:" ## For '.fRtitle_main' frame: set aRtext(labelPLOTTITLE) "Plot Title:" ## For '.fRsect_pcnts' frame: set aRtext(labelPERCENTS) "Pie-Section Percents:" set aRtext(labelPERCENTS2) "(No more than 100 total.)" ## For '.fRsect_names' frame: set aRtext(labelSECTNAMES) "Pie-Section Names:" ## For '.fRmid.fRmsg' frame: set aRtext(labelHELPMSG) "To the right is a 'canvas' to contain a single pie-chart plot. Recommended order of actions: 1. Increase the canvas AND plot size with the 'UpCan' button. Downsize with 'DwnCan'. Use the 'BkgdColor' button to change background color. 2. After making changes in any of the several entry fields, use the 'UpdatePlot' button. 3. You can drag any of the titles and labels with mouse-button-1. When you use the 'UpdatePlot' button, titles and labels are returned to their initial locations (handy to restore labels if pulled off-canvas). You can drag labels back where wanted. 4. You can use screen-grab, image- editor, and image-view-print utilities to make an image file or to print the plot." ## END OF if { "$VARlocale" == "en"} ##+#################################################################### ## DEFINE *ALL* THE FRAMES -- (top to bottom): ## ## - 'fRbuttons' contains buttons -- ## Exit & UpdatePlot and at least 3 other buttons. ## ## - 'fRtitle_main' contains 1 label & 1 entry widget. ## - 'fRsect_pcnts' contains 1 label & 1 entry widget. ## - 'fRsect_names' contains 1 label & 1 entry widget. ## ## - 'fRplot.fRmsg' contains a how-to-use message in a label. ## ## - 'fRplot.fRcanvas' contains a canvas widget. ## ##+#################################################################### ## For testing of packer location & sizing of frames: # 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 .fRtitle_main -relief $feRELIEF_frame -bd $feBDwidth_frame frame .fRsect_pcnts -relief $feRELIEF_frame -bd $feBDwidth_frame frame .fRsect_names -relief $feRELIEF_frame -bd $feBDwidth_frame frame .fRplot -relief $feRELIEF_frame -bd $feBDwidth_frame frame .fRplot.fRmsg -relief raised -bd 2 frame .fRplot.fRcanvas -relief raised -bd 2 ##+######################################################## ## PACK *ALL* the FRAMES. ##+######################################################## ## PACK THE FRAMES SEPARATELY, in order to ## experiment with different behaviors in window expansion ## (if expansion is allowed/implemented). ##+######################################################## pack .fRbuttons \ -side top \ -anchor w \ -fill none \ -expand 0 pack .fRtitle_main \ -side top \ -anchor w \ -fill x \ -expand 0 pack .fRsect_pcnts \ -side top \ -anchor w \ -fill x \ -expand 0 pack .fRsect_names \ -side top \ -anchor w \ -fill x \ -expand 0 pack .fRplot \ -side top \ -anchor center \ -fill both \ -expand 1 pack .fRplot.fRmsg \ -side left \ -anchor nw \ -fill y \ -expand 0 pack .fRplot.fRcanvas \ -side right \ -anchor ne \ -fill both \ -expand 1 ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+######################################################## ## IN THE 'fRbuttons' frame -- DEFINE 5 BUTTON WIDGETs. ## THEN PACK EM. ##+######################################################## button .fRbuttons.buttExit \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -text "$aRtext(buttonEXIT)" \ -command {exit} ## User could implement Help button someday. # button .fRbuttons.buttHelp \ # -text "$aRtext(buttonHELP)" \ # -font fontTEMP_button \ # -padx $fePADX_button \ # -pady $fePADY_button \ # -command {popup_msgVarWithScroll .fRhelp "$HELPtext"} ## User could implement Print option someday. # button .fRbuttons.buttPrint \ # -text "$aRtext(buttonPRINT)" \ # -font fontTEMP_button \ # -padx $fePADX_button \ # -pady $fePADY_button \ # -command {print_plot} ## User could implement Print-preview option someday. # button .fRbuttons.buttPrtPreview \ # -text "$aRtext(buttonPRTPREVIEW)" \ # -font fontTEMP_button \ # -padx $fePADX_button \ # -pady $fePADY_button \ # -command {print_preview} button .fRbuttons.buttBkgdColor \ -text "$aRtext(buttonCOLOR)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -command {getset_bkgdcolor} button .fRbuttons.buttBorder \ -text "$aRtext(buttonBORDER)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -command {toggle_border} ## User could implement an Apply-Image option someday. # button .fRbuttons.buttImage \ # -text "$aRtext(buttonIMAGE)" \ # -font fontTEMP_button \ # -padx $fePADX_button \ # -pady $fePADY_button \ # -command {get_image} button .fRbuttons.buttUpdate \ -text "$aRtext(buttonUPDATE)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -command {update_plot} button .fRbuttons.buttDWNwin \ -text "$aRtext(buttonDWNCAN)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -command {downsize_can} button .fRbuttons.buttUPwin \ -text "$aRtext(buttonUPCAN)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -command {upsize_can} label .fRbuttons.lab1 \ -text "$aRtext(labelCOLOR)" \ -font fontTEMP_label \ -justify left label .fRbuttons.lab2 \ -text "$COLOR_hex" \ -font fontTEMP_label \ -justify left pack .fRbuttons.buttExit \ .fRbuttons.buttBorder \ .fRbuttons.buttUpdate \ .fRbuttons.buttBkgdColor \ .fRbuttons.buttDWNwin \ .fRbuttons.buttUPwin \ .fRbuttons.lab1 \ .fRbuttons.lab2 \ -side left \ -anchor center \ -fill none \ -expand 0 # .fRbuttons.buttHelp \ # .fRbuttons.buttPrint \ # .fRbuttons.buttPrtPreview \ # .fRbuttons.buttImage \ ##+######################################################## ## IN THE 'fRtitle_main' frame -- ## DEFINE 1 LABEL & 1 ENTRY WIDGETs. THEN PACK EM. ##+######################################################## label .fRtitle_main.lab1 \ -text "$aRtext(labelPLOTTITLE)" \ -font fontTEMP_label \ -anchor w ## We initialize this var at the bottom of this script, ## in the GUI inititalization section. # set titleMain " MAIN PLOT TITLE goes here." entry .fRtitle_main.ent1 \ -width 80 \ -font fontTEMP_entry \ -bg $entryBKGD \ -relief sunken \ -bd 2 \ -textvariable titleMain pack .fRtitle_main.lab1 \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtitle_main.ent1 \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## IN THE 'fRsect_pcnts' frame -- ## DEFINE 1 LABEL & 1 ENTRY WIDGETs. THEN PACK EM. ##+######################################################## label .fRsect_pcnts.labPcnts \ -text "$aRtext(labelPERCENTS)" \ -font fontTEMP_label \ -justify left \ -anchor w ## We initialize this var at the bottom of this script, ## in the GUI inititalization section. # set SectPcnts "10 30 40 20" entry .fRsect_pcnts.entPcnts \ -width 50 \ -font fontTEMP_entry \ -bg $entryBKGD \ -relief sunken \ -bd 2 \ -textvariable SectPcnts label .fRsect_pcnts.labPcnts2 \ -text "$aRtext(labelPERCENTS2)" \ -font fontTEMP_label \ -justify left \ -anchor w pack .fRsect_pcnts.labPcnts \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsect_pcnts.entPcnts \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRsect_pcnts.labPcnts2 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRsect_names' frame -- ## DEFINE 1 LABEL & 1 ENTRY WIDGETs. THEN PACK EM. ##+######################################################## label .fRsect_names.labSectnames \ -text "$aRtext(labelSECTNAMES)" \ -font fontTEMP_label \ -justify left \ -anchor w ## We initialize this var at the bottom of this script, ## in the GUI inititalization section. # set SectNames "\"BMW sportscar\" Mercedes Lamborghini Lotus" entry .fRsect_names.entSectnames \ -width 80 \ -font fontTEMP_entry \ -bg $entryBKGD \ -relief sunken \ -bd 2 \ -textvariable SectNames pack .fRsect_names.labSectnames \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsect_names.entSectnames \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## IN THE 'fRplot.fRmsg' frame -- DEFINE 1 LABEL WIDGET. ## THEN PACK IT. ##+######################################################## label .fRplot.fRmsg.lab \ -font fontTEMP_SMALL_msg \ -justify left \ -anchor nw \ -text "$aRtext(labelHELPMSG)" pack .fRplot.fRmsg.lab \ -side top \ -anchor nw \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRplot.fRcanvas' frame -- DEFINE 1 CANVAS WIDGET. ## THEN PACK IT. ## ## We highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+######################################################## ## Instead of hard-coding an initial canvas size, like this ## set canWidthPx 500 ## set canHeightPx 375 ## we set the canvas size in proportion to the screen size. set SCRNsizexPx [winfo screenwidth .] set SCRNsizeyPx [winfo screenheight .] set canWidthPx [ expr {int(6 * $SCRNsizexPx / 10)} ] set canHeightPx [ expr {int(6 * $SCRNsizeyPx / 10)} ] ## FOR TESTING: # puts "canWidth: $canWidthPx" # puts "canHeight: $canHeightPx" canvas .fRplot.fRcanvas.c \ -width $canWidthPx \ -height $canHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 pack .fRplot.fRcanvas.c \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+################################################## ##+################################################## ## END OF THE MAIN, INITIAL GUI BUILDING SECTION. ##+################################################## ##+################################################## ##+##################################################### ##+##################################################### ## DEFINE BINDINGS -- for mouse actions: ## ## - to Drag slice-titles ## ## NOT IMPLEMENTED: ## - to Change color of slice-titles during their move ##+#################################################### ##+#################################################### ##+################################################################# ##+ To Drag main-title, slice-numbers, legend-titles, image (if any): ##+################################################################# .fRplot.fRcanvas.c bind TAGmoveable <ButtonPress-1> \ "itemSelect .fRplot.fRcanvas.c %x %y" bind .fRplot.fRcanvas.c <B1-Motion> \ "itemMove .fRplot.fRcanvas.c %x %y" .fRplot.fRcanvas.c bind TAGmoveable <ButtonRelease-1> \ ".fRplot.fRcanvas.c dtag TAGselected" ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## - 'itemSelect' ## - 'itemMove' ## - 'update_plot' (the pie-chart maker) ## - 'getset_bkgdcolor' ## ## - 'resize_win' ## - 'downsize_canvas' ## - 'upsize_canvas' ## ## - 'toggle_border' ## - 'popup_msgVarWithScroll' ## ## - 'print_plot' (not used yet) ## - 'print_preview' (not used yet) ## - 'get_image' (not used yet) ##+##################################################################### ##+##################################################################### ##+##################################################################### ## 'itemSelect' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked when the mouse is pressed over one of the ## canvas items. It sets up state to allow the item to be dragged. ## ## Arguments: ## w - The canvas window. ## x, y - The coordinates of the mouse press. ## ## CALLED BY: the binding ## .fRplot.fRcanvas.c bind TAGmoveable <ButtonPress-1> ##+##################################################################### set lastXsel 0 set lastYsel 0 proc itemSelect {w x y} { global lastXsel lastYsel $w dtag TAGselected $w addtag TAGselected withtag current $w raise current set lastXsel $x set lastYsel $y } ## END of proc 'itemSelect' ##+##################################################################### ## 'itemMove' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked during mouse motion events. ## It drags the current item. ## ## Arguments: ## w - The canvas window. ## x, y - The coordinates of the mouse. ## ## CALLED BY: the binding ## bind .fRplot.fRcanvas.c <B1-Motion> ##+##################################################################### proc itemMove {w x y} { global lastXsel lastYsel $w move TAGselected [expr {$x - $lastXsel}] [expr {$y - $lastYsel}] set lastXsel $x set lastYsel $y } ## END of proc 'itemMove' ##+##################################################################### ## 'update_plot' PROCEDURE (for pie-chart plot) ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to (re)create the canvas items using the ## current GUI entries. Uses 'arcs' to create pie-slices in chart. ## Uses 'create oval' to create pie background (white/transparent-on-print). ## Uses 'create text' for titles and labels. ## ## Arguments: none ## ## CALLED BY: button .fRbuttons.buttUpdate ##+##################################################################### proc update_plot {} { global titleMain SectPcnts SectNames global COLOR_plottitle # global COLOR_PIEbkgd global borderOnOff ######################################################################### ## CHECK FOR EQUAL NUMBER OF SECTION (SLICE) PERCENTS AND NAMES. ######################################################################### set Npcnts [ llength $SectPcnts ] set Nnames [ llength $SectNames ] if { $Npcnts != $Nnames } { set message "Number-of-Sect-%s NOT = Number-of-Sect-Names." tk_dialog .xxx "Input Err" $message warning 0 Close return } ######################################################################### ## CHECK FOR VALID NUMERIC ENTRIES IN $SectPcnts. ## (Not implemented. Rather than preclude some format that might ## work by implementing a too-conservative regexp, we let the ## error trace-back dialog greet the user.) ######################################################################### #### Example for splitting the numbers into a list, to process in a loop. ## set SectVals [ split [string trim $SectPcnts] " " ] #### Example for removing leading and trailing spaces. ## set SectPcnts [string trim $SectPcnts] #### Could try something like the following 'regexp'. But this is for #### 3 integer numbers, and we need an arbitrary number of floating-point nums. ## ## if [regexp {^([ ]*[0-9]+)[ ]([ ]*[0-9]+)[ ]([ ]*[0-9]+)[ ]*(.*)} $line {} r g b name] { ## .clr insert end "$r $g $b $name" ## } elseif [regexp {^!} $line] { ## } else { ## puts "$me: Can't parse \"$line\"" ## } ## #################################### ## Get the current canvas size. #################################### set CURcanWidthPx [winfo width .fRplot.fRcanvas.c] set CURcanHeightPx [winfo height .fRplot.fRcanvas.c] ########################################################################### ## CLEAR THE CANVAS WIDGET (and set border toggle var to OFF). ## (The border and image opts may be implemented via 2 radiobuttons, someday.) ########################################################################### .fRplot.fRcanvas.c delete all set borderOnOff OFF ## Alternative -- that could avoid deletion of image/logo & border. # .fRplot.fRcanvas.c delete TAGtitles # .fRplot.fRcanvas.c delete TAGarcs ## where TAGarcs is the tag for pie-slices (filled-arcs) ## and TAGtitles is the tag for the main title, pie-section-numbers, and ## the legend-lines. ####################################################################### ## PUT MAIN TITLE NEAR TOP OF THE PLOT-CANVAS. ####################################################################### set xPx [expr { 0.10 * $CURcanWidthPx }] ## Note that we are placing the text with '-anchor nw'. ## Rather than setting the top position of the title to be ## proportional to the canvas height, like so: ## set yPx [expr { 0.04 * $CURcanHeightPx }] ## INSTEAD we set the top postion in absolute units, pixels, ## and we use this for a margin both above and below the ## top title. set plottitleYmarginPx 4 .fRplot.fRcanvas.c create text \ $xPx $plottitleYmarginPx \ -anchor nw \ -justify center \ -text "$titleMain" \ -font fontTEMP_plottitle \ -fill $COLOR_plottitle \ -tag {TAGmoveable TAGtitles} ######################################################################## ## SET CORNERS OF PIE-SQUARE (x1,y1,x2,y2) to a square ## of height & width = $pieSquareDimPx -- which fits into the ## canvas height & width, with some vert margin & horiz margin. ######################################################################## ## FIRST determine size of a square that fits inside the canvas --- ## allowing some room for the legend at the bottom and title at top. ######################################################################## ## Calc legend height --- Npcnts chars above bottom of canvas. ## We throw in another char-height for some margin. set CharHeightPx [font metrics fontTEMP_legend -linespace] set legendHeightPx [ expr {($Npcnts + 1) * $CharHeightPx} ] ## Get top title height. set plottitleHeightPx [font metrics fontTEMP_plottitle -linespace] ## As a first pass at setting $pieSquareDimPx, base it on the canvas height ## and subtract out legend height and plottitle height and margin. # set pieSquareDimPx [expr { int($vertFactor * $CURcanHeightPx) }] set pieSquareDimPx [ expr {$CURcanHeightPx - \ ($legendHeightPx + $plottitleHeightPx + (2 * $plottitleYmarginPx))} ] ## As a second pass, check that $pieSquareDimPx will fit into the ## width of the canvas. if { $pieSquareDimPx > $CURcanWidthPx } { set pieSquareDimPx [expr { 0.90 * $CURcanWidthPx}] } ## FOR TESTING: # puts "update_plot > CURcanWidthPx: $CURcanWidthPx CURcanHeightPx: $CURcanHeightPx" # puts " legendHeightPx: $legendHeightPx pieSquareDimPx: $pieSquareDimPx" ######################################################################## ## SET COORDS OF MIDPOINT OF PIE-BOX -- AND 'RADIUS' OF PIE. ## (for use in computing xy-locations of labels on the slices) ######################################################################## set pieSquareRadiusPx [expr { int($pieSquareDimPx / 2) }] set pieSquare_xmidPx [expr { int($CURcanWidthPx / 2) }] ## The following is too simplistic for setting the y-midpoint of the pie-box. ## set pieSquare_ymidPx [expr { int($CURcanHeightPx / 2) }] ## Instead, we take into account the top plot title height and its y-margins. set pieSquare_ymidPx \ [expr {(2 * $plottitleYmarginPx) + $plottitleHeightPx + $pieSquareRadiusPx}] ######################################################################## ## SET CORNERS OF PIE-SQUARE (x1,y1,x2,y2) ######################################################################## set pieSquare_x1Px [expr { $pieSquare_xmidPx - $pieSquareRadiusPx }] set pieSquare_y1Px [expr { $pieSquare_ymidPx - $pieSquareRadiusPx }] set pieSquare_x2Px [expr { $pieSquare_xmidPx + $pieSquareRadiusPx }] set pieSquare_y2Px [expr { $pieSquare_ymidPx + $pieSquareRadiusPx }] ## FOR TESTING: # puts "Pie box coord's (in pixels): # $pieSquare_x1Px $pieSquare_y1Px $pieSquare_x2Px $pieSquare_y2Px" ######################################################################### ## SET AN ARRAY OF COLORS (seven) TO USE IN THE PLOT LOOP BELOW. ## (You can use 'showrgb' on Linux to see colornames and their RGB values.) ######################################################################### ## set sectColors {red green deepskyblue cyan magenta yellow black} ## ## set sectColors {#FF0000 #00FF00 #00BFFF #00FFFF #FF00FF #FFFF00 #000000 } ## set sectColors {indianred green3 deepskyblue cyan3 \ ## ~magenta ~yellow3 ~gray78} ## ## set sectColors {#CD5C5C #00CD00 #00BFFF #00CDCD \ ## ##CC00FF #B4B400 #C7C7C7 } ## Must be lighter, for printing: set sectColors {#FD8C8C #30FD30 #30CFFF #30FDFD \ #FC30FF #E4E430 #F7F7F7 } set numColors [llength $sectColors] ###################################################################### ## PREPARE FOR PLOT LOOP --- ## set CumPcnt cumulator and baseAngle parameter. ###################################################################### set CumPcnt 0 set baseAngle 90 ###################################################################### ## CREATE FULL PIE CIRCLE ON THE CANVAS. ## (De-activated! To avoid background problems when pie sections ## do not fill pie. But someone may want to do something like this, ## so this sample code is left here.) ###################################################################### ## NOTE: The 'oval' background shows through ## for any remaining, unused slice of the pie. ## (May have to use WHITE oval, to get white background ## in printouts.) ###################################################################### ## Use -outline "" so that no arc drawn. ## Let the 'create arc' commands draw the outlines. ###################################################################### # .fRplot.fRcanvas.c create oval \ # $pieSquare_x1Px $pieSquare_y1Px $pieSquare_x2Px $pieSquare_y2Px \ # -fill white \ # -outline "" \ # -tag TAGarcs # -fill $COLOR_PIEbkgd \ # -outline black \ # -width 2 \ ###################################################################### ## PLOT PIE SECTIONS --- loop through the %s --- ## according to number of Section %s and names. ###################################################################### for {set i 0} {$i < $Npcnts} {incr i} { ############################################################ ## SET COLOR for current slice. ############################################################ set colorIndex [ expr {$i % $numColors} ] set color [lindex $sectColors $colorIndex] ############################################################ ## SET start,extent ANGLES for current slice. ############################################################ set startAngle [ expr {$baseAngle + (3.60 * $CumPcnt)} ] set curPcnt [lindex $SectPcnts $i ] set extentAngle [ expr {3.60 * $curPcnt} ] ## For testing: # puts "******** CREATE SLICE ****************" # puts "i: $i" # puts "SectPcnt(i): [lindex $SectPcnts $i]" # puts "SectName(i): [lindex $SectNames $i]" # puts "CumPcnt: $CumPcnt" # puts "Start angle: $startAngle" # puts "Extent angle: $extentAngle" # puts "Section Color: $color" ############################################################ ## CREATE ARC for current slice. ############################################################ .fRplot.fRcanvas.c create arc \ $pieSquare_x1Px $pieSquare_y1Px $pieSquare_x2Px $pieSquare_y2Px \ -fill $color \ -start $startAngle \ -extent $extentAngle \ -tag TAGarcs ############################################################ ## CREATE TEXT LABEL (slice number) for current slice. ############################################################ set midAngle [ expr {$startAngle + ( $extentAngle / 2 )} ] set pi [expr {4*atan(1)}] set text_x [expr {$pieSquare_xmidPx + \ ($pieSquareRadiusPx / 2) * cos($midAngle * $pi / 180)} ] set text_y [expr {$pieSquare_ymidPx - \ ($pieSquareRadiusPx / 2) * sin($midAngle * $pi / 180)} ] ## For testing: # puts "******** CREATE SLICE LABEL ****************" # puts "i: $i" # puts "CumPcnt: $CumPcnt" # puts "Start angle: $startAngle" # puts "Extent angle: $extentAngle" # puts "Mid angle: $midAngle" # puts "text_x: $text_x" # puts "text_y: $text_y" # puts "pieSquare_x1 pieSquare_y1: $pieSquare_x1 $pieSquare_y1" # puts "pieSquare_x2 pieSquare_y2: $pieSquare_x2 $pieSquare_y2" # puts "pieSquare_xmid pieSquare_ymid: $pieSquare_xmid $pieSquare_ymid" .fRplot.fRcanvas.c create text \ $text_x $text_y \ -anchor center \ -fill black \ -text "[expr {$i + 1}]"\ -font fontTEMP_sectnum \ -tag {TAGmoveable TAGtitles} ############################################################ ## AUGMENT CumPcnt --- and check for over 100. ############################################################ set CumPcnt [ expr { $CumPcnt + $curPcnt } ] if { $CumPcnt > 100 } { set message "Percents exceed 100." tk_dialog .xxx "Input Err" $message warning 0 Close return } } ## END of FIRST "{set i 0} {$i < $Npcnts} {incr i}" loop. ############################################################# ## PUT LEGEND AT BOTTOM OF CANVAS --- loop through the percents ## --- and use the Section percents and names in the legend. ## (We do this in a separate loop to keep the legend-creating ## logic separate from the slice-creating logic.) ############################################################# ## Indent legend-text-items 5 pixels from left of canvas. set text_x 5 ## Set top of legend at (Npcnts + 1) chars above bottom of canvas. set CharHeightPx [font metrics fontTEMP_legend -linespace] set text_y [ expr {$CURcanHeightPx - (($Npcnts + 1) * $CharHeightPx)} ] ############################################################### ## START OF 2nd LOOP ON Npcnts, to make legend. ############################################################### for {set i 0} {$i < $Npcnts} {incr i} { ############################################################ ## SET COLOR FOR CURRENT LEGEND TEXT ITEM --- ## the same color we used for the slice. ############################################################ set colorIndex [expr {$i % $numColors} ] set color [lindex $sectColors $colorIndex] ## For testing: # puts "********* BOTTOM LEGEND ***********" # puts "i: $i" # puts "SectPcnt(i): [lindex $SectPcnts $i]" # puts "SectName(i): [lindex $SectNames $i]" # puts "Text Color: $color" ############################################################ ## SET Y-LOCATION OF CURRENT LEGEND TEXT ITEM. ## (Put each item down by the legend font height.) ############################################################ set text_y [ expr {$text_y + $CharHeightPx} ] ############################################################ ## CREATE A LEGEND TEXT ITEM. ############################################################ .fRplot.fRcanvas.c create text \ $text_x $text_y \ -anchor w \ -fill black \ -text "[expr {$i + 1}] - [ lindex $SectNames $i ] [ lindex $SectPcnts $i ]%"\ -font fontTEMP_legend \ -tag {TAGmoveable TAGtitles} # -fill $color } ## END of SECOND "{set i 0} {$i < $Npcnts} {incr i}" loop. } ## END of proc 'update_plot' ##+##################################################################### ## 'getset_bkgdcolor' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to get an RGB triplet (r255 g255 b255) ## via 3 RGB slider bars. ## ## Sets the canvas color and updates a color-label on the GUI. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttBkgdColor button ##+##################################################################### proc getset_bkgdcolor {} { global r255 g255 b255 # global feDIR_tkguis ## FOR TESTING: # puts "proc 'getset_bkgdcolor' starting." # puts "r255: $r255" # puts "g255: $g255" # puts "b255: $b255" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $r255 $g255 $b255] # $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \ ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB ## FOR TESTING: # puts "proc 'getset_bkgdcolor' finishing." # puts "TEMPrgb: $TEMPrgb" # puts "r255: $r255" # puts "g255: $g255" # puts "b255: $b255" ## Set the background (canvas) color. .fRplot.fRcanvas.c config -bg "#$hexRGB" .fRbuttons.lab2 config -text "#$hexRGB" } ## END OF 'getset_bkgdcolor' PROCEDURE ##+##################################################################### ## 'resize_win' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to UP/DOWN-size the Tk window by a ## given factor. ## ## Several methods could be used. ## For now, we query the current width and height (and location) ## of the window (with 'winfo' and 'wm') and apply the factor. ## ## Arguments: a size factor (can be less than 1 or greater than 1) ## ## CALLED BY: 'upsize_canvas' and 'downsize_canvas' procs ## ##+##################################################################### proc resize_win {factor} { ################################################################ ## This is not the exact window dimensions we want, but they ## are close enough. These do not include the window decoration. ################################################################ set winXlen [ winfo width . ] set winYlen [ winfo height . ] ################################################################ ## This is not the location we want. It is the upper-left of ## the window without the window decoration. Try again, with 'wm'. ## (We do not want the upper left corner of the window to ## move around as we resize the window.) ################################################################ # set winXloc [ winfo rootx . ] # set winYloc [ winfo rooty . ] ## Adjust the 'loc' vars for the window manager border. # set winXloc [ expr {$winXloc - 3} ] # set winYloc [ expr {$winYloc - 23} ] ########################################################## ## Get the true location of the upper left of the window. ########################################################## set WMgeom [wm geometry .] # set WMsize [lindex [split $WMgeom '+'] 0] set winXloc [lindex [split $WMgeom '+'] 1] set winYloc [lindex [split $WMgeom '+'] 2] ## FOR TESTING: # puts "proc 'resize_win' - Before:" # puts "winXlen : $winXlen" # puts "winYlen : $winYlen" # puts "winXloc : $winXloc" # puts "winYloc : $winYloc" ##################################### ## Resize the window size by 'factor'. ##################################### set winXlen [expr {int( $factor * $winXlen )} ] set winYlen [expr {int( $factor * $winYlen )} ] ############################################### ## Set the new size and location of the window. ############################################### ######################################################## ## Tried the following 'wm positionfrom . user' ## command to resize the window EVEN IF ## the user has maximized the window. ## DOES NOT SEEM TO WORK on Linux with X-windows ## (Ubuntu 9.10, Gnome 2.x desktop & window manager). ## Might work with some window managers. ######################################################## ## Ref: page 245 in Section 'Positioning Main Widget' in ## Chapter 7 'Dialog Windows' of the book ## Graphical Applications in Tcl & Tk (1st edition) ## by Eric F. Johnson. ######################################################## ## According to a Tk 8.5 reference manual, Tk sets ## 'user' mode, rather than 'program' mode, by default. ## So this was probably already set. It was worth a try. ######################################################## # wm positionfrom . user wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc} ## FOR TESTING: # puts "proc 'resize_win' - After:" # puts "winXlen : $winXlen" # puts "winYlen : $winYlen" # puts "winXloc : $winXloc" # puts "winYloc : $winYloc" } ## END OF 'resize_win' PROCEDURE ##+##################################################################### ## 'downsize_can' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to DOWN-size the canvas. ## ## We apply a factor to the current width and height of the canvas. ## ## Arguments: none other than global vars ## ## CALLED BY: .fRbuttons.buttDWNwin ## ## The user can keep clicking the button to downsize ~10% per click. ## ##+##################################################################### proc downsize_can {} { ################################################################ ## Downsize the window somewhat, so that the 'fill' pack-option ## of the canvas does not cause the canvas to keep the window ## from downsizing --- and/or so that the window surrounding the ## diminished canvas is not taking up too much screen space. ## (We seem to need to do this; especially if the user expands ## the window to max size with 'UpCan'.) ################################################################ ## FOR TESTING: # wm geometry . 640x480+100+100 if {1} { # resize_win 0.97 ## 0.97 seems to be too much. Legend may be clipped. ## But user can grab edge of window and drag to show all the text. resize_win 0.98 # resize_win 0.99 } ## END OF if {0}/{1} COMMENTABLE SECTION ############################################# ## Decrease the canvas size about 10% or 5%. ############################################# set CURcanWidthPx [winfo width .fRplot.fRcanvas.c] set CURcanHeightPx [winfo height .fRplot.fRcanvas.c] # set factor 0.40 set factor 0.90 # set factor 0.95 set NEWcanWidthPx [expr {int( $factor * $CURcanWidthPx )} ] set NEWcanHeightPx [expr {int( $factor * $CURcanHeightPx )} ] .fRplot.fRcanvas.c configure -width $NEWcanWidthPx -height $NEWcanHeightPx ##################################################### ## Re-pack the canvas frames, for the new canvas size. ##################################################### ## (We seem to need this section to down-size ## the canvas and window --- especially if the user ## expands the window to max size with 'UpCan'. ## A simple 'update' plus window downsize, above, ## does not seem to be enough.) ## (An 'if 1 or 0' clause is added here to facilitate ## experimentation with the presence or absence ## of this code.) ##################################################### if {1} { pack forget .fRplot.fRcanvas.c .fRplot.fRcanvas .fRplot pack .fRplot \ -side top \ -anchor center \ -fill both \ -expand 1 pack .fRplot.fRcanvas \ -side right \ -anchor center \ -fill both \ -expand 1 pack .fRplot.fRcanvas.c \ -side top \ -anchor nw \ -fill both \ -expand 1 } ## END OF if {0}/{1} COMMENTABLE SECTION ################################################################ ## Simply doing an 'update' to force the window to ## resize --- according to the current canvas size-request and ## the 'fill' pack-option for the canvas --- does not seem to ## suffice. We seem to need to do the re-pack. ################################################################ if {1} { update } ## END OF if {0}/{1} COMMENTABLE SECTION #################################################### # Re-do the plot, according to the new canvas size. #################################################### update_plot } ## END OF 'downsize_can' PROCEDURE ##+##################################################################### ## 'upsize_can' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to UP-size the canvas. ## ## We apply a factor to the current width and height of the canvas. ## ## Arguments: none other than global vars ## ## CALLED BY: .fRbuttons.buttUPwin ## ## The user can keep clicking the button to upsize ~10% per click. ## ##+##################################################################### proc upsize_can {} { ################################################################ ## Upsize the window, so that the enlarged canvas is ## pretty sure to show. (If the enlarged canvas is still clipped ## by the window size, the user can enlarge the window in the ## usual window-manager ways.) ################################################################ if {1} { resize_win 1.05 } ## END OF if {0}/{1} COMMENTABLE SECTION ############################################ ## Increase the canvas size about 10% or 5%. ############################################ set CURcanWidthPx [winfo width .fRplot.fRcanvas.c] set CURcanHeightPx [winfo height .fRplot.fRcanvas.c] # set factor 1.1 set factor 1.05 set NEWcanWidthPx [expr {int( $factor * $CURcanWidthPx )} ] set NEWcanHeightPx [expr {int( $factor * $CURcanHeightPx )} ] .fRplot.fRcanvas.c configure -width $NEWcanWidthPx -height $NEWcanHeightPx ##################################################### # Re-pack the canvas frames, for the new canvas size. ##################################################### ## COMMENTED this section. It appears that we do not need to re-pack, ## in this 'upsize_can' proc, although we may need to do this ## in the 'downsize_can' proc. The window up-size above seems to ## work to make the window-and-canvas upsize in essentially all ## scenarios. #################################################################### if {0} { pack forget .fRplot.fRcanvas.c .fRplot.fRcanvas .fRplot pack .fRplot \ -side top \ -anchor center \ -fill both \ -expand 1 pack .fRplot.fRcanvas \ -side right \ -anchor center \ -fill both \ -expand 1 pack .fRplot.fRcanvas.c \ -side top \ -anchor nw \ -fill both \ -expand 1 } ## END OF if {0}/{1} COMMENTABLE SECTION ################################################################ ## Simply doing an 'update' to force the window to ## size-up --- according to the current canvas size-request and ## the 'fill' pack-option for the canvas --- does not seem to ## suffice. But resizing the window, above, seems to work. ################################################################ ## BUT we do need this 'update' to expand the plot within ## the current canvas size, because this 'update' is needed ## so that 'update_plot' can get the true current canvas size. ################################################################ if {1} { update } ## END OF if {0}/{1} COMMENTABLE SECTION #################################################### # Re-do the plot, according to the new canvas size. #################################################### update_plot } ## END OF 'upsize_can' PROCEDURE ##+##################################################################### ## 'toggle_border' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to toggle a border on/off around the ## edge of the canvas. ## ## Arguments: none ## ## CALLED BY: button .fRbuttons.buttBorder ##+##################################################################### set borderOnOff OFF proc toggle_border {} { global borderOnOff set CURcanWidthPx [winfo width .fRplot.fRcanvas.c] set CURcanHeightPx [winfo height .fRplot.fRcanvas.c] if { "$borderOnOff" == "OFF" } { ################################################# ## Set border line-width (pixels) & line-color. ################################################# set bordWIDTH 2 # set bordCOLOR black set bordCOLOR #000000 ################################################# ## Set border limits. ################################################# set xbmin [expr { 0.005 * $CURcanWidthPx } ] set xbmax [expr { 0.995 * $CURcanWidthPx } ] set ybtop [expr { 0.005 * $CURcanHeightPx }] set ybbot [expr { 0.995 * $CURcanHeightPx }] ################################################# ## Set corner arc radius. ################################################# set minDim $CURcanWidthPx if { $minDim > $CURcanHeightPx } { set minDim $CURcanHeightPx } set brad [expr { 0.03 * $minDim } ] ################################################# ## Draw the four border lines (left,right,top,bot). ################################################# .fRplot.fRcanvas.c create line \ $xbmin [expr { $ybtop + $brad } ] \ $xbmin [expr { $ybbot - $brad } ] \ -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder .fRplot.fRcanvas.c create line \ $xbmax [expr { $ybtop + $brad } ] \ $xbmax [expr { $ybbot - $brad } ] \ -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder .fRplot.fRcanvas.c create line \ [expr { $xbmin + $brad } ] $ybtop \ [expr { $xbmax - $brad } ] $ybtop \ -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder .fRplot.fRcanvas.c create line \ [expr { $xbmin + $brad } ] $ybbot \ [expr { $xbmax - $brad } ] $ybbot \ -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder ################################################# ## Draw the four border arcs (UL,UR,LR,LL). ################################################# .fRplot.fRcanvas.c create arc \ $xbmin $ybtop \ [expr { $xbmin + 2*$brad } ] [expr { $ybtop + 2*$brad } ] \ -start 90 \ -extent 90 \ -style arc -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder .fRplot.fRcanvas.c create arc \ $xbmax $ybtop \ [expr { $xbmax - 2*$brad } ] [expr { $ybtop + 2*$brad } ] \ -start 0 \ -extent 90 \ -style arc -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder .fRplot.fRcanvas.c create arc \ $xbmax $ybbot \ [expr { $xbmax - 2*$brad } ] [expr { $ybbot - 2*$brad } ] \ -start 270 \ -extent 90 \ -style arc -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder .fRplot.fRcanvas.c create arc \ $xbmin $ybbot \ [expr { $xbmin + 2*$brad } ] [expr { $ybbot - 2*$brad } ] \ -start 180 \ -extent 90 \ -style arc -width $bordWIDTH -fill $bordCOLOR \ -tag TAGborder set borderOnOff ON } else { ## Restoring the whole plot with 'update_plot' is one way to ## remove the border --- but you lose changes like title/label moves. ## update_plot .fRplot.fRcanvas.c delete TAGborder set borderOnOff OFF } ## END OF if { "$borderOnOff" == "OFF" } } ## END of proc 'toggle_border' ##+######################################################################## ## 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 or to show msgs ##+######################################################################## ## 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. ##################################### text $toplevName.text \ -wrap none \ -font fontTEMP_varwidth \ -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" 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 ## 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 ##################################### ## 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' ##+##################################################################### ## 'print_plot' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to print the canvas image: .fRplot.fRcanvas.c ## ## Arguments: none ## ## CALLED BY: button .fRbuttons.buttPrint ##+##################################################################### proc print_plot {} { global env feDIR set tmp_filename "/tmp/$env(USER)_tmp_tkplot.ps" eval exec rm -f $tmp_filename .fRplot.fRcanvas.c postscript -file $tmp_filename \ -colormode gray \ -pageheight 10i \ -pagewidth 7i \ -pagex 1i \ -pagey 1i \ -pageanchor sw # -colormode color # -colormode gray # -colormode monochrome ## Use some external utility to print the postscript file. ## Examples: # set PRINTcmd "/usr/bin/kprinter" # set PRINTcmd "/usr/bin/hp-print" set fePRINTcmd "/usr/bin/cupsdoprint -H localhost:631 -P lp1" eval exec $PRINTcmd $tmp_filename # eval exec rm $tmp_filename } ## END of proc 'print_plot' ##+##################################################################### ## 'print_preview' PROCEDURE ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to preview the Postscript file of the ## canvas image: .fRplot.fRcanvas.c ## ## Arguments: none ## ## CALLED BY: button .fRbuttons.buttPrtPreview ##+##################################################################### proc print_preview {} { global env set tmp_filename "/tmp/$env(USER)_tmp_tkplot.ps" eval exec rm -f $tmp_filename .fRplot.fRcanvas.c postscript -file $tmp_filename \ -colormode color \ -pageheight 10i \ -pagewidth 7i \ -pagex 1i \ -pagey 1i \ -pageanchor sw # -colormode color # -colormode gray # -colormode monochrome ## Set an appropriate Postscript viewer app here. # set PSviewer "xpsview" set PSviewer "evince" eval exec "$PSviewer $tmp_filename &" # eval exec rm $tmp_filename } ## END of proc 'print_preview' ##+##################################################################### ## 'get_image' PROCEDURE ## (NOT IMPLEMENTED. Not tested. Code provided as ## a starting point to implement a get-image button.) ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked to put an image (ex: a logo or ## decorative image) on the plot canvas. ## ## Arguments: none ## ## CALLED BY: button .fRbuttons.buttImage ## ##+##################################################################### ## PRINTING NOTE: ## The image (logo) is not captured by the postscript Print button opts. ## But a screen image could be captured with a screen/window capture ## utility --- such as 'gnome-screenshot' on Linux --- for insertion in ## e-mails, web docs, or other docs. ## ## The captured image could be cropped with an image editor, such as ## 'mtpaint' on Linux. ## ## The captured-and-cropped image could be printed with an image ## view-print utility, such as 'eog' (Eye of Gnome) on Linux. ## ## DRAGABILITY NOTE: ## ## This image/logo is draggable, if bindings above and bind tags ## are implemented appropriately. ## ## If it is decided that the image is not wanted, it can be dragged ## off the canvas, out of sight. ##+##################################################################### ## Set a default directory from which to start the navigation ## to an image file. # set curDIR "$env(HOME)" set curDIR [pwd] proc get_image {} { global env curDIR img1 set CURcanWidthPx [winfo width .fRplot.fRcanvas.c] set CURcanHeightPx [winfo height .fRplot.fRcanvas.c] ## Get filename of a image file. set fName [tk_getOpenFile -parent . -title "Select image file (GIF/PNG) to load" \ -initialdir "$curDIR" ] ## FOR TESTING: # puts "fName : $fName" if {[file exists $fName]} { set endIDX [ expr {[string last "/" "$fName" ] - 1} ] set curDIR [ string range $strng 0 $endIDX ] # image create photo img1 -file "$fName" set img1 [image create photo -file "$fName"] ## FOR TESTING: # puts "get_image > img1: $img1" ## Put the southeast corner of the image near the ## lower-right corner of the canvas. set x [expr { 0.99 * $CURcanWidthPx } ] set y [expr { 0.99 * $CURcanHeightPx }] .fRplot.fRcanvas.c create image \ $x $y \ -anchor se \ -image $img1 \ -tag {TAGimage TAGmoveable} } } ## END of proc 'get_image' ##+###################################################### ## Additional GUI INITIALIZATION: ## ## Make a plot from the default entries. ##+###################################################### ##+#################################################### ## We can activate one of the following sets of initial ## plot settings by changing 'if {0}' to 'if {1}'. ##+#################################################### ## A generic plot example. if {0} { set titleMain " MAIN PLOT TITLE goes here." set SectPcnts "60 30 5" set SectNames "Large Medium Small" } ## END OF if {0} ## A budget statistics plot example. if {1} { set titleMain "President's proposed federal budget, sent to Congress Feb 2012" set SectPcnts "60 4 5 6 6 19" set SectNames "\ \"Military (DeptOfDefense, Wars, VetAffairs, NuclearWeaponsPrograms)\" \ \"DeptOfHomelandSec.\" \ \"StateDept\" \ \"HealthAndHumanServices\" \ \"Education\" \ \"Other (HUD, Ag, Justice, NASA, Energy excl. NukeWeapons, Labor, Interior, EPA, etc.)\"" } ## END OF if {0} ##+########################################################### ## Initialize the plot. ## ('update' is needed to pack the canvas before calling ## 'update_plot', which uses the canvas size to do the plot.) ##+########################################################### update update_plot ## Some wiki.tcl.tk person's alternative: # bind .fRplot.fRcanvas.c <Map> update_plot ## ## (Why? --- They did not document.) ## ## This will probably cause double re-draws whenever ## the 'downsize_can' or 'upsize_can' proc runs. ## ## (If you make changes to this code, please add ## name/nickname, date, reason for the change. ## If there are major changes, please start ## another script on another wiki page.)
So here is the first of 5 'PlotQuik' utilities to be converted to stand-alone mode from their 'integrated' form in the 'feHandyTools' system of the Freedom Environment software.
Other Tk plot scripts to follow:
2) bar chart, created from data entered on the GUI
3) math expression xy-plot, created from an expression entered on the GUI
4) points and/or lines xy-plot, created from data entered on the GUI
5) xy-plot, from a text file of data-columns (2 or 3 columns can be selected for x-y plots or x-y1-y2 plots)
uniquename 2013jul23 UPDATE
I have replaced the code above with slightly updated code. The main changes follow.
1) I put most of the text strings for buttons and labels in an array, aRtext, near the top of the code. This should make internationalization of this script much easier.
2) I made some 'esoteric' changes to the 'resize_win', 'downsize_can', and 'upsize_can' procs --- in an attempt to get the 'DwnCan' button to work in the case that the user has maximized the GUI window VIA THE WINDOW MANAGER of their operating system. I was unsuccessful in this. 'wm geometry' was not up to the task. It may be that most window managers will not honor requests from a program (instead of the user) that would effectively un-maximize a maximized window. The user can simply click on the 'Restore' button of the window. Then 'DwnCan' should work again. If the user maximizes the GUI window via the 'UpCan' button, 'DwnCan' works as intended.
3) I reduced the font of the Help message, on the left of the plot canvas, so that the text is unlikely to be clipped on netbook screens (600 pixels high).
4) I re-ordered the procs --- mainly to put the 'print_plot', 'print_preview, and 'get_image' procs at the bottom of the script. I put sample (untested but plausible) code for those procs in the script in case some users would like to be able to make a Postscript file from the plot image --- or in case some users would like to add an image to the plot (such as a logo).
5) I changed the names of the 2 procs that do the dragging of the main title, pie-section labels, and legend lines. The procs were 'plotDown' and 'plotMove'. They are now 'itemSelect' and 'itemMove' --- for a clearer indication of their function.
6) I added the suffix 'Px' on the end of some variable names --- so that it is clear (without scanning the code) that these variables are intended to be integer variables (denoting pixels), rather than floating-point variables (denoting 'world coordinates'). I also started all tag names with the capitalized string 'TAG', so that it is clear which names are tag-names.
7) I swept through the comments trying to bring them up-to-date and make them more accurate and more clearly worded. However, no matter how many times I sweep through this many lines of code-with-comments, I find discrepancies, obfuscations, and cases of 'brain fog'. My apologies for any such remnants in the comments.
If I have occasion to use this utility some day and find that it would be nice to implement the 'get_image' proc, then I may update this page with tested 'get_image' code and with a 'GetImage' button added to the GUI.
I may also implement a 'Help' button on the GUI to provide more extensive help --- for example, to explain that the 'DwnCan' button may not work if the user has maximized the GUI window via the user's window manager.