Get the color of the pixel under the pointer

HJG I have some images on a canvas, and I want to get the color of the pixel under the cursor.

CLN Not to be too pedantic but you mean the pointer, right? The pointer is where the mouse is, the cursor is where text is placed.

HJG: Yupp, it is the mouse-pointer.

Right now, this works only for an image placed on the canvas at 0,0 :


package require Img

proc int x { expr int($x) }

proc ShowPixel {x y i} {
    set c "- - -"
    catch { set c [$::img1 get $x $y] }
    wm title . "$x $y #$i : $c"
}

proc ShowColor {w x y} {
  # idea: RS
    set color ""
    set id [$w find withtag current]
    if {$id>0} {
      set xx $x
      set yy $y
      foreach {x0 y0 x1 y1} [$w bbox $id] break
      incr xx -$x0
      incr yy -$x0
     #wm title . "$x $y #$id : $x0 $y0  $x1 $y1 : $xx $yy"
     #return

      switch -- [$w type $id] {
        image {set color [[$w itemcget $id -image] get $xx $yy]}
        line - polygon - rectangle - oval - text {
            set cName [$w itemcget $id -fill]
            set color [winfo rgb $w $cName]
        }
      }
      wm title . "$x $y #$id $xx $yy = $color"
    } else {
      wm title . "$x $y"
    }

    #return $color
}

set fn gosmall.gif
set img1 [image create photo -file $fn]

pack [canvas .c]
.c create image  0  0 -image $img1 -anchor nw -tag Copy1
.c create image 80 80 -image $img1 -anchor nw -tag Copy2

.c create text  55  95  -text "ABC"     -fill white
.c create rect 125  25  145  45         -fill red 
.c create oval  25 125   45 145         -fill green
.c create line  15 100   70 125         -fill blue
.c create poly 100  65  130  65 100  20 -fill cyan -outline black

#bind .c <Motion> { ShowPixel    [int [%W canvasx %x]] [int [%W canvasy %y]] [%W find withtag current] }
bind .c <Motion> { ShowColor %W [int [%W canvasx %x]] [int [%W canvasy %y]] }

Using proc ShowPixel, the RGB-values of the pixel under the mouse-pointer are shown only for the first copy of the image...

I can query the coordinates of an item/image on the canvas with bbox (to adjust coordinates within the image vs. the screen-coordinates given by the pointer), but how do I pass the item (as opposed to its id) to the proc ShowPixel ?

RS: Pass in the widget name. For images you can retrieve the name as -image attribute.

HJG: Ok - proc ShowColor now does mostly what I wanted, but gives an error "coordinates out of range" at the edge of the images.

And for rect / oval etc. it would be nice to also get the RGB-values... - RS: That's precisely the job winfo rgb does :^)

HJG: Ok, but winfo returns 16-bit values, e.g. for white I get "65535 65535 65535".

MG It's very kludgy, and probably the worst solution anyone'll come up with, but what the hell. ;) For the sake of completeness...

You can also create an image of the window, and then check the colour of that pixel in the image. I believe this could would do it…

package require Tk
package require Img
pack [canvas .c -bg red -height 100 -width 100]
.c create rectangle 20 20 60 60 -fill blue
bind .c <Button-1> {puts "RGB of [getPixel %W %x %y] at %x,%y"}
catch {console show}
proc getPixel {w x y} {
     set img [image create photo -data $w -format window]
     set rgb [$img get [scan $x %d] [scan $y %d]]
     image delete $img
     return $rgb;
}

HJG: This gives the machine as much work as taking a whole screenshot for querying a single pixel :-)


GWM I have also developed a routine to produce a graph of image brightness along a line through an image Graph An Image.


WJP - 2007-11-12: Sometimes you would like to get the value of a pixel anywhere on the display, not just in a Tk window. As far as I know, this cannot be done from within Tk. However, on X11 systems you can do this using an external C program called xoris, which is available from the author's web site: http://gromnizki.unixdev.net (Freshmeat page: http://freshmeat.net/projects/xoris ). My program ColorExplorer provides its Copy function this way.