1#!/usr/local/bin/python3.8
2
3import gi
4gi.require_version("Gtk", "3.0")
5from gi.repository import Gtk, Gdk, GObject
6from PIL import Image
7
8class EyeDropper(Gtk.HBox):
9    __gsignals__ = {
10        'color-picked': (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_STRING,))
11    }
12
13    def __init__(self):
14        Gtk.HBox.__init__ (self)
15
16        self.button = Gtk.Button("")
17        self.button.set_tooltip_text(_("Click the eyedropper, then click a color anywhere on your screen to select that color"))
18        self.button.set_image(Gtk.Image().new_from_stock(Gtk.STOCK_COLOR_PICKER, Gtk.IconSize.BUTTON))
19        self.button.get_property('image').show()
20        self.button.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK);
21
22        self.pack_start(self.button, False, False, 2)
23
24        self.bp_handler = None
25        self.br_handler = None
26        self.kp_handler = None
27
28        self.button.connect("clicked", self.on_button_clicked)
29
30    def on_button_clicked(self, widget):
31        screen = widget.get_screen()
32        self.time = Gtk.get_current_event_time()
33        self.device = Gtk.get_current_event_device()
34
35        self.grab_widget = Gtk.Window(Gtk.WindowType.POPUP)
36        self.grab_widget.set_screen(screen)
37        self.grab_widget.resize(1, 1)
38        self.grab_widget.move(-100, -100)
39        self.grab_widget.show()
40
41        self.grab_widget.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK)
42        toplevel = widget.get_toplevel()
43
44        if isinstance(toplevel, Gtk.Window):
45            if toplevel.has_group():
46                toplevel.add_window(grab_widget)
47
48        window = self.grab_widget.get_window()
49
50        picker_cursor = Gdk.Cursor(screen.get_display(), Gdk.CursorType.CROSSHAIR);
51
52        grab_status = self.device.grab(window, Gdk.GrabOwnership.APPLICATION, False,
53                                       Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK,
54                                       picker_cursor, self.time)
55
56        if grab_status != Gdk.GrabStatus.SUCCESS:
57            return
58
59        Gtk.device_grab_add(self.grab_widget, self.device, True)
60
61        self.bp_handler = self.grab_widget.connect("button-press-event", self.mouse_press)
62        self.kp_handler = self.grab_widget.connect("key-press-event", self.key_press)
63
64    def mouse_press(self, widget, event):
65        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 1:
66            self.br_handler = widget.connect("button-release-event", self.mouse_release)
67            return True
68        return False
69
70    def key_press(self, widget, event):
71        screen, x_root, y_root = self.device.get_position()
72        if event.keyval == Gdk.KEY_Escape:
73            self.ungrab(self.device)
74            return True
75        elif event.keyval in (Gdk.KEY_space, Gdk.KEY_Return, Gdk.KEY_ISO_Enter, Gdk.KEY_KP_Enter, Gdk.KEY_KP_Space):
76            self.grab_color_at_pointer(event, screen, x_root, y_root)
77            return True
78        return False
79
80    def mouse_release(self, widget, event):
81        screen, x, y = self.device.get_position()
82        if event.button != 1:
83            return False
84        self.grab_color_at_pointer(event, screen, event.x_root, event.y_root)
85        return True
86
87    def grab_color_at_pointer(self, event, screen, x_root, y_root):
88        device = self.device
89        window = screen.get_root_window()
90        pixbuf = Gdk.pixbuf_get_from_window(window, x_root, y_root, 1, 1)
91        image = pixbuf2Image(pixbuf)
92
93        r, g, b = image.getpixel((0, 0))
94
95        color = Gdk.RGBA()
96        color.red = r / 255.0
97        color.green = g / 255.0
98        color.blue = b / 255.0
99        self.emit('color-picked', color.to_string())
100        self.ungrab(device)
101
102    def ungrab(self, device):
103        device.ungrab(self.time)
104        Gtk.device_grab_remove(self.grab_widget, device)
105        self.grab_widget.handler_disconnect(self.bp_handler)
106        self.grab_widget.handler_disconnect(self.br_handler)
107        self.grab_widget.handler_disconnect(self.kp_handler)
108
109def pixbuf2Image(pb):
110    width,height = pb.get_width(),pb.get_height()
111    return Image.fromstring("RGB",(width,height),pb.get_pixels() )
112