[uniquename] - 2012nov13 I published a nice, simple RGB color-selector Tk GUI script at [A non-obfuscated color selector GUI]. I have used that color selector repeatedly as an aid in various utilities published here, such as * [A color-gradient-button-maker GUI using a separate color-selector script] * [GUI for Drawing 'Super-ellipses', with nice shaded edges] * [A GUI for making 'Title Blocks' ... with text, fonts, colors, images] But I have been thinking over the past couple of months about how the 'complementary' (a.k.a. 'subtractive') colors Cyan-Magenta-Yellow should be just as capable of presenting 256 x 256 x 256 = 16,777,216 colors as the 'primary' (a.k.a. 'additive') colors, Red-Green-Blue. However, I was having a hard time devising a set of 3 conversion equations for converting CMY to RGB or vice versa. I wanted to use values for CMY in the range 0 to 255, like is typically done with RGB. I know that people (in the printing profession, I guess) typically specify CMY values as fractional real numbers in the range 0 to 1, rather than using a range of integers. I suppose this is because they do not want to be limited to 'only' 256 color levels for each of C, M, and Y. I can appreciate the desire for freedom. It must be very liberating to use real numbers for CMY rather than integers. But come on, guys (and gals). Wouldn't 256 x 256 = 65,536 shades of Cyan or Magenta or Yellow be enough. After all, that is a total of 65536 x 65536 x 65536 colors. Isn't 281,474,976,710,656 (281 trillion) colors enough? Well, I think I can do quite nicely with 256 x 256 x 256 = 16,777,216 colors, so I have been trying to think of a way to do the CMY to-from RGB mapping. I thought "How do I get a pure RGB color like Blue from CMY?" Cyan(=blue+green) and Magenta(=blue+red) could be combined to give blue, but somehow I would have to subtract out the red and green contributions of those 2 CMY colors to get pure Blue. I thought, in order to get Blue=RGB(0,0,255), if I use both Cyan and Magenta to get blue, I would need to put them in a scale that went to about 127 or 128 max. But then I got into questions of which should it be 127 or 128? And what should the lower range be? Should the minimum be -128? Or -127? And how do I use yellow to subtract out the extra red plus green? Or maybe the range should be -255 to 0. But I still could not figure a way to map CMY to RGB, and back. I was pretty much stymied. Then I saw an article that pointed out these three simple equations for converting from RGB to CMY: yellow = 1 - blue magenta = 1 - green cyan = 1 - red That was it! It may not relate to the 'real world', but it looked like a workable technique. All I needed to do was scale things up to 255: yellow = 255 - blue magenta = 255 - green cyan = 255 - red That's for converting RGB to CMY. And simply rearrange for CMY-to-RGB: blue = 255 - yellow green = 255 - magenta red = 255 - cyan So then I was equipped to convert my RGB-255 color selector at [A non-obfuscated color selector GUI] to a CMY-255 color selector. And I ended up with the following GUI --- with code below. [CMY255colorSelectorGUI_screenshot_732x202.jpg] I don't know how useful this might be. I tend to think in terms of RGB nowadays. But maybe there are people (printers? painters?) who think in terms of CMY. Maybe CMY is as natural to them as RGB is to me. So maybe this has some usefulness. Maybe not. But at least it can get one's thinking out of a rut. For example, on this GUI, slide the three scales to (255,255,255) and the color swatch is black. And, sure enough, slide to (0,0,0) and the color swatch is white. Of course, it is what you would expect if you know the equations at work behind the scenes. But even then ... it turns my color world upside down. ___ Actually, the translation formulas above correspond pretty well to the 'real world'. The CMY colors are said to be 'subtractive' colors. For example, yellow is yellow because it 'sucks' the blue frequency out of incident light, reflecting back red and green frequencies, yielding yellow. So it makes quite good sense that yellow is found by subtracting blue: yellow = 1 - blue or yellow = 255 - blue. In fact, my thought at one point of using a range from -255 to 0 might even make more sense. In that case, the transform equations could simply be yellow = - blue magenta = - green cyan = - red In fact, these equations are related to the ones above by simply shifting the latter numbers 255 units to the right on the real (or integer) number scale. So instead of making the 3 scales on the GUI go from 0 to 255, it would probably be just as valid to make the scales go from -255 to 0. The colors on the color swatch would still behave the same. So it appears these mind-bending formulas for converting between CMY and RGB are compatible with the 'real world'. The CMY colors are subtractive both in a physical and a mathematical sense. We get the CMY colors by subtracting the RGB colors. The math here is compatible with what is happening with paints and printer's ink. ---- '''The code''' I provide the code for this GUI below. 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, 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 in the frames, frame by frame. 3) Define keyboard and 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 Tk script, and when looking for code snippets to include in Tk scripts (code re-use). One new thing that I have started doing recently is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future. ------ '''Experimenting with the GUI''' As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on all of the 'pack' commands for the frames and widgets. That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window. For this particular GUI, I have used the statement ====== wm resizeable . 0 0 ====== to make the window (and its widgets) a fixed size --- since I have found no advantage in having the color swatch change size --- and no advantage to having the scale and button widgets change size, or position. However, if anyone sees an advantage to allowing the GUI to change size, they can comment the 'wm resizeable' statement --- and then 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. ___ In addition, you might want to change the fonts used for the various GUI widgets. 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. I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well. ___ That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing. Most of the code that is involved in doing the CMY/RGB color translations and 'dynamically' updating the color swatch and the CMY-and-RGB labels is in the proc 'color_update'. It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this. Without the comments, it might not be clear why certain approaches are taken --- for example, certain 'packing' strategies. Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch AFHV (Antarctica's Funniest Home Videos). ====== #!/usr/bin/wish -f ## ##+####################################################################### ## NOTE: ## If the 'wish' interpreter is in another directory, like ## /usr/local/bin, you, as root, can make a soft-link from 'wish' there ## to /usr/bin/wish --- with a command like ## ln -s /usr/bin/local/wish /usr/bin/wish ## The form of this command: ## ln -s ##+####################################################################### ## Tk SCRIPT NAME: CMY255_color_selector.tk ## ##+####################################################################### ## PURPOSE: This TkGUI script provides a GUI for selecting 'digitized' ## Cyan-Magenta-Yellow values, from 0 thru 255, via ## slider-bars/buttons on 3 Tk 'scale' widgets. ## ## For any slider-bar change, this GUI script ## immediately shows the color as the background color of a ## frame widget (a 'color swatch') in a corner of the GUI ## window (covering about 20% of the window). ## ## Also the GUI shows the current color's hex (and per-cent) ## CMY and RGB values in two small text widgets about 12 ## characters long. ## ## This script is based on a similar script for showing ## and choosing colors for given RGB-255 values. That script ## is the real 'workhorse'. This script is mainly a demo ## that shows that 'digital' screen colors can be specified ## via the 'complementary' CMY colors, as well as via the ## 'primary' RGB colors. ## ## For someone who thinks in CMY rather than RGB, it might ## have the following uses. ## ## SOME USES (perhaps by a CMY fan --- someone who thinks in terms of ## 'complementary' colors rather than 'primary' colors --- someone who ## thinks in terms of 'subtractive' colors rather than 'additive' colors ## --- someone who thinks in terms of CMY rather than RGB --- ## perhaps painters and printers): ## ## 1) Useful in determining a CMY color specification in integer (0-255) ## or percents (or hex values?) to MATCH A GIVEN COLOR. (The user ## can drag this GUI to a portion of the screen, and use the ## slider-bars to find color matches. Similarly, bring an ## image viewer window next to the GUI.) ## ## 2) Also may be useful in DETERMINING SUITABLE BACKGROUND COLORS ## for black/white text --- in CMY format. ## ## 3) IN A SHELL SCRIPT OR ANOTHER TK SCRIPT, this Tk script can ## ACT AS A COLOR SELECTOR by passing the current color to stdout ## (in RGB 0-255-integer and hex), when the OK/UseIt button is ## clicked. Example output string: 0 156 255 009CFF ## ##+####################################################################### ## SOURCE (and CREDIT-TO): ## This script is based on a color-chooser Tk script for the ## FE (Freedom Environment) software systems (www.freedomenv.com). ## ## That script was based on an example from Australia (circa 1999) at ## http://www.sci.usq.edu.au/~devoil/66309/tut2.html ## (Author's name unknown. The script may have been based on the Tk ## color selector GUI that was announced by Ousterhout around 1996. ## It may have been intended as a simplification of that GUI.) ## ##+##################################################################### ## INPUTS: The user slides the slider bars. Initial CMY values can be ## set as described in the next section, 'CALL FORMAT'. ## ## OUTPUT: The color-swatch and hex-and-percent CMY and RGB values ## displayed on the Tk window. ## ## Also the last CMY slider settings are passed, after conversion ## to RGB-255 values, to stdout as a string giving integer (0-255) ## and hex values of the RGB-color. ## ## Sample output string: 0 156 255 009CFF ## ## We pass RGB colors rather than CMY because, for most computer ## applications such as web pages and Tk scripts and graphics ## programming, colors need to be specified as RGB values. ## ##+##################################################################### ## CALL FORMAT: ## ## $FEDIR_TKGUIS/CMY255_color_selector.tk [rc255 m255 y255] ## ## where c255 m255 y255 represent integers between 0 and 255, ## and the values are ignored if there are not exactly three. ## If no values are entered, the defaults are 0 156 255 or ## some such triplet. ## ## OR ## ## use env vars C255, M255, Y255 to initialize the sliders. ## ----------------------------------------------------------------------- ## EXAMPLE CALLS in a shell script: ## (Could also be called in a tcl-tk script.) ## ## 1) C255=0 ## M255=156 ## Y255=255 ## export C255 M255 Y2555 ## ## TEMP=`$FEDIR_TKGUIS/sho_colorvals_via_sliders3rgb.tk` ## OR ## 2) ## TEMP=`$FEDIR_TKGUIS/sho_colorvals_via_sliders3rgb.tk 80 156 255` ## or ## TEMP=`$FEDIR_TKGUIS/sho_colorvals_via_sliders3rgb.tk` ## ## ## (Note: The values in TEMP can be extracted with a command like ## 'cut' or 'awk'.) ##+######################################################################## ## STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name,win-position,color-scheme, ## fonts, widget-geom-parms,win-size-control). ## 1a) Define ALL frames (and sub-frames). ## 1b) Pack ALL the frames and sub-frames. ## 2) Define & pack all widgets in the frames. ## ## 3) Define key and mouse/touchpad/touch-sensitive-screen action ## BINDINGS, if needed. ## 4) Define PROCS, if needed. ## 5) Additional GUI INITIALIZATION (typically with one or two ## procs in section 4), if needed. ## ## In more detail for this particular script: ## ## 1a) Define ALL frames: ## Top-level : '.fRtop' , '.fRbottom' (over-under) ## Sub-frames: '.fRtop.fRsliders' , '.fRtop.fRpreview' (left-right) ## Sub-sub-frames: ## '.fRtop.fRsliders.fRcyan', ## '.fRtop.fRsliders.fRmagenta' , ## '.fRtop.fRsliders.fRyellow' (over-under) ## ## 'fRsliders' and 'fRpreview' are to be on the top of the GUI, ## on the left and right, respectively. ## 'fRbottom' is to be on the bottom of the GUI. ## ## 1b) Pack ALL frames. ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in top-to-bottom and/or left-to-right order: ## ## - '.fRtop.fRsliders' (to contain 3 sliderbars) ## ## - '.fRtop.fRpreview' (to contain only its background color; ## to display the color specified by the ## sliderbar settings) ## ## - 'fRbottom' (to contain several buttons and label & text widgets ## to display the current % [0 to 100] and hex [ 00 ## to FF ] CMY and RGB values of the color.) ## ## 3) Define BINDINGS: none currently ## ## 4) Define PROCS: ## - 'color_update' - called by '-command' options on 3 CMY ## scales --- to update the color swatch ## and the display hex & percent values ## - 'toggle_side' - called by ToggleSide button ## - 'put_vars' - called by UseIt button ## - 'popup_msg_var_scroll' - called by Help button ## ## 5) Additional GUI INITIALIZATION: none, except for setting ## the HELPtext var for the Help button. ## ##+####################################################################### ## DEVELOPED WITH: Tcl-Tk 8.5 on Ubuntu 9.10 (2009 october, 'Karmic Koala) ## --- in 2012. ## ## wish> puts "$tcl_version $tk_version" ## showed ## 8.5 8.5 ##+######################################################################## ## FE system Copyright 2012+ by Blaise Montandon ##+######################################################################## ## Created by: Blaise Montandon 2012nov13 Started version for the FE ## 'tkGooies' system, on Linux, ## using Ubuntu 9.10 (2009oct version). ## Changed by: ...... ......... 2012nov ##+############################################################################ ##+################################# ## SET THE TOP WINDOW NAME. ##+################################# wm title . \ "CMY Colors - Integer (0-255) and PerCent and Hex" wm iconname . "CMYcolorSel" # catch { wm title . "$env(FE_WIN_TITLE)" } # catch { wm iconname . "$env(FE_ICON_TITLE)" } ##+################################### ## SET THE TOP WINDOW POSITION. ##+################################### wm geometry . +50+50 # catch {eval wm geometry . "$env(FE_COLORSEL_GEOM)" } ##+####################################################################### ## SET COLOR SCHEME (palette) FOR THE WINDOW. ##+####################################################################### ## Gray palette set r255pal 210 set g255pal 210 set b255pal 210 set COLOR_pal [format "#%02X%02X%02X" $r255pal $g255pal $b255pal] tk_setPalette $COLOR_pal ##+####################################################################### ## SET FONT VARS to use in the 'font create' statements below. ##+####################################################################### set FONTsize 14 set FONT_SMALLsize 12 ## For variable width: set FONT_varwidth \ " -family {comic sans ms} -size -$FONTsize -weight bold -slant roman " set FONT_SMALL_varwidth \ " -family {comic sans ms} -size -$FONT_SMALLsize -weight normal -slant roman " ## For fixed width: set FONT_fixedwidth \ " -family {dejavu sans mono} -size -$FONTsize -weight bold -slant roman " set FONT_SMALL_fixedwidth \ " -family {dejavu sans mono} -size -$FONT_SMALLsize -weight normal -slant roman " ##+##################################################################### ## DEFINE (temporary) 'font create' NAMES to be used ## in '-font' widget specs below. ##+##################################################################### eval font create fontTEMP_button $FONT_varwidth eval font create fontTEMP_label $FONT_varwidth # eval font create fontTEMP_entry $FONT_fixedwidth # eval font create fontTEMP_listbox $FONT_fixedwidth # eval font create fontTEMP_msg $FONT_fixedwidth eval font create fontTEMP_text $FONT_fixedwidth # eval font create fontTEMP_SMALL_button $FONT_SMALL_varwidth # eval font create fontTEMP_SMALL_label $FONT_SMALL_varwidth # eval font create fontTEMP_SMALL_entry $FONT_SMALL_fixedwidth # eval font create fontTEMP_SMALL_listbox $FONT_SMALL_fixedwidth # eval font create fontTEMP_SMALL_msg $FONT_SMALL_fixedwidth # eval font create fontTEMP_SMALL_text $FONT_SMALL_fixedwidth ##+####################################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. padx,pady for buttons) ##+####################################################################### ## For BUTTON widgets: set PADY_button 0 set PADX_button 0 set BDwidth_button 2 ## For LABEL widgets: set PADY_label 0 set PADX_label 0 set BDwidth_label 2 ## For ENTRY, LISTBOX widgets: # set BDwidth_entry 2 # set BDwidth_listbox 2 ## For TEXT, MESSAGE widgets: set BDwidth_text 2 # set BDwidth_msg 2 ##+######################################################################## ##+######################################################################## ## ## GET INPUT PARM VALUES -- ## i.e. set C255init M255init Y255init from ## ## 1) arguments passed to this script ## OR ## 2) environment vars C255, M255, Y255. ## ##+######################################################################## ##+######################################################################## ## Example argc/argv processing: ## ## if {$argc == 0} { ## set VARtext "$env(CONFIRM_TEXT)" ## } else { ## set VARtext [lindex $argv 0] ## } ##+######################################################################## if {$argc == 3} { set C255init [lindex $argv 0] set M255init [lindex $argv 1] set Y255init [lindex $argv 2] } else { # set C255init 40 set C255init 0 catch { set C255init "$env(C255)" } # set M255init 80 set M255init 156 catch { set M255init "$env(M255)" } # set Y255init 120 set Y255init 255 catch { set Y255init "$env(Y255)" } } ##+################################################ ## SET THE WINDOW SIZE. ## (We 'fix' the size rather than setting a minsize ## and allowing the window to expand.) ##+################################################ ## 'minsize' not used now. See 'wm resizable . 0 0' below. # wm minsize . 550 200 # catch {eval wm minsize . "$env(FE_COLORSEL_MINSIZE)" } ## Alternative to minsize (make win not resizable): wm resizable . 0 0 ##+################################################ ## Load a TEXT-ARRAY variable with text for ## labels and other GUI widgets --- to facilitate ## 'internationalization' of this script. ################################################### set aRtext(labelCYAN) "Cyan" set aRtext(labelMAGENTA) "Magenta" set aRtext(labelYELLOW) "Yellow" set aRtext(butRETURN) "UseIt" set aRtext(butCANCEL) "Cancel" set aRtext(butTOGSIDE) "ToggleSide" set aRtext(butHELP) "Help" set aRtext(labCMYHEX) "CMY-hex:" ;# not used? set aRtext(labCMYPCNT) "CMY-%s:" set aRtext(labRGBHEX) "RGB-hex:" set aRtext(labRGBPCNT) "RGB-%s:" ##+#################################################################### ##+#################################################################### ## DEFINE *ALL* THE FRAMES: ## TOP-LEVEL FRAMES: ## - 'fRsliders' - to contain 3 sliderbars. ## - 'fRpreview' - to contain only its background; to display ## the color specified by the sliderbar settings. ## - 'fRbottom' - to contain several buttons (UseIt,Cancel,etc), ## as well as 2 label & 2 text widgets to ## display the current per-cent and ## hex RGB-values of the color. ## ## 'fRsliders' & 'fRpreview' are packed on left & right. ## ## SUB-FRAMES: ## - 'fRsliders.fRcyan' (to contain 1 label, 1 sliderbar) ## - 'fRsliders.fRmagenta' (to contain 1 label, 1 sliderbar) ## - 'fRsliders.fRyellow' (to contain 1 label, 1 sliderbar) ##+#################################################################### ##+#################################################################### ## FOR TESTING of expansion of frames (esp. during window expansion): # set RELIEF_frame raised # set BDwidth_frame 2 set RELIEF_frame flat set BDwidth_frame 0 frame .fRtop -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRbottom -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRtop.fRsliders -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRtop.fRpreview -relief raised -borderwidth 4 \ -width 300 -height 150 ## Note: We could set the size of this 'preview' frame according ## to the screensize, say about 15 to 20% of the screen width and height. frame .fRtop.fRsliders.fRcyan \ -relief $RELIEF_frame \ -borderwidth $BDwidth_frame frame .fRtop.fRsliders.fRmagenta \ -relief $RELIEF_frame \ -borderwidth $BDwidth_frame frame .fRtop.fRsliders.fRyellow \ -relief $RELIEF_frame \ -borderwidth $BDwidth_frame ##+######################################################## ## PACK *ALL* the FRAMES. ##+######################################################## pack .fRtop \ .fRbottom \ -side top \ -anchor nw \ -fill none \ -expand 0 ## Set a variable to indicate the current ## '-side' option of the 2 subframes --- ## 'fRsliders' and 'fRpreview'. set LR_side "left" ##+########################################## ## NOTE: The variable 'LR_side' will ## be used to help toggle the position ## of 'fRpreview' to left or right. ##+########################################## pack .fRtop.fRsliders \ .fRtop.fRpreview \ -side $LR_side \ -anchor w \ -fill none \ -expand 0 pack .fRtop.fRsliders.fRcyan \ .fRtop.fRsliders.fRmagenta \ .fRtop.fRsliders.fRyellow \ -side top \ -anchor w \ -fill none \ -expand 0 ## OK. All frames are defined. ##+################################################################ ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+################################################################ ##+######################################################## ## IN THE '.fRtop.fRsliders' frame - ## DEFINE 3 LABELS and 3 SLIDERBAR WIDGETS. ## THEN PACK THEM. ##+######################################################## ## CYAN label .fRtop.fRsliders.fRcyan.label \ -text "$aRtext(labelCYAN)" \ -width 8 \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -bd $BDwidth_label scale .fRtop.fRsliders.fRcyan.scale \ -orient horizontal \ -from 0 -to 255 \ -digits 0 \ -length 350 \ -command "color_update" .fRtop.fRsliders.fRcyan.scale set $C255init ## MAGENTA label .fRtop.fRsliders.fRmagenta.label \ -text "$aRtext(labelMAGENTA)" \ -width 8 \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -bd $BDwidth_label scale .fRtop.fRsliders.fRmagenta.scale \ -orient horizontal \ -from 0 -to 255 \ -digits 0 \ -length 350 \ -command "color_update" .fRtop.fRsliders.fRmagenta.scale set $M255init ## YELLOW label .fRtop.fRsliders.fRyellow.label \ -text "$aRtext(labelYELLOW)" \ -width 8 \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -bd $BDwidth_label scale .fRtop.fRsliders.fRyellow.scale \ -orient horizontal \ -from 0 -to 255 \ -digits 0 \ -length 350 \ -command "color_update" .fRtop.fRsliders.fRyellow.scale set $Y255init ## Pack ALL the widgets --- 3 LABELS & 3 SCALES ## --- in frame '.fRtop.fRsliders'. pack .fRtop.fRsliders.fRcyan.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtop.fRsliders.fRcyan.scale \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRtop.fRsliders.fRmagenta.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtop.fRsliders.fRmagenta.scale \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRtop.fRsliders.fRyellow.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtop.fRsliders.fRyellow.scale \ -side left \ -anchor w \ -fill x \ -expand 0 ########################################################### ## IN THE '.fRtop.fRpreview' FRAME - ## there are no widgets. We simply set its ## background color in the 'color_update' proc. ########################################################### ##+######################################################## ## IN THE '.fRbottom' FRAME - ## DEFINE several BUTTONS --- ## 1 'UseIt' BUTTON, 1 'Cancel' BUTTON, ## 1 'Toggle-Side' BUTTON, 1 'Help' BUTTON, and ## several TEXT WIDGETS for CMY and RGB hex and percent ## values (rather than a label or message widget, ## so that it is possible to paste the hex & percent ## values to another window). ## THEN PACK THE WIDGETS. ##+######################################################## button .fRbottom.butRETURN \ -text "$aRtext(butRETURN)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidth_button \ -command {put_vars} button .fRbottom.butCANCEL \ -text "$aRtext(butCANCEL)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidth_button \ -command {exit} button .fRbottom.butTOGSIDE \ -text "$aRtext(butTOGSIDE)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidth_button \ -command {toggle_side} button .fRbottom.butHELP \ -text "$aRtext(butHELP)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidth_button \ -command {popup_msg_var_scroll "$HELPtext"} ## CMY text fields (CMY in percents & hex): label .fRbottom.labCMYPCNT \ -text "$aRtext(labCMYPCNT)" \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -relief flat \ -bd $BDwidth_label text .fRbottom.txtCMYPCNT \ -relief raised \ -borderwidth $BDwidth_text \ -height 1 \ -width 11 \ -wrap none \ -font fontTEMP_text if {0} { label .fRbottom.labCMYHEX \ -text "$aRtext(labCMYHEX)" \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -relief flat \ -bd $BDwidth_label text .fRbottom.txtCMYHEX \ -relief raised \ -borderwidth $BDwidth_text \ -height 1 \ -width 8 \ -wrap none \ -font fontTEMP_text } ## END OF if {0} (to hide this code) ## RGB text fields (RGB in percents & hex): label .fRbottom.labRGBPCNT \ -text "$aRtext(labRGBPCNT)" \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -relief flat \ -bd $BDwidth_label text .fRbottom.txtRGBPCNT \ -relief raised \ -borderwidth $BDwidth_text \ -height 1 \ -width 11 \ -wrap none \ -font fontTEMP_text label .fRbottom.labRGBHEX \ -text "$aRtext(labRGBHEX)" \ -font fontTEMP_label \ -padx $PADX_label \ -pady $PADY_label \ -relief flat \ -bd $BDwidth_label text .fRbottom.txtRGBHEX \ -relief raised \ -borderwidth $BDwidth_text \ -height 1 \ -width 8 \ -wrap none \ -font fontTEMP_text ## Pack ALL the widgets in frame '.fRbottom'. pack .fRbottom.butRETURN \ .fRbottom.butCANCEL \ .fRbottom.butTOGSIDE \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbottom.butHELP \ -side left \ -anchor w \ -fill none \ -expand 0 \ -padx {0 10} pack .fRbottom.labCMYPCNT \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbottom.txtCMYPCNT \ -side left \ -anchor w \ -fill none \ -expand 0 \ -padx {0 10} ## COMMENTED, for now. # .fRbottom.labCMYHEX \ # .fRbottom.txtCMYHEX \ pack .fRbottom.labRGBPCNT \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbottom.txtRGBPCNT \ -side left \ -anchor w \ -fill none \ -expand 0 \ -padx {0 10} pack .fRbottom.labRGBHEX \ .fRbottom.txtRGBHEX \ -side left \ -anchor w \ -fill none \ -expand 0 ##+####################################### ## END OF MAIN SECTION TO SETUP THE GUI. ## FRAMES AND WIDGETS ARE DEFINED. ##+####################################### ##+##################################################################### ## BINDINGS SECTION: none ##+##################################################################### ##+##################################################################### ## PROCS SECTION: ## - 'color_update' ## - 'toggle_side' ## - 'put_vars' ## - 'popup_msg_var_scroll' ##+##################################################################### ##+##################################################################### ## proc 'color_update' ##+##################################################################### ## PURPOSE: To set ## 1) the background color of the preview window ## 2) display the hex value of the CMY and RGB values ## as any slider is changed. ## ## CALLED BY: the '-command' option of 3 scale widgets: ## - .fRtop.fRsliders.fRcyan.scale ## - .fRtop.fRsliders.fRmagenta.scale ## - .fRtop.fRsliders.fRyellow.scale ##+##################################################################### proc color_update {x} { global c255 m255 y255 set c255 [.fRtop.fRsliders.fRcyan.scale get] set m255 [.fRtop.fRsliders.fRmagenta.scale get] set y255 [.fRtop.fRsliders.fRyellow.scale get] ## Set the background color of frame '.fRtop.fRpreview'. set r255 [expr {255 - $c255}] set g255 [expr {255 - $m255}] set b255 [expr {255 - $y255}] set hexRGBcolor [format "#%02X%02X%02X" $r255 $g255 $b255] .fRtop.fRpreview config -bg $hexRGBcolor ## Show the CMY values in percents. set c_pcnt [expr {$c255 * 100 / 255}] set m_pcnt [expr {$m255 * 100 / 255}] set y_pcnt [expr {$y255 * 100 / 255}] .fRbottom.txtCMYPCNT delete 1.0 end .fRbottom.txtCMYPCNT insert end "$c_pcnt $m_pcnt $y_pcnt" ## Show the RGB values in hex. .fRbottom.txtRGBHEX delete 1.0 end .fRbottom.txtRGBHEX insert end $hexRGBcolor ## Show the RGB values in percents. set r_pcnt [expr {$r255 * 100 / 255}] set g_pcnt [expr {$g255 * 100 / 255}] set b_pcnt [expr {$b255 * 100 / 255}] .fRbottom.txtRGBPCNT delete 1.0 end .fRbottom.txtRGBPCNT insert end "$r_pcnt $g_pcnt $b_pcnt" } ## END of proc 'color_update' ##+##################################################################### ## proc 'toggle_side' ##+##################################################################### ## PURPOSE: To switch the sides of the 'sliders' & 'preview' frames ## ## CALLED BY: .fRbottom.butTOGSIDE button ##+##################################################################### proc toggle_side { } { global LR_side if { "$LR_side" == "left" } { set LR_side "right" } else { set LR_side "left" } ## Re-pack the side-by-side frames 'fRsliders' and 'fRpreview' ## to the opposite sides from where they were. pack forget .fRtop.fRsliders .fRtop.fRpreview pack .fRtop.fRsliders \ .fRtop.fRpreview \ -side $LR_side \ -anchor w \ -fill both \ -expand 1 } ## END of proc 'toggle_side' ##+##################################################################### ## proc 'put_vars' ######################################################################## ## PURPOSE: Puts RGB values (three 0-255 values and one hex value) ## to standard output. ## ## CALLED BY: .fRbottom.butRETURN button ##+##################################################################### proc put_vars { } { global c255 m255 y255 set r255 [expr {255 - $c255}] set g255 [expr {255 - $m255}] set b255 [expr {255 - $y255}] set rgbCOLORhex [format "%02X%02X%02X" $r255 $g255 $b255] ## ALTERNATIVE: ## puts "R255=\"$r255\" ; G255=\"$g255\" ; B255=\"$b255\"" puts "$r255 $g255 $b255 $rgbCOLORhex" exit } ## END of proc 'puts_vars' ##+######################################################################## ## PROC 'popup_msg_var_scroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msg_var_scroll { VARtext } { ## global fontTEMP_text #; 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 .fRtopmsg} toplevel .fRtopmsg # wm geometry .fRtopmsg 600x400+100+50 wm geometry .fRtopmsg +100+50 wm title .fRtopmsg "Note" # wm title .fRtopmsg "Note to $env(USER)" wm iconname .fRtopmsg "Note" ##################################### ## In frame '.fRtopmsg' - ## DEFINE a TEXT widget and scrollbars, ## and a BUTTON widget. ##################################### text .fRtopmsg.text \ -wrap none \ -font fontTEMP_text \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand ".fRtopmsg.scrolly set" \ -xscrollcommand ".fRtopmsg.scrollx set" scrollbar .fRtopmsg.scrolly \ -orient vertical \ -command ".fRtopmsg.text yview" scrollbar .fRtopmsg.scrollx \ -orient horizontal \ -command ".fRtopmsg.text xview" button .fRtopmsg.butt \ -text "OK" \ -font fontTEMP_button \ -command "destroy .fRtopmsg" ####################################### ## PACK ALL the widgets in '.fRtopmsg'. ####################################### ## Pack the button at the bottom, before ## we pack the x-scrollbar there. pack .fRtopmsg.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 .fRtopmsg.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack .fRtopmsg.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack .fRtopmsg.text \ -side top \ -anchor center \ -fill both \ -expand 1 ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## .fRtopmsg.text delete 1.0 end .fRtopmsg.text insert end $VARtext .fRtopmsg.text configure -state disabled } ## END OF PROC 'popup_msg_var_scroll' ##+############################################### ## END OF PROCS SECTION. ##+############################################### ## ADDITONAL GUI INITIALIZATION FOLLOWS. ## (We simply set the help-text variable here.) ##+############################################### set HELPtext \ " ** CMY-255 Color Selector HELP *** Sometimes people want to set a color by color name --- or, perhaps, get an idea of numbers to use to get in a certain color 'area' and then fine tune from there. So here is a little HELP on setting colors that you know by name using appropriate CMY values. Most people know that: yellow = red + green ; bright-yellow = 255 255 0 (add some blue to brighten further) magenta = red + blue ; bright-magenta = 255 0 255 (add some green to brighten further) cyan = green + blue ; bright-cyan = 0 255 255 (add some red to brighten further) So it seems that if you combine yellow + magenta = lots of red and a medium amount of blue and green magenta + cyan = lots of blue and a medium amount of cyan and yellow cyan + yellow = lots of green and a medium amount of red and blue It turns out that a good way to think of the relationship of CMY to RGB is: yellow is the opposite of blue ( yellow = 255 - blue , in 'base 255') magenta is the opposite of green ( magenta = 255 - green, in 'base 255') cyan is the opposide of red ( cyan = 255 - red , in 'base 255') For some colors, like brown, it is not so obvious which CMY (or RGB) colors to use to create the color. Brown is basically lots of red, a medium amount of green, and even less blue. So a medium brightness brown is about RGB(200,150,100). In CMY-255, using our 3 RGB-to-CMY equations above, a medium brightness brown is about CMY(55,105,155). In particular, 'sandy brown' is RGB(244,164,96). In CMY-255, 'sandy brown' is CMY(11,91,159). X color names and their RGB values are shown by the command 'showrgb'. We could run the output of 'showrgb' through a script using 'awk' or Tcl, and convert the RGB-255 values to CMY-255. After I do that, I will fill out the following sections. Here are some color-names and CMY-255 values for RED-ISH colors, in alphabetic order by name: brown,sandy RGB(244 164 96) CMY(? ? ?) coral1 RGB(255 114 86) CMY(? ? ?) firebrick RGB(178 34 34) CMY(? ? ?) khaki,dark RGB(189 183 107) CMY(? ? ?) lavenderblush3 RGB(205 193 197) CMY(? ? ?) magenta3 RGB(205 0 205) CMY(? ? ?) maroon1 RGB(255 52 179) CMY(? ? ?) orange,dark RGB(255 127 0) CMY(? ? ?) orchid1 RGB(255 131 250) CMY(? ? ?) peach puff RGB(255 218 185) CMY(? ? ?) pink,deep RGB(255 20 147) CMY(? ? ?) pink3 RGB(205 145 158) CMY(? ? ?) plum RGB(221 160 221) CMY(? ? ?) rose,misty,3 RGB(205 183 181) CMY(? ? ?) salmon1 RGB(255 140 105) CMY(? ? ?) sienna1 RGB(255 130 71) CMY(? ? ?) tan1 RGB(255 165 79) CMY(? ? ?) tomato RGB(255 99 71) CMY(? ? ?) violet RGB(238 130 238) CMY(? ? ?) wood,burly,3 RGB(205 170 125) CMY(? ? ?) Here are some names and CMY values for GREEN-ISH colors, in alphabetic order by name: aquamarine2 RGB(118 238 198) CMY(? ? ?) chartreuse RGB(127 255 0) CMY(? ? ?) cyan,light RGB(224 255 255) CMY(? ? ?) green,lawn RGB(124 252 0) CMY(? ? ?) green,lime RGB( 50 205 50) CMY(? ? ?) green,pale RGB(152 251 152) CMY(? ? ?) green,sea,1 RGB( 84 255 159) CMY(? ? ?) green,spring RGB( 0 255 127) CMY(? ? ?) honeydew RGB(240 255 240) CMY(? ? ?) mint cream RGB(245 255 250) CMY(? ? ?) olivedrab2 RGB(179 238 58) CMY(? ? ?) turquoise RGB( 64 224 208) CMY(? ? ?) Here are some names and CMY values for BLUE-ISH colors, in alphabetic order by name: azure2 RGB(224 238 238) CMY(? ? ?) blue,cornflower RGB(100 149 237) CMY(? ? ?) blue,dodger RGB( 30 144 255) CMY(? ? ?) blue,midnight RGB( 25 25 112) CMY(? ? ?) lavender RGB(230 230 250) CMY(? ? ?) lightblue2 RGB(178 223 238) CMY(? ? ?) purple1 RGB(155 48 255) CMY(? ? ?) royalblue1 RGB( 72 118 255) CMY(? ? ?) skyblue1 RGB(135 206 255) CMY(? ? ?) steelblue1 RGB( 99 184 255) CMY(? ? ?) slateblue1 RGB(131 111 255) CMY(? ? ?) turquoise1 RGB( 0 245 255) CMY(? ? ?) turquoise,pale RGB(175 238 238) CMY(? ? ?) Of course, you can scale the RGB values down to get darker colors. And you can scale the RGB values up to get brighter colors. " ====== I hope that some Tcl-Tk 'newbies' can learn from this useful script. The 'pack forget' technique (seen in the 'toggle_side' proc) might open some eyes as to how flexible Tk is --- namely, Tk can be used to make some rather 'wild' self-re-configuring GUI's. And who knows? Maybe there are some CMY fans out there who really don't like to specify colors in RGB terms. So maybe this GUI can be of use to someone, other than as an exercise in Tk coding. Maybe there are some people who think in terms of 'complementary' colors rather than 'primary' colors --- people who think in terms of 'subtractive' colors rather than 'additive' colors --- people who think in terms of CMY rather than RGB. Perhaps painters and printers. Others? <> GUI