
Just a little utility to show the nearest system rgb colours (taken from rgb.txt) on an X11 system compared to the one under the cursor.
I couldn’t work out how to listen to mouse messages when I don’t own the window under the mouse pointer (on X Windows anyway) – so I cheated and this code takes a snapshot of the screen and displays it in the background. Obviously you can’t move/click on any windows when this thing is running! quit with ‘q’ or ‘ESC’ or just close the window.
#!/usr/bin/env python
#
# Takes a screenshot image of the root window and display a table of
# nearest system colours compared to that under the mouse pointer
# can specify an argument 1..29 to show more colours (default 6)
# The window title shows the exact rgb values of pointer in hex
#
import gtk, re
from sys import argv
RGBCOLOURS = '/etc/X11/rgb.txt' # rgb colour data
class ScreenColour(object):
syscolours = {} # hold system rgb.txt relating colours to names
def __init__(self, rgbfile=RGBCOLOURS):
# 1 pixel buffer for pixel under mouse pointer
self.pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False, 8, 1, 1)
for line in file(rgbfile).readlines():
if not line.startswith('!'):
rgbname = re.compile(r'\s*(\d+)\s*(\d+)\s*(\d+)\s*(.+)')
(r,g,b,name) = rgbname.match(line).groups()
self.syscolours[name.rstrip()] = (int(r),int(g),int(b))
def cmp_screencolour(self,col,basecol):
"""Numerical difference between colours col(name) and basecol(r,g,b)"""
return sum(abs(a-b) for a, b in zip(basecol, ScreenColour.syscolours[col]))
def pixelinfo(self):
"""Returns the (r,g,b) value of colour under mouse pointer"""
(_, x, y, _) = gtk.gdk.display_get_default().get_pointer()
self.pix.get_from_drawable(gtk.gdk.screen_get_default().get_root_window(),
gtk.gdk.colormap_get_system(),
x,y, 0,0, 1,1)
col = self.pix.get_pixels_array()
return (int(col[0,0,0]), int(col[0,0,1]), int(col[0,0,2]))
def nearest_colours(self, n, basecol):
"""Return the nearest n system colours compared to basecol"""
nearest = self.syscolours.keys()
nearest.sort(key=lambda (c): self.cmp_screencolour(c, basecol))
return nearest[:n]
class ColourInfoWindow(ScreenColour):
"""Draw a table of colours nearest to that under mouse pointer"""
label = []
eb = []
oldrgb = (-1,-1,-1)
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return False
def __init__(self, tablesize):
self.tablesize = tablesize
self.image = gtk.Window()
self.image.set_decorated(False)
self.image.add_events(gtk.gdk.POINTER_MOTION_MASK)
self.image.connect("motion_notify_event", self.event_handler)
#
# set background from a screen shot of the root window
#
screen = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
gtk.gdk.screen_width(), gtk.gdk.screen_height())
screen.get_from_drawable(gtk.gdk.get_default_root_window(),
gtk.gdk.colormap_get_system(), 0, 0, 0, 0,
gtk.gdk.screen_width(), gtk.gdk.screen_height())
pixmap, mask = screen.render_pixmap_and_mask()
self.image.set_app_paintable(True)
self.image.realize()
self.image.window.set_back_pixmap(pixmap, False)
del pixmap
self.image.fullscreen()
self.image.show()
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(200, 40*self.tablesize)
self.window.set_resizable(False)
self.window.set_transient_for(self.image) # stay above screen image
self.window.set_title("Colour Info")
self.window.connect("delete_event", self.delete_event)
self.window.add_events(gtk.gdk.KEY_PRESS_MASK)
self.window.connect("key-press-event", self.event_keys)
self.Colour = ScreenColour()
vbox = gtk.VBox(True,2)
for i in range(self.tablesize):
self.label.append(gtk.Label("Unknown"))
self.eb.append(gtk.EventBox())
self.eb[i].add(self.label[i])
vbox.add(self.eb[i])
self.window.add(vbox)
self.window.show_all()
self.update_colours()
def update_colours(self):
rgb = self.Colour.pixelinfo()
if self.oldrgb != rgb:
self.oldrgb = rgb
nearest = self.Colour.nearest_colours(self.tablesize, rgb)
self.window.set_title("%02X %02X %02X" % rgb)
for i in range(self.tablesize):
try:
self.eb[i].modify_bg(gtk.STATE_NORMAL,
gtk.gdk.color_parse(nearest[i]))
#
# if brighter than 128,128,128 switch to
# a black background so text is visable
#
if (sum(self.syscolours[nearest[i]]) > 384):
w = '<span foreground="black">%s</span>' % nearest[i]
else:
w = '<span foreground="white">%s</span>' % nearest[i]
self.label[i].set_markup(w)
except ValueError:
print 'unknown colour... ', nearest[i]
def event_handler(self, widget, event=None):
if event and event.type == gtk.gdk.MOTION_NOTIFY:
self.update_colours()
def event_keys(self, widget, event=None):
if event:
if event.keyval == gtk.gdk.keyval_from_name("Escape") \
or event.keyval == gtk.gdk.keyval_from_name("q"):
self.window.destroy()
self.image.destroy()
gtk.main_quit()
if __name__ == "__main__":
if len(argv) == 2 and (0<int(argv[1])<30):
ColourInfoWindow(int(argv[1]))
else:
ColourInfoWindow(6)
gtk.main()



