#!/usr/bin/wish -f ## ## SCRIPT: makeRectangle_withRoundedCorners_solidColors.tk ## ## PURPOSE: This Tk GUI script facilitates the creation of ## rectangular solid-color images with rounded corners, ## on a canvas widget. ## ## The image consists of two colors - one color inside the ## rectangle (with round corners) and a different color ## outside the rounded rectangle. ## ## Such images can be useful in creating rounded wide buttons ## for 'toolchest' GUI's, for example. ## ## METHOD: The GUI contains a rectangular canvas widget on which the ## solid-color rectangle with rounded corners is drawn. ## ## The GUI includes a 'scale' widget whose slider-bar can ## be used to change the radius of the corners dynamically. ## That is, the 4 corners change radius as the slider-bar ## is dragged in either direction. ## ## The GUI also includes the capability to set the 2 colors ## of the image. (Any number of methods could be used to ## set the two colors: two entry fields OR 2 buttons which ## call on an external script to allow the user to set each ## color via a color-selector-GUI. We use the latter.) ## ## (A set of 6 spinboxes or 6 scales could be put on the GUI ## to set the RGB values of the 2 colors --- but those widgets ## take a lot of space on the GUI.) ## ## There is a '-command' parameter on the radius-setting 'scale' ## widget. That parameter is used to call a 'DrawRoundRect' proc ## to redraw the solid-colored rectangle with round corners --- ## as the sliderbar is used to change the value of a radius variable. ## ## The redraw includes redrawing 4 ovals (circular disks) and ## 2 rectangles on the canvas for each detected change in radius. ## ## Since erasing these items from the canvas and redrawing them ## completes within a very small fraction of a second, it is ## feasible to do the redraws 'dynamically' with the sliderbar. ## ## Currently the 'DrawRoundRect' proc works as follows. ## The rounded rectangle is centered in the canvas. ## The rectangle dimensions are set to a fixed percent of ## the dimensions of the canvas. Furthermore ... ## ## The '-fill' and '-expand' pack parameters are set so that ## the canvas expands/contracts as the window is increased or ## decreased in size. Hence the size (and aspect ratio) of the ## rounded rectangle is controlled by resizing the window. ## ## USING THE GENERATED IMAGE: ## A screen/window capture utility (like 'gnome-screenshot' ## on Linux) can be used to capture the GUI image in a GIF file, say. ## ## If necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image to get only the ## rectangular area of the image, with the outer-color only ## at the rounded corners --- or with a border of the outer-color ## all around the rectangle. ## ## Several 'use cases' for the captured-cropped image file: ## ## 1) The 2-color image could be used, directly, for the background of ## 'buttons'/'drawers' in GUIs such as 'toolchests'. ## ## 2) The 2-color image file could be used with a utility (like the ## ImageMagick 'convert' command) to change the outer color ## to transparent, making a partially transparent GIF ## (or PNG) file --- with the rounded corners being transparent. ## Then the SEMI-TRANSPARENT, SOLID-COLOR image file could be used ## for the background of 'buttons'/'drawers' in GUI's --- ## such as 'toolchests'. ## ## 3) The semi-transparent, solid-color image file from use-case 2 ## could be used as a *MASK* on a NON-SOLID-COLOR image file of ## the same size. ## A utility (like the ImageMagick 'convert' or 'composite' command) ## could be used to 'apply' the mask to the NON-solid-color image ## file, making a rounded-corner semi-transparent image file ## (GIF or PNG) from the NON-solid-color image. ## Then the SEMI-TRANSPARENT, NON-SOLID-COLOR image file could be ## used, for the background of 'buttons'/'drawers' in GUIs such as ## 'toolchests'. ## ## 4) For a variety of toolchests, the toolchests will generally ## require a DIFFERENT WIDTH button/drawer FOR EACH TOOLCHEST. ## In that case, the semi-transparent, NON-solid-color image file ## from use-case 3 could be STRETCHED-OUT (by any one of several means) ## to make a semi-transparent, NON-solid-color image of the required ## width for each toolchest. ## ## REFERENCE: ## http://wiki.tcl.tk/1416 - 'Drawing rounded rectangles' ## (downloaded 14aug 2012) ## Author: ??????? (and GNJ for alternate proc 'roundRect2') ## The original 'roundRect' proc approximated the rounded corners ## with polugons with small sides. The 'roundRect2' of GNJ draws ## the corners with 'create oval' commands on the Tk canvas widget, ## thus yielding higher-quality rounded corners. ## ##+###################################################################### ## 'CANONICAL' STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name,win-position,win-size-control, ## win-color-scheme,fonts, widget-geometry-parms,etc.). ## 1) Define ALL frames (and sub-frames). Pack them. ## 2) Define all widgets in the frames. Pack them. ## ## 3) Define keyboard or mouse action BINDINGS, if needed. ## 4) Define PROCS, if needed. ## 5) Additional GUI INITIALIZATION (with procs), if needed. ## ## ## Some detail of the code structure for this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : '.fRbuttons' and '.fRcan' ## ## Sub-frames: none ## ## 1b) Pack ALL frames. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRbuttons': 1 button widget ('Exit') and ## 2 buttons (for setting the 2 colors), and ## 1 scale widget (with label widget) ## ## - In '.fRcan': one 'canvas' widget ## ## 3) Define bindings: none ## ## 4) Define procs: ## - 'DrawRoundRect' invoked when the scale widget sliderbar moves ## ## Variables calculated from current window size: ## X x-location of upper left corner of rectangle ## Y y-location of upper left corner of rectangle ## W width of the rectangle ## H height of the rectangle ## The rectangle is centered in the current window ## such that it occupies about 80% of the window. ## ## Global variables: ## curRADIUS radius of the 4 corners, from the scale widget ## CINhex color inside the rectangle ## COUThex color outside the rectangle ## ## - 'set_color_inside' shows a color selector GUI and uses the user-selected ## color to redraw the ovals & rectangles on the canvas ## in the specified color ## ## - 'set_color_inside' shows a color selector GUI and uses the user-selected ## color to reset the color of the canvas ## ## 5) Additional GUI initialization: Execute 'DrawRoundRect' once with ## an initial, example set of parms ## --- curRADIUS CINhex COUThex --- to start ## with a rounded rectangle on the canvas ## rather than a blank 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 on Ubuntu 9.10 ## after Tcl-Tk 8.4 was replaced by 8.5. ##+####################################################################### ## MAINTENANCE HISTORY: ## Created by: Blaise Montandon 2012aug14 ## Changed by: ...... ......... 2012 ##+####################################################################### ##+####################################################################### ## Set general window parms (title,position,size,color-scheme,fonts,etc.). ##+####################################################################### wm title . "Rectangle with Round Corners on a Canvas" wm iconname . "RoundRect" wm geometry . +15+30 ## 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. The user can move the sliderbar of the scale widget ## to re-draw the color-filled rectangle on the canvas. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+###################################################### ## Set the color scheme for the window and its widgets --- ## radiobuttons and spinboxes. ##+###################################################### tk_setPalette "#e0e0e0" # set entryBKGD "#f0f0f0" ## Initialize the 'inside' and 'outside' colors for the canvas. set CINr 255 set CINg 255 set CINb 255 set CINhex [format "#%02X%02X%02X" $CINr $CINg $CINb] set COUTr 0 set COUTg 0 set COUTb 0 set COUThex [format "#%02X%02X%02X" $COUTr $COUTg $COUTb] ##+######################################################## ## Use a variable-width font for label and button widgets ## --- and for scale labels. ## ## Use a fixed-width font for entry fields, if any. ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -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 ## 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) ##+########################################################### set initCanWidthPx 400 set initCanHeightPx 300 set minCanHeightPx 24 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## BUTTON (and LABEL) geom parameters: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## SCALE geom parameters: set BDwidthPx_scale 2 set initScaleLengthPx 200 ##+###################################################### ## Set a minsize of the window. ## For width, allow for about 3 buttons (Exit,Color1,Color2) ## and scale label (Radius:) and length of slider bar. ## For height, allow for a canvas at least 24 pixels high ## and for the scale widget height below the ## canvas. ##+###################################################### set minWinWidthPx [font measure fontTEMP_fixedwidth \ "ExitInsideOutsideRadius"] ## Add some pixels to account for right-left-size window decoration ## (about 8 pixels), about 4x8 pixels for borders/padding for ## 4 buttons/labels, and about 200 pixels for the scale sliderbar. set minWinWidthPx [expr 240 + $minWinWidthPx] set minCharHeightPx [font metrics fontTEMP_fixedwidth -linespace] ## Allow 2 characters high for the scale widget, with its label area above ## the sliderbar, and about 28 pixels for top-bottom window decoration, ## about 8 pixels for frame borders and widget (button/label/scale) borders. ## And add in the min-canvas-height. set minWinHeightPx [expr $minCanHeightPx + 36 + 2 * $minCharHeightPx] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : 'fRcan' , 'fRbuttons' ## ## Sub-frames: none ##+################################################################ # set BDwidth_frame 0 set BDwidth_frame 2 # set RELIEF_frame raised set RELIEF_frame flat frame .fRbuttons -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRcan -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the 2 top-level FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcan \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+################################################################ ## IN THE '.fRbuttons' frame -- DEFINE the 'Exit' button --- also ## 2 radiobuttons (with a label button). ##+################################################################ button .fRbuttons.buttEXIT \ -text "Exit" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttCIN \ -text "\ Inside Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_color_inside} button .fRbuttons.buttCOUT \ -text "\ Outside Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_color_outside} ## Define label for the scale: label .fRbuttons.labelSCALE \ -text "\ Radius (pixels)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button ## Set the init value for the scale var. set curRADIUS 25 # set scaleMaxUnits 200 # update # set scaleMaxUnits [expr [winfo height .fRcan.can] / 2] # set scaleMaxUnits [expr $initCanHeightPx / 2] set scaleMaxUnits $initCanHeightPx ## FOR TESTING: # puts "scaleMaxUnits: $scaleMaxUnits" scale .fRbuttons.scale \ -orient horizontal \ -digits 0 \ -from 0 -to $scaleMaxUnits \ -length $initScaleLengthPx \ -variable curRADIUS \ -command "DrawRoundRect .fRcan.can" ##+########################################### ## Pack the widgets in the 'fRbuttons1' frame. ##+########################################### pack .fRbuttons.buttEXIT \ .fRbuttons.buttCIN \ .fRbuttons.buttCOUT \ .fRbuttons.labelSCALE \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbuttons.scale \ -side left \ -anchor w \ -fill x \ -expand 1 ##+############################### ## DEFINE-and-PACK CANVAS WIDGET: ##+############################### canvas .fRcan.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief raised \ -borderwidth $BDwidthPx_canvas pack .fRcan.can \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+################################# ## END OF THE DEFINITION OF THE GUI ## --- frames and widgets. ##+################################# ##+################################# ## BINDINGS SECTION: none ##+################################# ##+############################################################### ## PROCS SECTION: three procs ## - DrawRoundRect - called by '-command' of the scale widget ## - set_color_inside - called by '-command' of a button widget ## - set_color_outside - called by '-command' of a button widget ##+############################################################### ##+##################################################################### ## proc DrawRoundRect: ## ## Draws a rounded rectangle in a canvas. ## ## Arguments: ## w - Path name of the canvas ## ## Global parms: ## ## curRADIUS - radius of the bend at the corners, in any form ## acceptable to Tk_GetPixels ## --- the variable for the scale widget ## ## tag - a tag for all the elements put on the canvas ## ## Calculated within this proc: ## ## X, Y - Co-ordinates of the upper left corner of the rectangle, in pixels ## W, H - width and height of the rectangle, in pixels ## ## X,Y,W,H are based on the current size of the canvas, based on: ## centering the rectangle in the canvas and ## making the width & height of the rectangle about 3/4 the ## width & height of the canvas. ## ## Based on the 'roundRect2' proc by GNJ = ? ## - Changed to calc X,Y,W,H based on the current size of the canvas. ## - Fixed some offset calcs for the ovals and rectangles ## to fix a slightly off-center problem. ##+##################################################################### ## We set a factor used to size the rectangle within the canvas. # set rectFactor 0.8 set rectFactor 0.6 ## We put the name of the tag in a variable rather than ## hard-coding the tag name on the 'create oval' and ## 'create rectangle' statements. set geomTAG canElement proc DrawRoundRect {w rad} { ## NOTE: The '-command' parm of the 'scale' widget passes ## the current value of the scale to the proc it calls. ## We need the 'rad' argument as a place holder to ## avoid a syntax error. ## ## We hold the current value of the scale in the global ## var 'curRADIUS', and use that var in the ## 'set_color_inside' proc as well as in this proc. global curRADIUS geomTAG rectFactor CINhex COUThex set canWidth [winfo width $w] set canHeight [winfo height $w] set W [expr $canWidth * $rectFactor] set H [expr $canHeight * $rectFactor] set X [expr ( $canWidth - $W ) / 2 ] set Y [expr ( $canHeight - $H ) / 2 ] set diam [expr 2 * $curRADIUS] ## Delete all the tagged items on the canvas. $w delete $geomTAG ## Create the upper-left round corner - a disk $w create oval \ $X $Y \ [expr $X + $diam] [expr $Y + $diam] \ -fill $CINhex -outline $CINhex -tag $geomTAG ## Create the upper-right round corner - a disk $w create oval \ [expr ($X + $W) - $diam] $Y \ [expr $X + $W] [expr $Y + $diam] \ -fill $CINhex -outline $CINhex -tag $geomTAG ## Create the lower-left round corner - a disk $w create \ oval $X [expr ($Y + $H) - $diam] \ [expr $X + $diam] [expr $Y + $H] \ -fill $CINhex -outline $CINhex -tag $geomTAG ## Create the lower-right round corner - a disk $w create oval \ [expr $X + $W - $diam] [expr ($Y + $H) - $diam] \ [expr $X + $W] [expr $Y + $H] \ -fill $CINhex -outline $CINhex -tag $geomTAG ## Create the vertical-strip rectangle - up the middle of the interior $w create rectangle \ [expr $X + $curRADIUS] $Y \ [expr $X + $W - $curRADIUS] [expr $Y + $H] \ -fill $CINhex -outline $CINhex -tag $geomTAG ## Create the horizontal-strip rectangle - across the middle of the interior $w create rectangle \ $X [expr $Y + $curRADIUS] \ [expr $X + $W] [expr $Y + $H - $curRADIUS] \ -fill $CINhex -outline $CINhex -tag $geomTAG } ## END OF proc DrawRoundRect ##+##################################################################### ## proc 'set_color_inside' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of all the tagged items ## (ovals and rectangles) on the canvas. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCIN button ##+##################################################################### proc set_color_inside {} { global CINr CINg CINb CINhex curRADIUS # global feDIR_tkguis ## FOR TESTING: # puts "CINr: $CINr" # puts "CINg: $CINg" # puts "CINb: $CINb" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $CINr $CINg $CINb] # $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 "TEMPrgb: $TEMPrgb" set CINhex "#$hexRGB" ## Call proc roundRect to redraw the geometry in the new interior color. DrawRoundRect .fRcan.can $curRADIUS } ## END OF proc 'set_color_inside' ##+##################################################################### ## proc 'set_color_outside' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the canvas --- ## on which all the tagged items (ovals and rectangles) lie. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCOUT button ##+##################################################################### proc set_color_outside {} { global COUTr COUTg COUTb COUThex # global feDIR_tkguis ## FOR TESTING: # puts "COUTr: $COUTr" # puts "COUTg: $COUTb" # puts "COUTb: $COUTb" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $COUTr $COUTg $COUTb] # $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 "TEMPrgb: $TEMPrgb" set COUThex "#$hexRGB" ## Set the color of the canvas. .fRcan.can config -bg $COUThex } ## END OF proc 'set_color_outside' ##+##################################################### ## Additional GUI initialization, if needed (or wanted). ##+##################################################### DrawRoundRect .fRcan.can $curRADIUS .fRcan.can config -bg $COUThex