FF 2009-09-15: This is a GPL multi-mode color picker. Actually features two color picker types: switches and hsv.
if {[catch {package require colorpicker}]} {source colorpicker.tcl; package require colorpicker} namespace import ::colorpicker::colorpicker colorpicker .picker1 switches -command {.picker2 set} pack .picker1 colorpicker .picker2 hsv -textvar ::color2 pack .picker2 puts [.picker2 get] .picker1 set "#ff0033"
I use it also as a multiple color picker, by gridding two of it (switches and hsv) to the same cell, linking them with -command/set, then using raise to select the one which should be visible.
if {[info exists ::colorpicker::version]} {return} namespace eval ::colorpicker { namespace export colorpicker # ========================================= # colorpicker set version 0.1 # (C) 2009 - Federico Ferri # mescalinum (at) gmail (dot) com # # Released under GPL-3 license: # http://www.gnu.org/licenses/gpl-3.0.html # ========================================= package provide colorpicker $version variable presets { ffffff dfdfdf bbbbbb ffc7c6 ffe3c6 feffc6 c6ffc7 c6feff c7c6ff e3c6ff 9f9f9f 7c7c7c 606060 ff0400 ff8300 faff00 00ff04 00faff 0400ff 9c00ff 404040 202020 000000 551312 553512 535512 0f4710 0e4345 131255 2f004d } proc colorpicker {w mode args} { variable {} set modes {switches hsv} if {[lsearch -exact $modes $mode] == -1} { error "bad mode: $mode. must be one of: $modes." } set ($w:mode) $mode set ($w:color) "#000000" set ($w:command) {} set ($w:textvar) {} frame $w init_$mode $w rename $w ::colorpicker::_$w interp alias {} $w {} ::colorpicker::dispatch $w if {$args != {}} {uplevel 1 ::colorpicker::config $w $args} return $w } proc dispatch {w cmd args} { variable {} switch -glob -- $cmd { get {set ($w:color)} set {uplevel 1 [linsert $args 0 ::colorpicker::set_color_ext $w]} con* {uplevel 1 [linsert $args 0 ::colorpicker::config $w]} default {uplevel 1 [linsert $args 0 ::colorpicker::_$w $cmd]} } } proc config {w args} { variable {} set options {} set flag 0 foreach {key value} $args { switch -glob -- $key { -com* { set ($w:command) $value set flag 1 } -textvar* { set ($w:textvar) $value set flag 1 } default { lappend options $key $value } } } if {!$flag || $options != ""} { uplevel 1 [linsert $options 0 ::scrolledframe::_$w config] } } proc set_color_ext {w c} { # called by the widget public method variable {} set c [string tolower $c] if {![regexp {^#[0-9a-f]{6,6}$} $c]} { error "Invalid color: $c. Specify a color in the format #HHHHHH" } switch -exact -- $($w:mode) { switches { set_color $w $c } hsv { set r [expr 0x[string range $c 1 2]] set g [expr 0x[string range $c 3 4]] set b [expr 0x[string range $c 5 6]] set hsv [rgbToHsv $r $g $b] hsv_set $w h [lindex $hsv 0] hsv_set $w s [lindex $hsv 1] hsv_set $w v [lindex $hsv 2] set_color $w $c } } } proc set_color {w c} { # called internally in reaction to events variable {} set c [string tolower $c] set ($w:color) $c if {$($w:command) != {}} { set cmd $($w:command) lappend cmd $c uplevel #0 $cmd } if {$($w:textvar) != {}} { uplevel #0 [list set $($w:textvar) $c] } switch -exact -- $($w:mode) { switches { variable presets set q 0 for {set row 0} {$row < 3} {incr row} { for {set col 0} {$col < 10} {incr col} { set b [expr {$c == "#[lindex $presets $q]"}] ${w}.r${row}c${col} configure \ -relief [lindex {raised sunken} $b] incr q } } } hsv { } } } proc mkColor {rgb} { set r [lindex $rgb 0]; set g [lindex $rgb 1]; set b [lindex $rgb 2] if {$r < 0} {set r 0} elseif {$r > 255} {set r 255} if {$g < 0} {set g 0} elseif {$g > 255} {set g 255} if {$b < 0} {set b 0} elseif {$b > 255} {set b 255} return #[format "%2.2x%2.2x%2.2x" $r $g $b] } proc rgbToHsv {r g b} { set sorted [lsort -real [list $r $g $b]] set temp [lindex $sorted 0] set v [lindex $sorted 2] set value $v set bottom [expr {$v-$temp}] if {$bottom == 0} { set hue 0 set saturation 0 set value $v } else { if {$v == $r} { set top [expr {$g-$b}] if {$g >= $b} { set angle 0 } else { set angle 360 } } elseif {$v == $g} { set top [expr {$b-$r}] set angle 120 } elseif {$v == $b} { set top [expr {$r-$g}] set angle 240 } set hue [expr {round(60*(double($top)/$bottom)+$angle)}] } if {$v == 0} { set saturation 0 } else { set saturation [expr {round(255-255*(double($temp)/$v))}] } return [list $hue $saturation $value] } proc hsvToRgb {h s v} { set hi [expr {int(double($h)/60)%6}] set f [expr {double($h)/60-$hi}] set s [expr {double($s)/255}] set v [expr {double($v)/255}] set p [expr {double($v)*(1-$s)}] set q [expr {double($v)*(1-$f*$s)}] set t [expr {double($v)*(1-(1-$f)*$s)}] switch -- $hi { 0 {set r $v; set g $t; set b $p} 1 {set r $q; set g $v; set b $p} 2 {set r $p; set g $v; set b $t} 3 {set r $p; set g $q; set b $v} 4 {set r $t; set g $p; set b $v} 5 {set r $v; set g $p; set b $q} default {error "[lindex [info level 0] 0]: bad H value"} } set r [expr {round($r*255)}] set g [expr {round($g*255)}] set b [expr {round($b*255)}] return [list $r $g $b] } proc init_switches {w} { variable {} variable presets set q 0 for {set row 0} {$row < 3} {incr row} { for {set col 0} {$col < 10} {incr col} { set c "#[lindex $presets $q]" set b [expr {$($w:color) == $c}] grid [frame ${w}.r${row}c${col} -width 18 -height 16 \ -borderwidth 1 -relief [lindex {raised sunken} $b] \ -background $c -highlightthickness 0] \ -row $row -column $col bind ${w}.r${row}c${col} <ButtonPress-1> \ "[namespace current]::set_color $w $c" incr q } } } proc init_hsv {w} { variable colorhsv set colorhsv($w:h) 0 set colorhsv($w:s) 255 set colorhsv($w:v) 255 grid [canvas ${w}.hue -width 130 -height 15 -borderwidth 1 \ -relief sunken -highlightthickness 0] -column 0 -row 0 grid [canvas ${w}.sat -width 130 -height 14 -borderwidth 1 \ -relief sunken -highlightthickness 0] -column 0 -row 1 grid [canvas ${w}.val -width 130 -height 14 -borderwidth 1 \ -relief sunken -highlightthickness 0] -column 0 -row 2 grid [canvas ${w}.test -width 46 -height 46 -borderwidth 1 \ -relief sunken -highlightthickness 0 -background red] \ -column 1 -row 0 -rowspan 3 variable mh variable ms variable mv set mh($w) 0; set ms($w) 0; set mv($w) 0; set sh "[namespace current]::hsv_set $w h \[expr {%x*360.0/130.0}\]" set ss "[namespace current]::hsv_set $w s \[expr {%x*255.0/130.0}\]" set sv "[namespace current]::hsv_set $w v \[expr {%x*255.0/130.0}\]" bind ${w}.hue <ButtonPress-1> "set [namespace current]::mh($w) 1; $sh" bind ${w}.sat <ButtonPress-1> "set [namespace current]::ms($w) 1; $ss" bind ${w}.val <ButtonPress-1> "set [namespace current]::mv($w) 1; $sv" bind ${w}.hue <ButtonRelease-1> "set [namespace current]::mh($w) 0" bind ${w}.sat <ButtonRelease-1> "set [namespace current]::ms($w) 0" bind ${w}.val <ButtonRelease-1> "set [namespace current]::mv($w) 0" bind ${w}.hue <Motion> "if {\$[namespace current]::mh($w)} {$sh}" bind ${w}.sat <Motion> "if {\$[namespace current]::ms($w)} {$ss}" bind ${w}.val <Motion> "if {\$[namespace current]::mv($w)} {$sv}" for {set x 0} {$x < 130} {incr x 3} { set c [mkColor [hsvToRgb [expr {$x*360.0/130.0}] 255 255]] ${w}.hue create rectangle $x 0 [expr {4+$x}] 16 -fill $c -outline {} } hsv_regen $w $colorhsv($w:h) } proc hsv_regen {w hue} { ${w}.sat delete all ${w}.val delete all for {set x 0} {$x < 130} {incr x 3} { set x1 [expr {$x*255.0/130.0}] set c1 [mkColor [hsvToRgb $hue $x1 255]] set c2 [mkColor [hsvToRgb $hue 255 $x1]] ${w}.sat create rectangle $x 0 [expr {4+$x}] 16 \ -fill $c1 -outline {} ${w}.val create rectangle $x 0 [expr {4+$x}] 16 \ -fill $c2 -outline {} } } proc hsv_set {w what val} { variable colorhsv if {$what != {h} && $what != {s} && $what != {v}} {return} set colorhsv($w:$what) $val if {$colorhsv($w:$what) < 0.0} {set colorhsv($w:$what) 0} if {$what == {h}} { if {$colorhsv($w:$what) >= 360.0} {set colorhsv($w:$what) 0} hsv_regen $w $colorhsv($w:$what) } else { if {$colorhsv($w:$what) > 255.0} {set colorhsv($w:$what) 255} } set c [mkColor [hsvToRgb \ $colorhsv($w:h) $colorhsv($w:s) $colorhsv($w:v)]] ${w}.test configure -background $c set_color $w $c } }
Here's the usage above reworked a bit to let you use 'stand alone' (assuming the above code is in a file named colorpicker.tcl along side)
if {[catch {package require colorpicker}]} { source [file join [file dirname [info script]] "colorpicker.tcl"] package require colorpicker } namespace import ::colorpicker::colorpicker colorpicker .picker1 switches -command {.picker2 set} grid .picker1 -row 1 -column 1 colorpicker .picker2 hsv -textvar ::color2 grid .picker2 -row 2 -column 1 button .btn -text "Get Color" -command { .txt delete 0.0 end .txt insert end "[.picker2 get]" } grid .btn -row 1 -column 2 text .txt -width 22 -height 3 grid .txt -row 2 -column 2