9.13. Color Selection

The color selection widget is, not surprisingly, a widget for interactive selection of colors. This composite widget lets the user select a color by manipulating RGB (Red, Green, Blue) and HSV (Hue, Saturation, Value) triples. This is done either by adjusting single values with sliders or entries, or by picking the desired color from a hue-saturation wheel/value bar. Optionally, the opacity of the color can also be set.

The color selection widget currently emits only one signal, "color_changed", which is emitted whenever the current color in the widget changes, either when the user changes it or if it's set explicitly through the set_color() method.

Lets have a look at what the color selection widget has to offer us. The widget comes in two flavours: GtkColorSelection and GtkColorSelectionDialog.
 
colorsel = GtkColorSelection()

You'll probably not be using this constructor directly. It creates an orphan ColorSelection widget which you'll have to parent yourself. The ColorSelection widget inherits from the VBox widget.
 
colorseldlg = GtkColorSelectionDialog(title)

where title is an optional string to be used in the titlebar of the dialog.

This is the most common color selection constructor. It creates a ColorSelectionDialog. It consists of a Frame containing a ColorSelection widget, an HSeparator and an HBox with three buttons, "Ok", "Cancel" and "Help". You can reach these buttons by accessing the "ok_button", "cancel_button" and "help_button" attributes of the ColorSelectionDialog, (i.e., colorseldlg.ok_button). The ColorSelection widget is accessed using the attribute colorsel:
 
colorsel = colorseldlg.colorsel

The ColorSelection widget has a number of methods that change its characteristics or provide access to the color selection.
 
colorsel.set_update_policy(policy)

This method sets the update policy. The default policy is UPDATE_CONTINUOUS which means that the current color is updated continuously when the user drags the sliders or presses the mouse and drags in the hue-saturation wheel or value bar. If you experience performance problems, you may want to set the policy to UPDATE_DISCONTINUOUS or UPDATE_DELAYED.
 
colorsel.set_opacity(use_opacity)

The color selection widget supports adjusting the opacity of a color (also known as the alpha channel). This is disabled by default. Calling this method with use_opacity set to TRUE enables opacity. Likewise, use_opacity set to FALSE will disable opacity.
 
colorsel.set_color(color)

You can set the current color explicitly by calling this method with an array of color numbers (float). The length of the array depends on whether opacity is enabled or not. Position 0 contains the red component, 1 is green, 2 is blue and opacity is at position 3 (only if opacity is enabled, see the set_opacity() method above). All values are between 0.0 and 1.0.
 
color = colorsel.get_color()

When you need to query the current color, typically when you've received a "color_changed" signal, you use this method. The return value color is a tuple of color numbers either three or four depending on whether opacity is set. See the set_color() method for the description of this array.

The colorsel.py example program demonstrates the use of the ColorSelectionDialog. The program displays a window containing a drawing area. Clicking on it opens a color selection dialog, and changing the color in the color selection dialog changes the background color. Figure 9.12 illustrates this program in action:

Figure 9.12 Color Selection Dialog Example

The source code for colorsel.py is:
 
    1   #!/usr/bin/env python
    2   
    3   # example colorsel.py
    4   
    5   import gtk
    6   import GDK
    7   
    8   class ColorSelectionExample:
    9       # Color changed handler
   10       def color_changed_cb(self, widget):
   11           # Get drawingarea colormap
   12           colormap = self.drawingarea.get_colormap()
   13   
   14           # Get current color
   15           colordata = self.colorseldlg.colorsel.get_color()
   16   
   17           # Fit to a unsigned 16 bit integer (0..65535) and
   18           # insert into the GdkColor structure
   19           # And allocate color
   20           gdk_color = apply(colormap.alloc,
   21                             map(lambda color: int(color*65535.0), colordata))
   22   
   23           # Set window background color
   24           style = self.drawingarea.get_style().copy()
   25           style.bg[gtk.STATE_NORMAL] = gdk_color
   26           self.drawingarea.set_style(style)
   27   
   28           # Clear window
   29           self.drawingarea.draw_rectangle(style.bg_gc[gtk.STATE_NORMAL],
   30                                           gtk.TRUE, 0, 0, -1, -1)
   31   
   32       # Drawingarea event handler
   33       def area_event(self, widget, event):
   34           handled = gtk.FALSE
   35   
   36           # Check if we've received a button pressed event
   37           if event.type == GDK.BUTTON_PRESS:
   38               if self.colorseldlg == None:
   39                   # Yes, we have an event and there's no colorseldlg yet!
   40                   handled = gtk.TRUE
   41   
   42                   # Create color selection dialog
   43                   self.colorseldlg = gtk.GtkColorSelectionDialog(
   44                       "Select background color")
   45   
   46                   # Get the ColorSelection widget
   47                   colorsel = self.colorseldlg.colorsel
   48   
   49                   # Connect to the "color_changed" signal
   50                   # to the colorsel widget
   51                   colorsel.connect("color_changed", self.color_changed_cb)
   52                   self.colorseldlg.connect(
   53                       "delete_event", self.dialog_delete_cb)
   54                   self.colorseldlg.ok_button.connect_object(
   55                       "clicked", self.colorseldlg.hide, self.colorseldlg)
   56                   self.colorseldlg.cancel_button.connect_object(
   57                       "clicked", self.colorseldlg.hide, self.colorseldlg)
   58               # Show the dialog
   59               self.colorseldlg.show()
   60           return handled
   61   
   62       def dialog_delete_cb(self, widget, event):
   63           widget.hide()
   64           return gtk.TRUE
   65   
   66       # Close down and exit handler
   67       def destroy_window(self, widget, event):
   68           gtk.mainquit()
   69           return gtk.TRUE
   70   
   71       def __init__(self):
   72           self.colorseldlg = None
   73           # Create toplevel window, set title and policies
   74           window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
   75           window.set_title("Color selection test")
   76           window.set_policy(gtk.TRUE, gtk.TRUE, gtk.TRUE)
   77   
   78           # Attach to the "delete" and "destroy" events so we can exit
   79           window.connect("delete_event", self.destroy_window)
   80     
   81           # Create drawingarea, set size and catch button events
   82           self.drawingarea = gtk.GtkDrawingArea()
   83           self.drawingarea.size(200, 200)
   84           self.drawingarea.set_events(GDK.BUTTON_PRESS_MASK)
   85           self.drawingarea.connect("event",  self.area_event)
   86     
   87           # Add drawingarea to window, then show them both
   88           window.add(self.drawingarea)
   89           self.drawingarea.show()
   90           window.show()
   91     
   92   def main():
   93       gtk.mainloop()
   94       return 0
   95   
   96   if __name__ == "__main__":
   97       ColorSelectionExample()
   98       main()